diff options
| author | Dmitry Shmidt <dimitrysh@google.com> | 2015-01-05 13:08:17 -0800 |
|---|---|---|
| committer | Vinit Deshpande <vinitd@google.com> | 2015-01-13 12:08:30 -0800 |
| commit | 6c0da2bb83f6915d8260912362692d1a742e057b (patch) | |
| tree | 8a35b3f1c32928f64b2a8880ecbba71ab354df39 | |
| parent | adaa28a85b3a1e28d36875bdf9113fea8eeb0248 (diff) | |
| download | android_external_wpa_supplicant_8-6c0da2bb83f6915d8260912362692d1a742e057b.tar.gz android_external_wpa_supplicant_8-6c0da2bb83f6915d8260912362692d1a742e057b.tar.bz2 android_external_wpa_supplicant_8-6c0da2bb83f6915d8260912362692d1a742e057b.zip | |
Cumulative patch from commit 8b48e3200680f71ae083b84793e6bdc2099416d2 [DO NOT MERGE]
8b48e32 wpa_cli: Add MAC address randomization in scan
fb37588 ctrl_iface: Add MAC address randomization in scan processing
56c76fa scan: Add MAC address randomization in scan handling
86056fe nl80211: Handle MAC address randomization in scan/sched_scan
ff23ed2 driver: Add definitions for MAC address randomization in scan
7db53bb wpa_cli: Implement TDLS start/cancel channel switching commands
72b2605 nl80211: Pass TDLS channel-switch start/stop params to kernel
6b90dea TDLS: Propagate enable/disable channel-switch commands to driver
d9d3b78 TDLS: Track TDLS channel switch prohibition in BSS
4daa572 TDLS: Add channel-switch capability flag
ca16586 Sync with wireless-testing.git include/uapi/linux/nl80211.h
8c42b36 WMM AC: Reconfigure tspecs on reassociation to the same BSS
677e7a9 WMM AC: Do not fail on unknown IEs in Association Response
fecc2bb WMM AC: Delete tspecs on roaming
20fe745 WMM AC: Print user-priority in wmm_ac_status
730a0d1 nl80211: Always register management frames handler
...
209702d Add possibility to set the setband parameter
ee82e33 Do not trigger the scan during initialization on Android platforms
e69ae5f Reject new SCAN commands if there is a pending request
...
59d7148 nl80211: Provide subtype and reason code for AP SME drivers
9d4ff04 Add external EAPOL transmission option for testing purposes
61fc904 P2P: Handle improper WPS termination on GO during group formation
58b40fd P2P: Clear p2p_go_group_formation_completed on GO start
c155305 Complete sme-connect radio work when clearing connection state
debb2da P2P: Report group removal reason PSK_FAILURE in timeout case
51465a0 The master branch is now used for v2.4 development
Change-Id: I9b9cfa5c5cd4d26b2f3f5595f7c226ac60de6258
285 files changed, 24804 insertions, 14768 deletions
diff --git a/hostapd/Android.mk b/hostapd/Android.mk index edaf6fc8..d6d04c5f 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -95,6 +95,7 @@ OBJS += src/ap/preauth_auth.c OBJS += src/ap/pmksa_cache_auth.c OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c OBJS_d = OBJS_p = LIBS = @@ -200,6 +201,17 @@ endif ifdef CONFIG_HS20 NEED_AES_OMAC1=y +CONFIG_PROXYARP=y +endif + +ifdef CONFIG_PROXYARP +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_SUITEB +L_CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211W @@ -854,6 +866,15 @@ OBJS += src/common/gas.c OBJS += src/ap/gas_serv.c endif +ifdef CONFIG_PROXYARP +L_CFLAGS += -DCONFIG_PROXYARP +OBJS += src/ap/x_snoop.c +OBJS += src/ap/dhcp_snoop.c +ifdef CONFIG_IPV6 +OBJS += src/ap/ndisc_snoop.c +endif +endif + OBJS += src/drivers/driver_common.c ifdef CONFIG_ACS diff --git a/hostapd/Makefile b/hostapd/Makefile index ac6373e6..e64c2497 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -9,6 +9,8 @@ endif CFLAGS += -I$(abspath ../src) CFLAGS += -I$(abspath ../src/utils) +export BINDIR ?= /usr/local/bin/ + # 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 @@ -59,6 +61,7 @@ OBJS += ../src/ap/preauth_auth.o OBJS += ../src/ap/pmksa_cache_auth.o OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/bss_load.o OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o @@ -187,6 +190,17 @@ endif ifdef CONFIG_HS20 NEED_AES_OMAC1=y +CONFIG_PROXYARP=y +endif + +ifdef CONFIG_PROXYARP +CONFIG_L2_PACKET=y +endif + +ifdef CONFIG_SUITEB +CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211W @@ -246,6 +260,12 @@ OBJS += ../src/l2_packet/l2_packet_none.o endif +ifdef CONFIG_ERP +CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + ifdef CONFIG_EAP_MD5 CFLAGS += -DEAP_SERVER_MD5 OBJS += ../src/eap_server/eap_server_md5.o @@ -755,6 +775,9 @@ endif ifdef NEED_TLS_PRF_SHA256 OBJS += ../src/crypto/sha256-tlsprf.o endif +ifdef NEED_HMAC_SHA256_KDF +OBJS += ../src/crypto/sha256-kdf.o +endif endif ifdef NEED_DH_GROUPS @@ -839,6 +862,15 @@ OBJS += ../src/common/gas.o OBJS += ../src/ap/gas_serv.o endif +ifdef CONFIG_PROXYARP +CFLAGS += -DCONFIG_PROXYARP +OBJS += ../src/ap/x_snoop.o +OBJS += ../src/ap/dhcp_snoop.o +ifdef CONFIG_IPV6 +OBJS += ../src/ap/ndisc_snoop.o +endif +endif + OBJS += ../src/drivers/driver_common.o ifdef CONFIG_WPA_CLI_EDIT @@ -881,6 +913,10 @@ ifeq ($(V), 1) Q= E=true endif +ifeq ($(QUIET), 1) +Q=@ +E=true +endif ifdef CONFIG_CODE_COVERAGE %.o: %.c @@ -901,9 +937,10 @@ verify_config: exit 1; \ fi -install: all - mkdir -p $(DESTDIR)/usr/local/bin - for i in $(ALL); do cp -f $$i $(DESTDIR)/usr/local/bin/$$i; done +$(DESTDIR)$(BINDIR)/%: % + install -D $(<) $(@) + +install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL)) ../src/drivers/build.hostapd: @if [ -f ../src/drivers/build.wpa_supplicant ]; then \ diff --git a/hostapd/README b/hostapd/README index 50868ee1..ea016cc5 100644 --- a/hostapd/README +++ b/hostapd/README @@ -74,12 +74,6 @@ Current hardware/software requirements: Please note that station firmware version needs to be 1.7.0 or newer to work in WPA mode. - madwifi driver for cards based on Atheros chip set (ar521x) - (http://sourceforge.net/projects/madwifi/) - Please note that you will need to add the correct path for - madwifi driver root directory in .config (see defconfig file for - an example: CFLAGS += -I<path>) - mac80211-based drivers that support AP mode (with driver=nl80211). This includes drivers for Atheros (ath9k) and Broadcom (b43) chipsets. diff --git a/hostapd/README-WPS b/hostapd/README-WPS index bb7d35f9..d5f713a8 100644 --- a/hostapd/README-WPS +++ b/hostapd/README-WPS @@ -58,10 +58,9 @@ hostapd configuration WPS is an optional component that needs to be enabled in hostapd build configuration (.config). Here is an example configuration that -includes WPS support and uses madwifi driver interface: +includes WPS support and uses nl80211 driver interface: -CONFIG_DRIVER_MADWIFI=y -CFLAGS += -I/usr/src/madwifi-0.9.3 +CONFIG_DRIVER_NL80211=y CONFIG_WPS=y CONFIG_WPS_UPNP=y @@ -74,8 +73,8 @@ Following section shows an example runtime configuration (hostapd.conf) that enables WPS: # Configure the driver and network interface -driver=madwifi -interface=ath0 +driver=nl80211 +interface=wlan0 # WPA2-Personal configuration for the AP ssid=wps-test diff --git a/hostapd/android.config b/hostapd/android.config index ad833081..938aa546 100644 --- a/hostapd/android.config +++ b/hostapd/android.config @@ -15,10 +15,6 @@ # Driver interface for wired authenticator #CONFIG_DRIVER_WIRED=y -# Driver interface for madwifi driver -#CONFIG_DRIVER_MADWIFI=y -#CFLAGS += -I../../madwifi # change to the madwifi source directory - # Driver interface for drivers using the nl80211 kernel interface #CONFIG_DRIVER_NL80211=y # driver_nl80211.c requires a rather new libnl (version 1.1) which may not be @@ -132,7 +128,7 @@ CONFIG_IPV6=y #CONFIG_IEEE80211R=y # Use the hostapd's IEEE 802.11 authentication (ACL), but without -# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) +# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211) #CONFIG_DRIVER_RADIUS_ACL=y # IEEE 802.11n (High Throughput) support diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 44de8260..e30efbe3 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -680,6 +680,8 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (os_strcmp(start, "FT-SAE") == 0) val |= WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ + else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -1865,6 +1867,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "driver_params") == 0) { + os_free(conf->driver_params); + conf->driver_params = os_strdup(pos); } else if (os_strcmp(buf, "debug") == 0) { wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore", line); @@ -1984,6 +1989,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "dh_file") == 0) { os_free(bss->dh_file); bss->dh_file = os_strdup(pos); + } else if (os_strcmp(buf, "openssl_ciphers") == 0) { + os_free(bss->openssl_ciphers); + bss->openssl_ciphers = os_strdup(pos); } else if (os_strcmp(buf, "fragment_size") == 0) { bss->fragment_size = atoi(pos); #ifdef EAP_SERVER_FAST @@ -2044,6 +2052,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "pwd_group") == 0) { bss->pwd_group = atoi(pos); #endif /* EAP_SERVER_PWD */ + } else if (os_strcmp(buf, "eap_server_erp") == 0) { + bss->eap_server_erp = atoi(pos); #endif /* EAP_SERVER */ } else if (os_strcmp(buf, "eap_message") == 0) { char *term; @@ -2063,6 +2073,11 @@ static int hostapd_config_fill(struct hostapd_config *conf, (term - bss->eap_req_id_text) - 1); bss->eap_req_id_text_len--; } + } else if (os_strcmp(buf, "erp_send_reauth_start") == 0) { + bss->erp_send_reauth_start = atoi(pos); + } else if (os_strcmp(buf, "erp_domain") == 0) { + os_free(bss->erp_domain); + bss->erp_domain = os_strdup(pos); } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { bss->default_wep_key_len = atoi(pos); if (bss->default_wep_key_len > 13) { @@ -2405,9 +2420,6 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "radius_server_ipv6") == 0) { bss->radius_server_ipv6 = atoi(pos); #endif /* RADIUS_SERVER */ - } else if (os_strcmp(buf, "test_socket") == 0) { - os_free(bss->test_socket); - bss->test_socket = os_strdup(pos); } else if (os_strcmp(buf, "use_pae_group_addr") == 0) { bss->use_pae_group_addr = atoi(pos); } else if (os_strcmp(buf, "hw_mode") == 0) { @@ -2486,6 +2498,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, bss->dtim_period); return 1; } + } else if (os_strcmp(buf, "bss_load_update_period") == 0) { + bss->bss_load_update_period = atoi(pos); + if (bss->bss_load_update_period < 0 || + bss->bss_load_update_period > 100) { + wpa_printf(MSG_ERROR, + "Line %d: invalid bss_load_update_period %d", + line, bss->bss_load_update_period); + return 1; + } } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) { @@ -2996,6 +3017,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->hs20 = atoi(pos); } else if (os_strcmp(buf, "disable_dgaf") == 0) { bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "proxy_arp") == 0) { + bss->proxy_arp = atoi(pos); } else if (os_strcmp(buf, "osen") == 0) { bss->osen = atoi(pos); } else if (os_strcmp(buf, "anqp_domain_id") == 0) { @@ -3106,6 +3129,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, pos++; WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); bss->bss_load_test_set = 1; + } else if (os_strcmp(buf, "radio_measurements") == 0) { + bss->radio_measurements = atoi(pos); #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strcmp(buf, "vendor_elements") == 0) { struct wpabuf *elems; diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 591c3957..0e35aa66 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -10,6 +10,11 @@ #ifndef CONFIG_NATIVE_WINDOWS +#ifdef CONFIG_TESTING_OPTIONS +#include <net/ethernet.h> +#include <netinet/ip.h> +#endif /* CONFIG_TESTING_OPTIONS */ + #include <sys/un.h> #include <sys/stat.h> #include <stddef.h> @@ -21,6 +26,7 @@ #include "drivers/driver.h" #include "radius/radius_client.h" #include "radius/radius_server.h" +#include "l2_packet/l2_packet.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ieee802_1x.h" @@ -240,14 +246,14 @@ static int hostapd_ctrl_iface_wps_check_pin( if (!wps_pin_valid(pin_val)) { wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; @@ -578,7 +584,7 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, ret = os_snprintf(pos, end - pos, "PBC Status: %s\n", pbc_status_str(hapd->wps_stats.pbc_status)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -588,7 +594,7 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, (hapd->wps_stats.status == WPS_STATUS_FAILURE ? "Failed" : "None"))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -599,7 +605,7 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, "Failure Reason: %s\n", wps_ei_str(hapd->wps_stats.failure_reason)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -608,7 +614,7 @@ static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n", MAC2STR(hapd->wps_stats.peer_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -857,6 +863,193 @@ static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); } + +static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *pos, *end; + int disassoc_timer = 0; + struct sta_info *sta; + u8 req_mode = 0, valid_int = 0x01; + u8 bss_term_dur[12]; + char *url = NULL; + int ret; + u8 nei_rep[1000]; + u8 *nei_pos = nei_rep; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for BSS TM Request message", + MAC2STR(addr)); + return -1; + } + + pos = os_strstr(cmd, " disassoc_timer="); + if (pos) { + pos += 16; + disassoc_timer = atoi(pos); + if (disassoc_timer < 0 || disassoc_timer > 65535) { + wpa_printf(MSG_DEBUG, "Invalid disassoc_timer"); + return -1; + } + } + + pos = os_strstr(cmd, " valid_int="); + if (pos) { + pos += 11; + valid_int = atoi(pos); + } + + pos = os_strstr(cmd, " bss_term="); + if (pos) { + pos += 10; + req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED; + /* TODO: TSF configurable/learnable */ + bss_term_dur[0] = 4; /* Subelement ID */ + bss_term_dur[1] = 10; /* Length */ + os_memset(bss_term_dur, 2, 8); + end = os_strchr(pos, ','); + if (end == NULL) { + wpa_printf(MSG_DEBUG, "Invalid bss_term data"); + return -1; + } + end++; + WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); + } + + + /* + * BSS Transition Candidate List Entries - Neighbor Report elements + * neighbor=<BSSID>,<BSSID Information>,<Operating Class>, + * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>] + */ + pos = cmd; + while (pos) { + u8 *nei_start; + long int val; + char *endptr, *tmp; + + pos = os_strstr(pos, " neighbor="); + if (!pos) + break; + if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) { + wpa_printf(MSG_DEBUG, + "Not enough room for additional neighbor"); + return -1; + } + pos += 10; + + nei_start = nei_pos; + *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; + nei_pos++; /* length to be filled in */ + + if (hwaddr_aton(pos, nei_pos)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID"); + return -1; + } + nei_pos += ETH_ALEN; + pos += 17; + if (*pos != ',') { + wpa_printf(MSG_DEBUG, "Missing BSSID Information"); + return -1; + } + pos++; + + val = strtol(pos, &endptr, 0); + WPA_PUT_LE32(nei_pos, val); + nei_pos += 4; + if (*endptr != ',') { + wpa_printf(MSG_DEBUG, "Missing Operating Class"); + return -1; + } + pos = endptr + 1; + + *nei_pos++ = atoi(pos); /* Operating Class */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing Channel Number"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* Channel Number */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing PHY Type"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* PHY Type */ + end = os_strchr(pos, ' '); + tmp = os_strchr(pos, ','); + if (tmp && (!end || tmp < end)) { + /* Optional Subelements (hexdump) */ + size_t len; + + pos = tmp + 1; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) { + wpa_printf(MSG_DEBUG, + "Not enough room for neighbor subelements"); + return -1; + } + if (len & 0x01 || + hexstr2bin(pos, nei_pos, len / 2) < 0) { + wpa_printf(MSG_DEBUG, + "Invalid neighbor subelement info"); + return -1; + } + nei_pos += len / 2; + pos = end; + } + + nei_start[1] = nei_pos - nei_start - 2; + } + + pos = os_strstr(cmd, " url="); + if (pos) { + size_t len; + pos += 5; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + url = os_malloc(len + 1); + if (url == NULL) + return -1; + os_memcpy(url, pos, len); + url[len] = '\0'; + req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; + } + + if (os_strstr(cmd, " pref=1")) + req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED; + if (os_strstr(cmd, " abridged=1")) + req_mode |= WNM_BSS_TM_REQ_ABRIDGED; + if (os_strstr(cmd, " disassoc_imminent=1")) + req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT; + + ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, + valid_int, bss_term_dur, url, + nei_pos > nei_rep ? nei_rep : NULL, + nei_pos - nei_rep); + os_free(url); + return ret; +} + #endif /* CONFIG_WNM */ @@ -874,7 +1067,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, MAC2STR(hapd->own_addr), wpa_ssid_txt(hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -883,7 +1076,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, hapd->conf->wps_state == 0 ? "disabled" : (hapd->conf->wps_state == 1 ? "not configured" : "configured")); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -891,7 +1084,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, hapd->conf->ssid.wpa_passphrase) { ret = os_snprintf(pos, end - pos, "passphrase=%s\n", hapd->conf->ssid.wpa_passphrase); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -903,7 +1096,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, wpa_snprintf_hex(hex, sizeof(hex), hapd->conf->ssid.wpa_psk->psk, PMK_LEN); ret = os_snprintf(pos, end - pos, "psk=%s\n", hex); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -911,39 +1104,39 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { ret = os_snprintf(pos, end - pos, "key_mgmt="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "WPA-PSK "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "WPA-EAP "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_IEEE80211R if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "FT-PSK "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "FT-EAP "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "FT-SAE "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -952,13 +1145,13 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211W if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -966,14 +1159,20 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, "SAE "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -981,14 +1180,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, if (hapd->conf->wpa) { ret = os_snprintf(pos, end - pos, "group_cipher=%s\n", wpa_cipher_txt(hapd->conf->wpa_group)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) { ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -999,14 +1198,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) { ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1017,7 +1216,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1074,6 +1273,8 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); + } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { + hapd->ext_eapol_frame_io = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ } else { struct sta_info *sta; @@ -1122,7 +1323,7 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd, if (os_strcmp(cmd, "version") == 0) { res = os_snprintf(buf, buflen, "%s", VERSION_STR); - if (res < 0 || (unsigned int) res >= buflen) + if (os_snprintf_error(buflen, res)) return -1; return res; } @@ -1249,6 +1450,248 @@ static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) return res; } + +static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) +{ + char *pos; + u8 src[ETH_ALEN], *buf; + int used; + size_t len; + + wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + ieee802_1x_receive(hapd, src, buf, len); + os_free(buf); + + return 0; +} + + +static u16 ipv4_hdr_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len / 2; i++) + sum += *pos++; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return sum ^ 0xffff; +} + + +#define HWSIM_PACKETLEN 1500 +#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + const struct ether_header *eth; + const struct iphdr *ip; + const u8 *pos; + unsigned int i; + + if (len != HWSIM_PACKETLEN) + return; + + eth = (const struct ether_header *) buf; + ip = (const struct iphdr *) (eth + 1); + pos = (const u8 *) (ip + 1); + + if (ip->ihl != 5 || ip->version != 4 || + ntohs(ip->tot_len) != HWSIM_IP_LEN) + return; + + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + if (*pos != (u8) i) + return; + pos++; + } + + wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); +} + + +static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd, + char *cmd) +{ + int enabled = atoi(cmd); + char *pos; + const char *ifname; + + if (!enabled) { + if (hapd->l2_test) { + l2_packet_deinit(hapd->l2_test); + hapd->l2_test = NULL; + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "test data: Disabled"); + } + return 0; + } + + if (hapd->l2_test) + return 0; + + pos = os_strstr(cmd, " ifname="); + if (pos) + ifname = pos + 8; + else + ifname = hapd->conf->iface; + + hapd->l2_test = l2_packet_init(ifname, hapd->own_addr, + ETHERTYPE_IP, hostapd_data_test_rx, + hapd, 1); + if (hapd->l2_test == NULL) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled"); + + return 0; +} + + +static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) +{ + u8 dst[ETH_ALEN], src[ETH_ALEN]; + char *pos; + int used; + long int val; + u8 tos; + u8 buf[HWSIM_PACKETLEN]; + struct ether_header *eth; + struct iphdr *ip; + u8 *dpos; + unsigned int i; + + if (hapd->l2_test == NULL) + return -1; + + /* format: <dst> <src> <tos> */ + + pos = cmd; + used = hwaddr_aton2(pos, dst); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + + val = strtol(pos, NULL, 0); + if (val < 0 || val > 0xff) + return -1; + tos = val; + + eth = (struct ether_header *) buf; + os_memcpy(eth->ether_dhost, dst, ETH_ALEN); + os_memcpy(eth->ether_shost, src, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IP); + ip = (struct iphdr *) (eth + 1); + os_memset(ip, 0, sizeof(*ip)); + ip->ihl = 5; + ip->version = 4; + ip->ttl = 64; + ip->tos = tos; + ip->tot_len = htons(HWSIM_IP_LEN); + ip->protocol = 1; + ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); + dpos = (u8 *) (ip + 1); + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + *dpos++ = i; + + if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf, + HWSIM_PACKETLEN) < 0) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR + " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); + + return 0; +} + + +static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd, + char *cmd) +{ + u8 *buf; + struct ether_header *eth; + struct l2_packet_data *l2 = NULL; + size_t len; + u16 ethertype; + int res = -1; + const char *ifname = hapd->conf->iface; + + if (os_strncmp(cmd, "ifname=", 7) == 0) { + cmd += 7; + ifname = cmd; + cmd = os_strchr(cmd, ' '); + if (cmd == NULL) + return -1; + *cmd++ = '\0'; + } + + len = os_strlen(cmd); + if (len & 1 || len < ETH_HLEN * 2) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, buf, len) < 0) + goto done; + + eth = (struct ether_header *) buf; + ethertype = ntohs(eth->ether_type); + + l2 = l2_packet_init(ifname, hapd->own_addr, ethertype, + hostapd_data_test_rx, hapd, 1); + if (l2 == NULL) + goto done; + + res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res); +done: + if (l2) + l2_packet_deinit(l2); + os_free(buf); + + return res < 0 ? -1 : 0; +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1366,7 +1809,8 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; @@ -1376,8 +1820,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, reply = os_malloc(reply_size); if (reply == NULL) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } return; } @@ -1525,6 +1972,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) { if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13)) reply_len = -1; + } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { + if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) + reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "GET_CONFIG") == 0) { reply_len = hostapd_ctrl_iface_get_config(hapd, reply, @@ -1551,6 +2001,18 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { + if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) { + if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { + if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { + if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -1558,7 +2020,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply, reply_size); - + } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { + ieee802_1x_erp_flush(hapd); +#ifdef RADIUS_SERVER + radius_server_erp_flush(hapd->radius_srv); +#endif /* RADIUS_SERVER */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -1568,7 +2034,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, os_memcpy(reply, "FAIL\n", 5); reply_len = 5; } - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } os_free(reply); } @@ -1623,7 +2093,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) wpa_printf(MSG_DEBUG, "Using existing control " "interface directory."); } else { - perror("mkdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); goto fail; } } @@ -1631,7 +2102,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) if (hapd->conf->ctrl_interface_gid_set && chown(hapd->conf->ctrl_interface, -1, hapd->conf->ctrl_interface_gid) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + strerror(errno)); return -1; } @@ -1639,7 +2111,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) hapd->iface->interfaces->ctrl_iface_group && chown(hapd->conf->ctrl_interface, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + strerror(errno)); return -1; } @@ -1664,7 +2137,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) s = socket(PF_UNIX, SOCK_DGRAM, 0); if (s < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -1685,15 +2158,16 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) " 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); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); goto fail; } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("hostapd-ctrl-iface: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "hostapd-ctrl-iface: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -1711,19 +2185,22 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) if (hapd->conf->ctrl_interface_gid_set && chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { - perror("chown[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + strerror(errno)); goto fail; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { - perror("chown[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + strerror(errno)); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { - perror("chmod[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); goto fail; } os_free(fname); @@ -1782,6 +2259,11 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) dst = dst->next; os_free(prev); } + +#ifdef CONFIG_TESTING_OPTIONS + l2_packet_deinit(hapd->l2_test); + hapd->l2_test = NULL; +#endif /* CONFIG_TESTING_OPTIONS */ } @@ -1831,7 +2313,8 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; @@ -1871,7 +2354,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = 5; } - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } } @@ -1912,13 +2399,15 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) wpa_printf(MSG_DEBUG, "Using existing control " "interface directory."); } else { - perror("mkdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); goto fail; } } else if (interface->ctrl_iface_group && chown(interface->global_iface_path, -1, interface->ctrl_iface_group) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + strerror(errno)); goto fail; } @@ -1928,7 +2417,7 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) s = socket(PF_UNIX, SOCK_DGRAM, 0); if (s < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -1949,15 +2438,15 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) " 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); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); goto fail; } if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -1975,12 +2464,14 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) if (interface->ctrl_iface_group && chown(fname, -1, interface->ctrl_iface_group) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + strerror(errno)); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { - perror("chmod[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); goto fail; } os_free(fname); diff --git a/hostapd/defconfig b/hostapd/defconfig index 5b74b64f..4cde2b56 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -15,10 +15,6 @@ CONFIG_DRIVER_HOSTAP=y # Driver interface for wired authenticator #CONFIG_DRIVER_WIRED=y -# Driver interface for madwifi driver -#CONFIG_DRIVER_MADWIFI=y -#CFLAGS += -I../../madwifi # change to the madwifi source directory - # Driver interface for drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y @@ -60,6 +56,9 @@ CONFIG_IEEE80211W=y # Integrated EAP server CONFIG_EAP=y +# EAP Re-authentication Protocol (ERP) in integrated EAP server +CONFIG_ERP=y + # EAP-MD5 for the integrated EAP server CONFIG_EAP_MD5=y @@ -142,7 +141,7 @@ CONFIG_IPV6=y #CONFIG_IEEE80211R=y # Use the hostapd's IEEE 802.11 authentication (ACL), but without -# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211) +# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211) #CONFIG_DRIVER_RADIUS_ACL=y # IEEE 802.11n (High Throughput) support diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c index c041887e..42d59dba 100644 --- a/hostapd/hlr_auc_gw.c +++ b/hostapd/hlr_auc_gw.c @@ -711,7 +711,7 @@ static int gsm_auth_req(char *imsi, char *resp, size_t resp_len) rend = resp + resp_len; rpos = resp; ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi); - if (ret < 0 || ret >= rend - rpos) + if (os_snprintf_error(rend - rpos, ret)) return -1; rpos += ret; @@ -737,7 +737,7 @@ static int gsm_auth_req(char *imsi, char *resp, size_t resp_len) printf("No GSM triplets found for %s\n", imsi); ret = os_snprintf(rpos, rend - rpos, " FAILURE"); - if (ret < 0 || ret >= rend - rpos) + if (os_snprintf_error(rend - rpos, ret)) return -1; rpos += ret; diff --git a/hostapd/hostapd.8 b/hostapd/hostapd.8 index b4456bbc..d19d862c 100644 --- a/hostapd/hostapd.8 +++ b/hostapd/hostapd.8 @@ -12,7 +12,7 @@ daemon. .B hostapd is a user space daemon for access point and authentication servers. It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. -The current version supports Linux (Host AP, madwifi, mac80211-based drivers) and FreeBSD (net80211). +The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211). .B hostapd is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication. diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index a7ab0f6b..2f6126c3 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -2,10 +2,10 @@ # Empty lines and lines starting with # are ignored # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for -# management frames); ath0 for madwifi +# management frames with the Host AP driver); wlan0 with many nl80211 drivers interface=wlan0 -# In case of madwifi, atheros, and nl80211 driver interfaces, an additional +# In case of atheros and nl80211 driver interfaces, an additional # configuration parameter, bridge, may be used to notify hostapd if the # interface is included in a bridge. This parameter is not used with Host AP # driver. If the bridge parameter is not set, the drivers will automatically @@ -18,12 +18,15 @@ interface=wlan0 # interface is also created. #bridge=br0 -# Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd); +# Driver interface type (hostap/wired/none/nl80211/bsd); # default: hostap). nl80211 is used with all Linux mac80211 drivers. # Use driver=none if building hostapd as a standalone RADIUS server that does # not control any wireless/wired driver. # driver=hostap +# Driver interface parameters (mainly for development testing use) +# driver_params=<params> + # hostapd event logger configuration # # Two output method: syslog and stdout (only usable if not forking to @@ -221,7 +224,7 @@ fragm_threshold=2346 # Station MAC address -based authentication # Please note that this kind of access control requires a driver that uses # hostapd to take care of management frame processing and as such, this can be -# used with driver=hostap or driver=nl80211, but not with driver=madwifi. +# used with driver=hostap or driver=nl80211, but not with driver=atheros. # 0 = accept unless in deny list # 1 = deny unless in accept list # 2 = use external RADIUS server (accept/deny lists are searched first) @@ -435,6 +438,11 @@ wmm_ac_vo_acm=0 # associated stations in the BSS. By default, this bridging is allowed. #ap_isolate=1 +# BSS Load update period (in BUs) +# This field is used to enable and configure adding a BSS Load element into +# Beacon and Probe Response frames. +#bss_load_update_period=50 + # Fixed BSS Load value for testing purposes # This field can be used to configure hostapd to add a fixed BSS Load element # into Beacon and Probe Response frames for testing purposes. The format is @@ -688,6 +696,17 @@ eapol_key_index_workaround=0 # is only used by one station. #use_pae_group_addr=1 +# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696) +# +# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before +# EAP-Identity/Request +#erp_send_reauth_start=1 +# +# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not +# set (no local ER server). This is also used by the integrated EAP server if +# ERP is enabled (eap_server_erp=1). +#erp_domain=example.com + ##### Integrated EAP server ################################################### # Optionally, hostapd can be configured to use an integrated EAP server @@ -763,6 +782,15 @@ eap_server=0 # "openssl dhparam -out /etc/hostapd.dh.pem 1024" #dh_file=/etc/hostapd.dh.pem +# OpenSSL cipher string +# +# This is an OpenSSL specific configuration option for configuring the default +# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default. +# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation +# on cipher suite configuration. This is applicable only if hostapd is built to +# use OpenSSL. +#openssl_ciphers=DEFAULT:!EXP:!LOW + # Fragment size for EAP methods #fragment_size=1400 @@ -824,6 +852,10 @@ eap_server=0 # EAP method is enabled, the peer will be allowed to connect without TNC. #tnc=1 +# EAP Re-authentication Protocol (ERP) - RFC 6696 +# +# Whether to enable ERP on the EAP server. +#eap_server_erp=1 ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### @@ -1439,6 +1471,11 @@ own_ip_addr=127.0.0.1 # 1 = enabled #bss_transition=1 +# Proxy ARP +# 0 = disabled (default) +# 1 = enabled +#proxy_arp=1 + ##### IEEE 802.11u-2011 ####################################################### # Enable Interworking service diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 9e62befc..70091842 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -393,7 +393,7 @@ static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, else res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_CHECK_PIN command.\n"); return -1; } @@ -456,7 +456,7 @@ static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); return -1; } @@ -477,7 +477,7 @@ static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, } res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_NFC_TOKEN command.\n"); return -1; } @@ -499,7 +499,7 @@ static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long NFC_GET_HANDOVER_SEL command.\n"); return -1; } @@ -596,7 +596,7 @@ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", argv[0], argv[1]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } @@ -616,12 +616,39 @@ static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s", argv[0], argv[1], argv[2]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } +static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[2000], *tmp; + int res, i, total; + + if (argc < 1) { + printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + + total = res; + for (i = 1; i < argc; i++) { + tmp = &buf[total]; + res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]); + if (os_snprintf_error(sizeof(buf) - total, res)) + return -1; + total += res; + } + return wpa_ctrl_command(ctrl, buf); +} + + static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -709,7 +736,7 @@ static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, } res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } @@ -728,7 +755,7 @@ static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, } res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } @@ -748,7 +775,7 @@ static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s", argv[0], argv[1]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } @@ -773,7 +800,7 @@ static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(buf, sizeof(buf), "HS20_DEAUTH_REQ %s %s %s", argv[0], argv[1], argv[2]); - if (res < 0 || res >= (int) sizeof(buf)) + if (os_snprintf_error(sizeof(buf), res)) return -1; return wpa_ctrl_command(ctrl, buf); } @@ -866,7 +893,7 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) } res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long SET command.\n"); return -1; } @@ -886,7 +913,7 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long GET command.\n"); return -1; } @@ -914,7 +941,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long CHAN_SWITCH command.\n"); return -1; } @@ -923,7 +950,7 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, for (i = 2; i < argc; i++) { tmp = cmd + total; res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]); - if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) { + if (os_snprintf_error(sizeof(cmd) - total, res)) { printf("Too long CHAN_SWITCH command.\n"); return -1; } @@ -933,6 +960,27 @@ static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, } +static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ENABLE"); +} + + +static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD"); +} + + +static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISABLE"); +} + + static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -946,7 +994,7 @@ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1], argc == 3 ? argv[2] : ""); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long VENDOR command.\n"); return -1; } @@ -954,6 +1002,13 @@ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ERP_FLUSH"); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -989,6 +1044,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { #endif /* CONFIG_WPS */ { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent }, { "ess_disassoc", hostapd_cli_cmd_ess_disassoc }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req }, { "get_config", hostapd_cli_cmd_get_config }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, @@ -1003,6 +1059,10 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif }, { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req }, { "vendor", hostapd_cli_cmd_vendor }, + { "enable", hostapd_cli_cmd_enable }, + { "reload", hostapd_cli_cmd_reload }, + { "disable", hostapd_cli_cmd_disable }, + { "erp_flush", hostapd_cli_cmd_erp_flush }, { NULL, NULL } }; diff --git a/hostapd/main.c b/hostapd/main.c index c3af7044..3ecd009d 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -28,8 +28,6 @@ #include "eap_register.h" #include "ctrl_iface.h" -struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers, - struct wpa_driver_capa *capa); struct hapd_global { void **drv_priv; @@ -186,9 +184,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) } params.bssid = b; params.ifname = hapd->conf->iface; - params.ssid = hapd->conf->ssid.ssid; - params.ssid_len = hapd->conf->ssid.ssid_len; - params.test_socket = hapd->conf->test_socket; + params.driver_params = hapd->iconf->driver_params; params.use_pae_group_addr = hapd->conf->use_pae_group_addr; params.num_bridge = hapd->iface->num_bss; @@ -217,6 +213,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) struct wowlan_triggers *triggs; iface->drv_flags = capa.flags; + iface->smps_modes = capa.smps_modes; iface->probe_resp_offloads = capa.probe_resp_offloads; iface->extended_capa = capa.extended_capa; iface->extended_capa_mask = capa.extended_capa_mask; @@ -411,7 +408,7 @@ static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, #endif /* EAP_SERVER_TNC */ if (daemonize && os_daemonize(pid_file)) { - perror("daemon"); + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); return -1; } @@ -641,6 +638,8 @@ int main(int argc, char *argv[]) if (log_file) wpa_debug_open_file(log_file); + else + wpa_debug_setup_stdout(); #ifdef CONFIG_DEBUG_LINUX_TRACING if (enable_trace_dbg) { int tret = wpa_debug_open_linux_tracing(); diff --git a/src/ap/accounting.c b/src/ap/accounting.c index 6290d3f3..7c55146b 100644 --- a/src/ap/accounting.c +++ b/src/ap/accounting.c @@ -10,6 +10,8 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" #include "radius/radius.h" #include "radius/radius_client.h" #include "hostapd.h" @@ -50,12 +52,19 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd, if (sta) { radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); - os_snprintf(buf, sizeof(buf), "%08X-%08X", - sta->acct_session_id_hi, sta->acct_session_id_lo); - if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, - (u8 *) buf, os_strlen(buf))) { - wpa_printf(MSG_INFO, "Could not add Acct-Session-Id"); - goto fail; + if ((hapd->conf->wpa & 2) && + !hapd->conf->disable_pmksa_caching && + sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) { + os_snprintf(buf, sizeof(buf), "%08X+%08X", + sta->eapol_sm->acct_multi_session_id_hi, + sta->eapol_sm->acct_multi_session_id_lo); + if (!radius_msg_add_attr( + msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_INFO, + "Could not add Acct-Multi-Session-Id"); + goto fail; + } } } else { radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); diff --git a/src/ap/acs.c b/src/ap/acs.c index b94b8a43..97cf26fb 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -816,6 +816,14 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); + if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { + wpa_printf(MSG_INFO, "ACS: Offloading to driver"); + err = hostapd_drv_do_acs(iface->bss[0]); + if (err) + return HOSTAPD_CHAN_INVALID; + return HOSTAPD_CHAN_ACS; + } + acs_cleanup(iface); err = acs_request_scan(iface); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index c7da69e0..1c0ed7aa 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -425,6 +425,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); + os_free(conf->erp_domain); os_free(conf->accept_mac); os_free(conf->deny_mac); os_free(conf->nas_identifier); @@ -444,12 +445,12 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->private_key_passwd); os_free(conf->ocsp_stapling_response); os_free(conf->dh_file); + os_free(conf->openssl_ciphers); os_free(conf->pac_opaque_encr_key); os_free(conf->eap_fast_a_id); os_free(conf->eap_fast_a_id_info); os_free(conf->eap_sim_db); os_free(conf->radius_server_clients); - os_free(conf->test_socket); os_free(conf->radius); os_free(conf->radius_das_shared_secret); hostapd_config_free_vlan(conf); @@ -495,6 +496,12 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->model_description); os_free(conf->model_url); os_free(conf->upc); + { + unsigned int i; + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(conf->wps_vendor_ext[i]); + } wpabuf_free(conf->wps_nfc_dh_pubkey); wpabuf_free(conf->wps_nfc_dh_privkey); wpabuf_free(conf->wps_nfc_dev_pw); @@ -566,6 +573,7 @@ void hostapd_config_free(struct hostapd_config *conf) os_free(conf->supported_rates); os_free(conf->basic_rates); os_free(conf->chanlist); + os_free(conf->driver_params); os_free(conf); } @@ -888,12 +896,20 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, int cipher = WPA_CIPHER_NONE; bss->ssid.security_policy = SECURITY_IEEE_802_1X; bss->ssid.wep.default_len = bss->default_wep_key_len; - if (bss->default_wep_key_len) + if (full_config && bss->default_wep_key_len) { cipher = bss->default_wep_key_len >= 13 ? WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40; + } else if (full_config && bss->ssid.wep.keys_set) { + if (bss->ssid.wep.len[0] >= 13) + cipher = WPA_CIPHER_WEP104; + else + cipher = WPA_CIPHER_WEP40; + } bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA; } else if (bss->ssid.wep.keys_set) { int cipher = WPA_CIPHER_WEP40; if (bss->ssid.wep.len[0] >= 13) @@ -902,6 +918,8 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, bss->wpa_group = cipher; bss->wpa_pairwise = cipher; bss->rsn_pairwise = cipher; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; } else if (bss->osen) { bss->ssid.security_policy = SECURITY_OSEN; bss->wpa_group = WPA_CIPHER_CCMP; @@ -912,5 +930,7 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, bss->wpa_group = WPA_CIPHER_NONE; bss->wpa_pairwise = WPA_CIPHER_NONE; bss->rsn_pairwise = WPA_CIPHER_NONE; + if (full_config) + bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE; } } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 2858c6ee..58af6cb1 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -15,6 +15,34 @@ #include "common/ieee802_11_common.h" #include "wps/wps.h" +/** + * mesh_conf - local MBSS state and settings + */ +struct mesh_conf { + u8 meshid[32]; + u8 meshid_len; + /* Active Path Selection Protocol Identifier */ + u8 mesh_pp_id; + /* Active Path Selection Metric Identifier */ + u8 mesh_pm_id; + /* Congestion Control Mode Identifier */ + u8 mesh_cc_id; + /* Synchronization Protocol Identifier */ + u8 mesh_sp_id; + /* Authentication Protocol Identifier */ + u8 mesh_auth_id; + u8 *ies; + int ie_len; +#define MESH_CONF_SEC_NONE BIT(0) +#define MESH_CONF_SEC_AUTH BIT(1) +#define MESH_CONF_SEC_AMPE BIT(2) + unsigned int security; + int dot11MeshMaxRetries; + int dot11MeshRetryTimeout; /* msec */ + int dot11MeshConfirmTimeout; /* msec */ + int dot11MeshHoldingTimeout; /* msec */ +}; + #define MAX_STA_COUNT 2007 #define MAX_VLAN_ID 4094 @@ -196,6 +224,7 @@ struct hostapd_bss_config { int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; + int bss_load_update_period; int ieee802_1x; /* use IEEE 802.1X */ int eapol_version; @@ -204,6 +233,7 @@ struct hostapd_bss_config { struct hostapd_eap_user *eap_user; char *eap_user_sqlite; char *eap_sim_db; + int eap_server_erp; /* Whether ERP is enabled on internal EAP server */ struct hostapd_ip_addr own_ip_addr; char *nas_identifier; struct hostapd_radius_servers *radius; @@ -230,6 +260,8 @@ struct hostapd_bss_config { int wep_rekeying_period; int broadcast_key_idx_min, broadcast_key_idx_max; int eap_reauth_period; + int erp_send_reauth_start; + char *erp_domain; int ieee802_11f; /* use IEEE 802.11f (IAPP) */ char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast @@ -302,6 +334,7 @@ struct hostapd_bss_config { int check_crl; char *ocsp_stapling_response; char *dh_file; + char *openssl_ciphers; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -319,8 +352,6 @@ struct hostapd_bss_config { int radius_server_acct_port; int radius_server_ipv6; - char *test_socket; /* UNIX domain socket path for driver_test */ - int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group * address instead of individual address * (for driver_wired.c). @@ -458,6 +489,7 @@ struct hostapd_bss_config { unsigned int qos_map_set_len; int osen; + int proxy_arp; #ifdef CONFIG_HS20 int hs20; int disable_dgaf; @@ -514,6 +546,11 @@ struct hostapd_bss_config { u8 bss_load_test[5]; u8 bss_load_test_set; #endif /* CONFIG_TESTING_OPTIONS */ + +#define MESH_ENABLED BIT(0) + int mesh; + + int radio_measurements; }; @@ -540,6 +577,7 @@ struct hostapd_config { int *basic_rates; const struct wpa_driver_ops *driver; + char *driver_params; int ap_table_max_size; int ap_table_expiration_time; diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index cc4ac102..8514cbe7 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -477,7 +477,8 @@ int hostapd_flush(struct hostapd_data *hapd) } -int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, +int hostapd_set_freq_params(struct hostapd_freq_params *data, + enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, @@ -562,8 +563,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, } -int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int vht_enabled, +int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1) { @@ -573,7 +574,8 @@ int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, vht_enabled, sec_channel_offset, vht_oper_chwidth, center_segment0, center_segment1, - hapd->iface->current_mode->vht_capab)) + hapd->iface->current_mode ? + hapd->iface->current_mode->vht_capab : 0)) return -1; if (hapd->driver == NULL) @@ -747,7 +749,8 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, } -int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, +int hostapd_start_dfs_cac(struct hostapd_iface *iface, + enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1) @@ -792,3 +795,18 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd, return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set, qos_map_set_len); } + + +int hostapd_drv_do_acs(struct hostapd_data *hapd) +{ + struct drv_acs_params params; + + if (hapd->driver == NULL || hapd->driver->do_acs == NULL) + return 0; + os_memset(¶ms, 0, sizeof(params)); + params.hw_mode = hapd->iface->conf->hw_mode; + params.ht_enabled = !!(hapd->iface->conf->ieee80211n); + params.ht40_enabled = !!(hapd->iface->conf->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET); + return hapd->driver->do_acs(hapd->drv_priv, ¶ms); +} diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 7cc9d7de..c133be75 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -57,8 +57,8 @@ int hostapd_set_ieee8021x(struct hostapd_data *hapd, int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, const u8 *addr, int idx, u8 *seq); int hostapd_flush(struct hostapd_data *hapd); -int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, - int channel, int ht_enabled, int vht_enabled, +int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode, + int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1); int hostapd_set_rts(struct hostapd_data *hapd, int rts); @@ -102,15 +102,18 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr, int reassoc, u16 status, const u8 *ie, size_t len); int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr, u8 *tspec_ie, size_t tspec_ielen); -int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq, +int hostapd_start_dfs_cac(struct hostapd_iface *iface, + enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1); -int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode, +int hostapd_set_freq_params(struct hostapd_freq_params *data, + enum hostapd_hw_mode mode, int freq, int channel, int ht_enabled, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps); +int hostapd_drv_do_acs(struct hostapd_data *hapd); #include "drivers/driver.h" @@ -280,6 +283,47 @@ static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf, return hapd->driver->status(hapd->drv_priv, buf, buflen); } +static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd, + int version, const u8 *ipaddr, + int prefixlen, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_add_ip_neigh == NULL) + return -1; + return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr, + prefixlen, addr); +} + +static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd, + u8 version, const u8 *ipaddr) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_delete_ip_neigh == NULL) + return -1; + return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version, + ipaddr); +} + +static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd, + enum drv_br_port_attr attr, + unsigned int val) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_port_set_attr == NULL) + return -1; + return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val); +} + +static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd, + enum drv_br_net_param param, + unsigned int val) +{ + if (hapd->driver == NULL || hapd->drv_priv == NULL || + hapd->driver->br_set_net_param == NULL) + return -1; + return hapd->driver->br_set_net_param(hapd->drv_priv, param, val); +} + static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd, int vendor_id, int subcmd, const u8 *data, size_t data_len, diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index 86f1cbe1..bd1778e4 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -124,6 +124,8 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; #endif /* CONFIG_HS20 */ + srv.erp = conf->eap_server_erp; + srv.erp_domain = conf->erp_domain; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -158,6 +160,7 @@ int authsrv_init(struct hostapd_data *hapd) params.private_key = hapd->conf->private_key; params.private_key_passwd = hapd->conf->private_key_passwd; params.dh_file = hapd->conf->dh_file; + params.openssl_ciphers = hapd->conf->openssl_ciphers; params.ocsp_stapling_response = hapd->conf->ocsp_stapling_response; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4cae0d99..4a8703ac 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -32,18 +32,47 @@ #ifdef NEED_AP_MLME +static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ + if (!hapd->conf->radio_measurements || len < 2 + 4) + return eid; + + *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *eid++ = 5; + *eid++ = (hapd->conf->radio_measurements & BIT(0)) ? + WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + *eid++ = 0x00; + return eid; +} + + static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len) { + if (len < 2 + 5) + return eid; + #ifdef CONFIG_TESTING_OPTIONS if (hapd->conf->bss_load_test_set) { - if (2 + 5 > len) - return eid; *eid++ = WLAN_EID_BSS_LOAD; *eid++ = 5; os_memcpy(eid, hapd->conf->bss_load_test, 5); eid += 5; + return eid; } #endif /* CONFIG_TESTING_OPTIONS */ + if (hapd->conf->bss_load_update_period) { + *eid++ = WLAN_EID_BSS_LOAD; + *eid++ = 5; + WPA_PUT_LE16(eid, hapd->num_sta); + eid += 2; + *eid++ = hapd->iface->channel_utilization; + WPA_PUT_LE16(eid, 0); /* no available admission capabity */ + eid += 2; + } return eid; } @@ -398,6 +427,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_bss_load(hapd, pos, epos - pos); + pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos); + #ifdef CONFIG_IEEE80211N pos = hostapd_eid_ht_capabilities(hapd, pos); pos = hostapd_eid_ht_operation(hapd, pos); @@ -808,6 +839,10 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); + tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + tailpos = hostapd_eid_bss_load(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - tailpos); @@ -908,6 +943,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, break; } params->isolate = hapd->conf->isolate; + params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK; #ifdef NEED_AP_MLME params->cts_protect = !!(ieee802_11_erp_info(hapd) & ERP_INFO_USE_PROTECTION); diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c new file mode 100644 index 00000000..fb639423 --- /dev/null +++ b/src/ap/bss_load.c @@ -0,0 +1,65 @@ +/* + * BSS Load Element / Channel Utilization + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "hostapd.h" +#include "bss_load.h" +#include "ap_drv_ops.h" +#include "beacon.h" + + +static void update_channel_utilization(void *eloop_data, void *user_data) +{ + struct hostapd_data *hapd = eloop_data; + unsigned int sec, usec; + int err; + + if (!(hapd->beacon_set_done && hapd->started)) + return; + + err = hostapd_drv_get_survey(hapd, hapd->iface->freq); + if (err) { + wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data"); + return; + } + + ieee802_11_set_beacon(hapd); + + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); +} + + +int bss_load_update_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + struct hostapd_config *iconf = hapd->iconf; + unsigned int sec, usec; + + if (!conf->bss_load_update_period || !iconf->beacon_int) + return -1; + + hapd->bss_load_update_timeout = conf->bss_load_update_period * + iconf->beacon_int; + sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; + usec = (hapd->bss_load_update_timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, + NULL); + return 0; +} + + +void bss_load_update_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(update_channel_utilization, hapd, NULL); +} diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h new file mode 100644 index 00000000..ac3c793c --- /dev/null +++ b/src/ap/bss_load.h @@ -0,0 +1,17 @@ +/* + * BSS load update + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef BSS_LOAD_UPDATE_H +#define BSS_LOAD_UPDATE_H + + +int bss_load_update_init(struct hostapd_data *hapd); +void bss_load_update_deinit(struct hostapd_data *hapd); + + +#endif /* BSS_LOAD_UPDATE_H */ diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 39edbd70..8c84e3ef 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -1,6 +1,6 @@ /* * Control interface for shared AP commands - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/sae.h" #include "eapol_auth/eapol_auth_sm.h" #include "hostapd.h" #include "ieee802_1x.h" @@ -36,7 +37,7 @@ static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, "rx_bytes=%lu\ntx_bytes=%lu\n", data.rx_packets, data.tx_packets, data.rx_bytes, data.tx_bytes); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; } @@ -55,7 +56,7 @@ static int hostapd_get_sta_conn_time(struct sta_info *sta, ret = os_snprintf(buf, buflen, "connected_time=%u\n", (unsigned int) age.sec); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; } @@ -92,7 +93,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len = 0; ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=", MAC2STR(sta->addr)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -104,7 +105,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n" "listen_interval=%d\nsupported_rates=", sta->aid, sta->capability, sta->listen_interval); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -112,14 +113,14 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, ret = os_snprintf(buf + len, buflen - len, "%02x%s", sta->supported_rates[i], i + 1 < sta->supported_rates_len ? " " : ""); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n", timeout_next_str(sta->timeout_next)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -143,6 +144,15 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len); len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len); +#ifdef CONFIG_SAE + if (sta->sae && sta->sae->state == SAE_ACCEPTED) { + res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n", + sta->sae->group); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_SAE */ + return len; } @@ -164,7 +174,7 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, if (hwaddr_aton(txtaddr, addr)) { ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; } @@ -203,7 +213,7 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, if (hwaddr_aton(txtaddr, addr) || (sta = ap_get_sta(hapd, addr)) == NULL) { ret = os_snprintf(buf, buflen, "FAIL\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; return ret; } @@ -422,7 +432,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->num_sta_ht40_intolerant, iface->olbc_ht, iface->ht_op_mode); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -444,7 +454,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->dfs_cac_ms / 1000, left_time); } - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -463,7 +473,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, iface->conf->vht_oper_centr_freq_seg1_idx); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -480,7 +490,7 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, wpa_ssid_txt(bss->conf->ssid.ssid, bss->conf->ssid.ssid_len), (int) i, bss->num_sta); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } diff --git a/src/ap/dfs.c b/src/ap/dfs.c index a6ec20bd..0db5ef69 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -440,7 +440,8 @@ dfs_get_valid_channel(struct hostapd_iface *iface, if (num_available_chandefs == 0) return NULL; - os_get_random((u8 *) &_rand, sizeof(_rand)); + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); chan_idx = _rand % num_available_chandefs; dfs_find_channel(iface, &chan, chan_idx, skip_radar); @@ -639,6 +640,16 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1; int skip_radar = 0; + if (!iface->current_mode) { + /* + * This can happen with drivers that do not provide mode + * information and as such, cannot really use hostapd for DFS. + */ + wpa_printf(MSG_DEBUG, + "DFS: No current_mode information - assume no need to perform DFS operations by hostapd"); + return 1; + } + iface->cac_started = 0; do { diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c new file mode 100644 index 00000000..a7060246 --- /dev/null +++ b/src/ap/dhcp_snoop.c @@ -0,0 +1,166 @@ +/* + * DHCP snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include "utils/common.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "x_snoop.h" +#include "dhcp_snoop.h" + +struct bootp_pkt { + struct iphdr iph; + struct udphdr udph; + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; + u8 exten[312]; +} STRUCT_PACKED; + +#define DHCPACK 5 +static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; + + +static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + const struct bootp_pkt *b; + struct sta_info *sta; + int exten_len; + const u8 *end, *pos; + int res, msgtype = 0, prefixlen = 32; + u32 subnet_mask = 0; + u16 tot_len; + + exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten)); + if (exten_len < 4) + return; + + b = (const struct bootp_pkt *) &buf[ETH_HLEN]; + tot_len = ntohs(b->iph.tot_len); + if (tot_len > (unsigned int) (len - ETH_HLEN)) + return; + + if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) + return; + + /* Parse DHCP options */ + end = (const u8 *) b + tot_len; + pos = &b->exten[4]; + while (pos < end && *pos != 0xff) { + const u8 *opt = pos++; + + if (*opt == 0) /* padding */ + continue; + + pos += *pos + 1; + if (pos >= end) + break; + + switch (*opt) { + case 1: /* subnet mask */ + if (opt[1] == 4) + subnet_mask = WPA_GET_BE32(&opt[2]); + if (subnet_mask == 0) + return; + while (!(subnet_mask & 0x1)) { + subnet_mask >>= 1; + prefixlen--; + } + break; + case 53: /* message type */ + if (opt[1]) + msgtype = opt[2]; + break; + default: + break; + } + } + + if (msgtype == DHCPACK) { + if (b->your_ip == 0) + return; + + /* DHCPACK for DHCPREQUEST */ + sta = ap_get_sta(hapd, b->hw_addr); + if (!sta) + return; + + wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR + " @ IPv4 address %X/%d", + MAC2STR(sta->addr), ntohl(b->your_ip), prefixlen); + + if (sta->ipaddr == b->your_ip) + return; + + if (sta->ipaddr != 0) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Removing IPv4 address %X from the ip neigh table", + sta->ipaddr); + hostapd_drv_br_delete_ip_neigh(hapd, 4, + (u8 *) &sta->ipaddr); + } + + res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip, + prefixlen, sta->addr); + if (res) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Adding ip neigh table failed: %d", + res); + return; + } + sta->ipaddr = b->your_ip; + } + + if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) { + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + } +} + + +int dhcp_snoop_init(struct hostapd_data *hapd) +{ + hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp, + L2_PACKET_FILTER_DHCP); + if (hapd->sock_dhcp == NULL) { + wpa_printf(MSG_DEBUG, + "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void dhcp_snoop_deinit(struct hostapd_data *hapd) +{ + l2_packet_deinit(hapd->sock_dhcp); +} diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h new file mode 100644 index 00000000..93d0050f --- /dev/null +++ b/src/ap/dhcp_snoop.h @@ -0,0 +1,30 @@ +/* + * DHCP snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DHCP_SNOOP_H +#define DHCP_SNOOP_H + +#ifdef CONFIG_PROXYARP + +int dhcp_snoop_init(struct hostapd_data *hapd); +void dhcp_snoop_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_PROXYARP */ + +static inline int dhcp_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void dhcp_snoop_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_PROXYARP */ + +#endif /* DHCP_SNOOP_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 3bde7205..40a2a9c7 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -442,9 +442,10 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, int channel, chwidth, seg0_idx = 0, seg1_idx = 0; hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "driver had channel switch: " - "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d", - freq, ht, offset, width, cf1, cf2); + HOSTAPD_LEVEL_INFO, + "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + freq, ht, offset, width, channel_width_to_string(width), + cf1, cf2); hapd->iface->freq = freq; @@ -489,6 +490,8 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; + if (!ht) + hapd->iconf->ieee80211ac = 0; hapd->iconf->secondary_channel = offset; hapd->iconf->vht_oper_chwidth = chwidth; hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; @@ -522,6 +525,51 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd, } +#ifdef CONFIG_ACS +static void hostapd_acs_channel_selected(struct hostapd_data *hapd, + u8 pri_channel, u8 sec_channel) +{ + int channel; + int ret; + + if (hapd->iconf->channel) { + wpa_printf(MSG_INFO, "ACS: Channel was already set to %d", + hapd->iconf->channel); + return; + } + + hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel); + + channel = pri_channel; + if (!channel) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "driver switched to bad channel"); + return; + } + + hapd->iconf->channel = channel; + + if (sec_channel == 0) + hapd->iconf->secondary_channel = 0; + else if (sec_channel < pri_channel) + hapd->iconf->secondary_channel = -1; + else if (sec_channel > pri_channel) + hapd->iconf->secondary_channel = 1; + else { + wpa_printf(MSG_ERROR, "Invalid secondary channel!"); + return; + } + + ret = hostapd_acs_completed(hapd->iface, 0); + if (ret) { + wpa_printf(MSG_ERROR, + "ACS: Possibly channel configuration is invalid"); + } +} +#endif /* CONFIG_ACS */ + + int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) @@ -858,6 +906,42 @@ static void hostapd_update_nf(struct hostapd_iface *iface, } +static void hostapd_single_channel_get_survey(struct hostapd_iface *iface, + struct survey_results *survey_res) +{ + struct hostapd_channel_data *chan; + struct freq_survey *survey; + u64 divisor, dividend; + + survey = dl_list_first(&survey_res->survey_list, struct freq_survey, + list); + if (!survey || !survey->freq) + return; + + chan = hostapd_get_mode_channel(iface, survey->freq); + if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED) + return; + + wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)", + survey->freq, + (unsigned long int) survey->channel_time, + (unsigned long int) survey->channel_time_busy); + + if (survey->channel_time > iface->last_channel_time && + survey->channel_time > survey->channel_time_busy) { + dividend = survey->channel_time_busy - + iface->last_channel_time_busy; + divisor = survey->channel_time - iface->last_channel_time; + + iface->channel_utilization = dividend * 255 / divisor; + wpa_printf(MSG_DEBUG, "Channel Utilization: %d", + iface->channel_utilization); + } + iface->last_channel_time = survey->channel_time; + iface->last_channel_time_busy = survey->channel_time_busy; +} + + static void hostapd_event_get_survey(struct hostapd_data *hapd, struct survey_results *survey_results) { @@ -870,6 +954,11 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd, return; } + if (survey_results->freq_filter) { + hostapd_single_channel_get_survey(iface, survey_results); + return; + } + dl_list_for_each_safe(survey, tmp, &survey_results->survey_list, struct freq_survey, list) { chan = hostapd_get_mode_channel(iface, survey->freq); @@ -979,12 +1068,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, if (hapd->iface->scan_cb) hapd->iface->scan_cb(hapd->iface); break; -#ifdef CONFIG_IEEE80211R - case EVENT_FT_RRB_RX: - wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src, - data->ft_rrb_rx.data, data->ft_rrb_rx.data_len); - break; -#endif /* CONFIG_IEEE80211R */ case EVENT_WPS_BUTTON_PUSHED: hostapd_wps_button_pushed(hapd, NULL); break; @@ -1125,6 +1208,19 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, hapd->iface, data->channel_list_changed.initiator); break; #endif /* NEED_AP_MLME */ + case EVENT_INTERFACE_ENABLED: + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED); + break; + case EVENT_INTERFACE_DISABLED: + wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED); + break; +#ifdef CONFIG_ACS + case EVENT_ACS_CHANNEL_SELECTED: + hostapd_acs_channel_selected( + hapd, data->acs_selected_channels.pri_channel, + data->acs_selected_channels.sec_channel); + break; +#endif /* CONFIG_ACS */ default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index ad07107d..9d19f98d 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -58,7 +58,7 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) } if (sta->gas_dialog == NULL) { - sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX * + sta->gas_dialog = os_calloc(GAS_DIALOG_MAX, sizeof(struct gas_dialog_info)); if (sta->gas_dialog == NULL) return NULL; @@ -748,6 +748,7 @@ struct anqp_query_info { size_t home_realm_query_len; const u8 *icon_name; size_t icon_name_len; + int p2p_sd; }; @@ -919,6 +920,21 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, return; } +#ifdef CONFIG_P2P + if (*pos == P2P_OUI_TYPE) { + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + *pos); + qi->p2p_sd = 1; + return; + } +#endif /* CONFIG_P2P */ + if (*pos != HS20_ANQP_OUI_TYPE) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", *pos); @@ -969,6 +985,14 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, buf); if (!buf) return; +#ifdef CONFIG_P2P + if (wpabuf_len(buf) == 0 && qi->p2p_sd) { + wpa_printf(MSG_DEBUG, + "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)"); + wpabuf_free(buf); + return; + } +#endif /* CONFIG_P2P */ if (wpabuf_len(buf) > hapd->gas_frag_limit || hapd->conf->gas_comeback_delay) { diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 31423915..2103747e 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -35,6 +35,10 @@ #include "gas_serv.h" #include "dfs.h" #include "ieee802_11.h" +#include "bss_load.h" +#include "x_snoop.h" +#include "dhcp_snoop.h" +#include "ndisc_snoop.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -252,6 +256,16 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) static void hostapd_free_hapd_data(struct hostapd_data *hapd) { + os_free(hapd->probereq_cb); + hapd->probereq_cb = NULL; + +#ifdef CONFIG_P2P + wpabuf_free(hapd->p2p_beacon_ie); + hapd->p2p_beacon_ie = NULL; + wpabuf_free(hapd->p2p_probe_resp_ie); + hapd->p2p_probe_resp_ie = NULL; +#endif /* CONFIG_P2P */ + if (!hapd->started) { wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started", __func__, hapd->conf->iface); @@ -294,28 +308,28 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) } } - os_free(hapd->probereq_cb); - hapd->probereq_cb = NULL; - -#ifdef CONFIG_P2P - wpabuf_free(hapd->p2p_beacon_ie); - hapd->p2p_beacon_ie = NULL; - wpabuf_free(hapd->p2p_probe_resp_ie); - hapd->p2p_probe_resp_ie = NULL; -#endif /* CONFIG_P2P */ - wpabuf_free(hapd->time_adv); #ifdef CONFIG_INTERWORKING gas_serv_deinit(hapd); #endif /* CONFIG_INTERWORKING */ + bss_load_update_deinit(hapd); + ndisc_snoop_deinit(hapd); + dhcp_snoop_deinit(hapd); + x_snoop_deinit(hapd); + #ifdef CONFIG_SQLITE bin_clear_free(hapd->tmp_eap_user.identity, hapd->tmp_eap_user.identity_len); bin_clear_free(hapd->tmp_eap_user.password, hapd->tmp_eap_user.password_len); #endif /* CONFIG_SQLITE */ + +#ifdef CONFIG_MESH + wpabuf_free(hapd->mesh_pending_auth); + hapd->mesh_pending_auth = NULL; +#endif /* CONFIG_MESH */ } @@ -691,6 +705,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) int ssid_len, set_ssid; char force_ifname[IFNAMSIZ]; u8 if_addr[ETH_ALEN]; + int flush_old_stations = 1; wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)", __func__, hapd, conf->iface, first); @@ -745,7 +760,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; - hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID); +#ifdef CONFIG_MESH + if (hapd->iface->mconf == NULL) + flush_old_stations = 0; +#endif /* CONFIG_MESH */ + + if (flush_old_stations) + hostapd_flush_old_stations(hapd, + WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_set_privacy(hapd, 0); hostapd_broadcast_wep_clear(hapd); @@ -875,6 +897,31 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) } #endif /* CONFIG_INTERWORKING */ + if (conf->bss_load_update_period && bss_load_update_init(hapd)) { + wpa_printf(MSG_ERROR, "BSS Load initialization failed"); + return -1; + } + + if (conf->proxy_arp) { + if (x_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Generic snooping infrastructure initialization failed"); + return -1; + } + + if (dhcp_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "DHCP snooping initialization failed"); + return -1; + } + + if (ndisc_snoop_init(hapd)) { + wpa_printf(MSG_ERROR, + "Neighbor Discovery snooping initialization failed"); + return -1; + } + } + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { wpa_printf(MSG_ERROR, "VLAN initialization failed."); return -1; @@ -899,6 +946,11 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) int i; struct hostapd_tx_queue_params *p; +#ifdef CONFIG_MESH + if (iface->mconf == NULL) + return; +#endif /* CONFIG_MESH */ + for (i = 0; i < NUM_TX_QUEUES; i++) { p = &iface->conf->tx_queue[i]; @@ -1164,6 +1216,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) struct hostapd_data *hapd = iface->bss[0]; size_t j; u8 *prev_addr; + int delay_apply_cfg = 0; if (err) goto fail; @@ -1193,7 +1246,17 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) } #endif /* NEED_AP_MLME */ - if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, +#ifdef CONFIG_MESH + if (iface->mconf != NULL) { + wpa_printf(MSG_DEBUG, + "%s: Mesh configuration will be applied while joining the mesh network", + iface->bss[0]->conf->iface); + delay_apply_cfg = 1; + } +#endif /* CONFIG_MESH */ + + if (!delay_apply_cfg && + hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, hapd->iconf->channel, hapd->iconf->ieee80211n, hapd->iconf->ieee80211ac, @@ -1820,7 +1883,7 @@ static struct hostapd_iface * hostapd_data_alloc( hapd_iface->conf = conf; hapd_iface->num_bss = conf->num_bss; - hapd_iface->bss = os_zalloc(conf->num_bss * + hapd_iface->bss = os_calloc(conf->num_bss, sizeof(struct hostapd_data *)); if (hapd_iface->bss == NULL) return NULL; @@ -1882,11 +1945,19 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) } if (new_iface) { - if (interfaces->driver_init(hapd_iface) || - hostapd_setup_interface(hapd_iface)) { + if (interfaces->driver_init(hapd_iface)) { interfaces->count--; goto fail; } + + if (hostapd_setup_interface(hapd_iface)) { + interfaces->count--; + hostapd_deinit_driver( + hapd_iface->bss[0]->driver, + hapd_iface->bss[0]->drv_priv, + hapd_iface); + goto fail; + } } else { /* Assign new BSS with bss[0]'s driver info */ hapd = hapd_iface->bss[hapd_iface->num_bss - 1]; @@ -1978,14 +2049,14 @@ fail: wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)", __func__, hapd_iface->bss[i], hapd->conf->iface); + hostapd_cleanup(hapd); os_free(hapd); hapd_iface->bss[i] = NULL; } os_free(hapd_iface->bss); + hapd_iface->bss = NULL; } - wpa_printf(MSG_DEBUG, "%s: free iface %p", - __func__, hapd_iface); - os_free(hapd_iface); + hostapd_cleanup_iface(hapd_iface); } return -1; } @@ -2367,6 +2438,12 @@ int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { int ret; + + if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) { + wpa_printf(MSG_INFO, "CSA is not supported"); + return -1; + } + ret = hostapd_fill_csa_settings(hapd, settings); if (ret) return ret; diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index 3c8727b1..8e2c70ec 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -1,6 +1,6 @@ /* * hostapd / Initialization and configuration - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define HOSTAPD_H #include "common/defs.h" +#include "utils/list.h" #include "ap_config.h" #include "drivers/driver.h" @@ -22,6 +23,9 @@ struct ieee80211_ht_capabilities; struct full_dynamic_vlan; enum wps_event; union wps_event_data; +#ifdef CONFIG_MESH +struct mesh_conf; +#endif /* CONFIG_MESH */ struct hostapd_iface; @@ -150,6 +154,7 @@ struct hostapd_data { void *ssl_ctx; void *eap_sim_db_priv; struct radius_server_data *radius_srv; + struct dl_list erp_keys; /* struct eap_server_erp_key */ int parameter_set_count; @@ -218,6 +223,9 @@ struct hostapd_data { unsigned int cs_c_off_proberesp; int csa_in_progress; + /* BSS Load */ + unsigned int bss_load_update_timeout; + #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -235,6 +243,17 @@ struct hostapd_data { #ifdef CONFIG_INTERWORKING size_t gas_frag_limit; #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_PROXYARP + struct l2_packet_data *sock_dhcp; + struct l2_packet_data *sock_ndisc; +#endif /* CONFIG_PROXYARP */ +#ifdef CONFIG_MESH + int num_plinks; + int max_plinks; + void (*mesh_sta_free_cb)(struct sta_info *sta); + struct wpabuf *mesh_pending_auth; + struct os_reltime mesh_pending_auth_time; +#endif /* CONFIG_MESH */ #ifdef CONFIG_SQLITE struct hostapd_eap_user tmp_eap_user; @@ -247,7 +266,10 @@ struct hostapd_data { #endif /* CONFIG_SAE */ #ifdef CONFIG_TESTING_OPTIONS - int ext_mgmt_frame_handling; + unsigned int ext_mgmt_frame_handling:1; + unsigned int ext_eapol_frame_io:1; + + struct l2_packet_data *l2_test; #endif /* CONFIG_TESTING_OPTIONS */ }; @@ -272,6 +294,10 @@ struct hostapd_iface { HAPD_IFACE_ENABLED } state; +#ifdef CONFIG_MESH + struct mesh_conf *mconf; +#endif /* CONFIG_MESH */ + size_t num_bss; struct hostapd_data **bss; @@ -288,7 +314,10 @@ struct hostapd_iface { struct ap_info *ap_list; /* AP info list head */ struct ap_info *ap_hash[STA_HASH_SIZE]; - unsigned int drv_flags; + u64 drv_flags; + + /* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */ + unsigned int smps_modes; /* * A bitmap of supported protocols for probe response offload. See @@ -351,6 +380,11 @@ struct hostapd_iface { /* lowest observed noise floor in dBm */ s8 lowest_nf; + /* channel utilization calculation */ + u64 last_channel_time; + u64 last_channel_time_busy; + u8 channel_utilization; + unsigned int dfs_cac_ms; struct os_reltime dfs_cac_start; diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 4e66d1b7..f959215d 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -107,7 +107,8 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) /* * Disable all channels that are marked not to allow - * IBSS operation or active scanning. + * to initiate radiation (a.k.a. passive scan and no + * IBSS). * Use radar channels only if the driver supports DFS. */ if ((feature->channels[j].flag & @@ -118,8 +119,7 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) !(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) || (feature->channels[j].flag & - (HOSTAPD_CHAN_NO_IBSS | - HOSTAPD_CHAN_PASSIVE_SCAN))) { + HOSTAPD_CHAN_NO_IR)) { feature->channels[j].flag |= HOSTAPD_CHAN_DISABLED; } @@ -746,11 +746,24 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) return 0; } - if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && - (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { - wpa_printf(MSG_ERROR, "Driver does not support configured " - "HT capability [SMPS-*]"); - return 0; + switch (conf & HT_CAP_INFO_SMPS_MASK) { + case HT_CAP_INFO_SMPS_STATIC: + if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) { + wpa_printf(MSG_ERROR, + "Driver does not support configured HT capability [SMPS-STATIC]"); + return 0; + } + break; + case HT_CAP_INFO_SMPS_DYNAMIC: + if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) { + wpa_printf(MSG_ERROR, + "Driver does not support configured HT capability [SMPS-DYNAMIC]"); + return 0; + } + break; + case HT_CAP_INFO_SMPS_DISABLED: + default: + break; } if ((conf & HT_CAP_INFO_GREEN_FIELD) && @@ -839,16 +852,16 @@ static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) } -static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap, +static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, + unsigned int shift, const char *name) { - u32 hw_max = hw & cap; - u32 conf_val = conf & cap; + u32 hw_max = hw & mask; + u32 conf_val = conf & mask; if (conf_val > hw_max) { - int offset = find_first_bit(cap); wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", - name, conf_val >> offset, hw_max >> offset); + name, conf_val >> shift, hw_max >> shift); return 0; } return 1; @@ -871,7 +884,8 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) #define VHT_CAP_CHECK_MAX(cap) \ do { \ - if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \ + if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ + #cap)) \ return 0; \ } while (0) @@ -945,12 +959,10 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, return 1; wpa_printf(MSG_DEBUG, - "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s", + "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s", primary ? "" : "Configured HT40 secondary ", i, chan->chan, chan->flag, - chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "", - chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ? - " PASSIVE-SCAN" : "", + chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "", chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); } diff --git a/src/ap/iapp.c b/src/ap/iapp.c index 9b2900f2..99aa04dc 100644 --- a/src/ap/iapp.c +++ b/src/ap/iapp.c @@ -361,7 +361,7 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) switch (hdr->command) { case IAPP_CMD_ADD_notify: - iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); + iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr)); break; case IAPP_CMD_MOVE_notify: /* TODO: MOVE is using TCP; so move this to TCP handler once it diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index de1ee5ed..97f98f28 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,6 +29,7 @@ #include "sta_info.h" #include "ieee802_1x.h" #include "wpa_auth.h" +#include "pmksa_cache_auth.h" #include "wmm.h" #include "ap_list.h" #include "accounting.h" @@ -198,6 +199,9 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, (hapd->iconf->spectrum_mgmt_required || dfs)) capab |= WLAN_CAPABILITY_SPECTRUM_MGMT; + if (hapd->conf->radio_measurements) + capab |= IEEE80211_CAP_RRM; + return capab; } @@ -324,8 +328,8 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, #ifdef CONFIG_SAE -static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, - struct sta_info *sta) +static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, + struct sta_info *sta, int update) { struct wpabuf *buf; @@ -334,7 +338,8 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, return NULL; } - if (sae_prepare_commit(hapd->own_addr, sta->addr, + if (update && + sae_prepare_commit(hapd->own_addr, sta->addr, (u8 *) hapd->conf->ssid.wpa_passphrase, os_strlen(hapd->conf->ssid.wpa_passphrase), sta->sae) < 0) { @@ -342,15 +347,11 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd, return NULL; } - if (sae_process_commit(sta->sae) < 0) { - wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); - return NULL; - } - buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); if (buf == NULL) return NULL; - sae_write_commit(sta->sae, buf, NULL); + sae_write_commit(sta->sae, buf, sta->sae->tmp ? + sta->sae->tmp->anti_clogging_token : NULL); return buf; } @@ -371,6 +372,46 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd, } +static int auth_sae_send_commit(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *bssid, int update) +{ + struct wpabuf *data; + + data = auth_build_sae_commit(hapd, sta, update); + if (data == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + send_auth_reply(hapd, sta->addr, bssid, + WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, + wpabuf_head(data), wpabuf_len(data)); + + wpabuf_free(data); + + return WLAN_STATUS_SUCCESS; +} + + +static int auth_sae_send_confirm(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *bssid) +{ + struct wpabuf *data; + + data = auth_build_sae_confirm(hapd, sta); + if (data == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + send_auth_reply(hapd, sta->addr, bssid, + WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, + wpabuf_head(data), wpabuf_len(data)); + + wpabuf_free(data); + + return WLAN_STATUS_SUCCESS; +} + + static int use_sae_anti_clogging(struct hostapd_data *hapd) { struct sta_info *sta; @@ -411,7 +452,7 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr, static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, - const u8 *addr) + int group, const u8 *addr) { struct wpabuf *buf; u8 *token; @@ -428,10 +469,12 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, hapd->last_sae_token_key_update = now; } - buf = wpabuf_alloc(SHA256_MAC_LEN); + buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN); if (buf == NULL) return NULL; + wpabuf_put_le16(buf, group); /* Finite Cyclic Group */ + token = wpabuf_put(buf, SHA256_MAC_LEN); hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key), addr, ETH_ALEN, token); @@ -440,15 +483,150 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, } +static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *bssid, u8 auth_transaction) +{ + int ret; + + if (auth_transaction != 1 && auth_transaction != 2) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + switch (sta->sae->state) { + case SAE_NOTHING: + if (auth_transaction == 1) { + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + sta->sae->state = SAE_COMMITTED; + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* + * In mesh case, both Commit and Confirm can be sent + * immediately. In infrastructure BSS, only a single + * Authentication frame (Commit) is expected from the AP + * here and the second one (Confirm) will be sent once + * the STA has sent its second Authentication frame + * (Confirm). + */ + if (hapd->conf->mesh & MESH_ENABLED) { + /* + * Send both Commit and Confirm immediately + * based on SAE finite state machine + * Nothing -> Confirm transition. + */ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + sta->sae->state = SAE_CONFIRMED; + } else { + /* + * For infrastructure BSS, send only the Commit + * message now to get alternating sequence of + * Authentication frames between the AP and STA. + * Confirm will be sent in + * Commited -> Confirmed/Accepted transition + * when receiving Confirm from STA. + */ + } + } else { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "SAE confirm before commit"); + } + break; + case SAE_COMMITTED: + if (auth_transaction == 1) { + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + sta->sae->state = SAE_CONFIRMED; + } else if (hapd->conf->mesh & MESH_ENABLED) { + /* + * In mesh case, follow SAE finite state machine and + * send Commit now. + */ + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + } else { + /* + * For instructure BSS, send the postponed Confirm from + * Nothing -> Confirmed transition that was reduced to + * Nothing -> Committed above. + */ + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + + sta->sae->state = SAE_CONFIRMED; + + /* + * Since this was triggered on Confirm RX, run another + * step to get to Accepted without waiting for + * additional events. + */ + return sae_sm_step(hapd, sta, bssid, auth_transaction); + } + break; + case SAE_CONFIRMED: + if (auth_transaction == 1) { + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + ret = auth_sae_send_confirm(hapd, sta, bssid); + if (ret) + return ret; + } else { + sta->flags |= WLAN_STA_AUTH; + sta->auth_alg = WLAN_AUTH_SAE; + mlme_authenticate_indication(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->sae->state = SAE_ACCEPTED; + wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, + sta->sae->pmk); + } + break; + case SAE_ACCEPTED: + if (auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR + ") doing reauthentication", + MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); + } else { + ret = auth_sae_send_confirm(hapd, sta, bssid); + sae_clear_temp_data(sta->sae); + if (ret) + return ret; + } + break; + default: + wpa_printf(MSG_ERROR, "SAE: invalid state %d", + sta->sae->state); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + return WLAN_STATUS_SUCCESS; +} + + static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, const struct ieee80211_mgmt *mgmt, size_t len, - u8 auth_transaction) + u16 auth_transaction, u16 status_code) { u16 resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; if (!sta->sae) { - if (auth_transaction != 1) + if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) return; sta->sae = os_zalloc(sizeof(*sta->sae)); if (sta->sae == NULL) @@ -457,11 +635,62 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, } if (auth_transaction == 1) { - const u8 *token = NULL; + const u8 *token = NULL, *pos, *end; size_t token_len = 0; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "start SAE authentication (RX commit)"); + "start SAE authentication (RX commit, status=%u)", + status_code); + + if ((hapd->conf->mesh & MESH_ENABLED) && + status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && + sta->sae->tmp) { + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + if (pos + sizeof(le16) > end) { + wpa_printf(MSG_ERROR, + "SAE: Too short anti-clogging token request"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + resp = sae_group_allowed(sta->sae, + hapd->conf->sae_groups, + WPA_GET_LE16(pos)); + if (resp != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_ERROR, + "SAE: Invalid group in anti-clogging token request"); + goto reply; + } + pos += sizeof(le16); + + wpabuf_free(sta->sae->tmp->anti_clogging_token); + sta->sae->tmp->anti_clogging_token = + wpabuf_alloc_copy(pos, end - pos); + if (sta->sae->tmp->anti_clogging_token == NULL) { + wpa_printf(MSG_ERROR, + "SAE: Failed to alloc for anti-clogging token"); + return; + } + + /* + * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code + * is 76, a new Commit Message shall be constructed + * with the Anti-Clogging Token from the received + * Authentication frame, and the commit-scalar and + * COMMIT-ELEMENT previously sent. + */ + if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) { + wpa_printf(MSG_ERROR, + "SAE: Failed to send commit message"); + return; + } + sta->sae->state = SAE_COMMITTED; + return; + } + + if (status_code != WLAN_STATUS_SUCCESS) + return; + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, @@ -474,67 +703,56 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, return; } - if (resp == WLAN_STATUS_SUCCESS) { - if (!token && use_sae_anti_clogging(hapd)) { - wpa_printf(MSG_DEBUG, "SAE: Request anti-" - "clogging token from " MACSTR, - MAC2STR(sta->addr)); - data = auth_build_token_req(hapd, sta->addr); - resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; - } else { - data = auth_process_sae_commit(hapd, sta); - if (data == NULL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - else - sta->sae->state = SAE_COMMITTED; - } + if (resp != WLAN_STATUS_SUCCESS) + goto reply; + + if (!token && use_sae_anti_clogging(hapd)) { + wpa_printf(MSG_DEBUG, + "SAE: Request anti-clogging token from " + MACSTR, MAC2STR(sta->addr)); + data = auth_build_token_req(hapd, sta->sae->group, + sta->addr); + resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; + if (hapd->conf->mesh & MESH_ENABLED) + sta->sae->state = SAE_NOTHING; + goto reply; } + + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else if (auth_transaction == 2) { - if (sta->sae->state != SAE_COMMITTED) { - hostapd_logger(hapd, sta->addr, - HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_DEBUG, - "SAE confirm before commit"); - resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; - goto failed; - } hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "SAE authentication (RX confirm)"); - if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable) < 0) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - } else { - resp = WLAN_STATUS_SUCCESS; - sta->flags |= WLAN_STA_AUTH; - wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->auth_alg = WLAN_AUTH_SAE; - mlme_authenticate_indication(hapd, sta); - - data = auth_build_sae_confirm(hapd, sta); - if (data == NULL) + "SAE authentication (RX confirm, status=%u)", + status_code); + if (status_code != WLAN_STATUS_SUCCESS) + return; + if (sta->sae->state >= SAE_CONFIRMED || + !(hapd->conf->mesh & MESH_ENABLED)) { + if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, + ((u8 *) mgmt) + len - + mgmt->u.auth.variable) < 0) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - else { - sta->sae->state = SAE_ACCEPTED; - sae_clear_temp_data(sta->sae); + goto reply; } } + resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "unexpected SAE authentication transaction %u", - auth_transaction); + "unexpected SAE authentication transaction %u (status=%u)", + auth_transaction, status_code); + if (status_code != WLAN_STATUS_SUCCESS) + return; resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; } -failed: - sta->auth_alg = WLAN_AUTH_SAE; - - send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, - auth_transaction, resp, - data ? wpabuf_head(data) : (u8 *) "", - data ? wpabuf_len(data) : 0); +reply: + if (resp != WLAN_STATUS_SUCCESS) { + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0); + } wpabuf_free(data); } #endif /* CONFIG_SAE */ @@ -556,6 +774,7 @@ static void handle_auth(struct hostapd_data *hapd, size_t resp_ies_len = 0; char *identity = NULL; char *radius_cui = NULL; + u16 seq_ctrl; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", @@ -577,6 +796,7 @@ static void handle_auth(struct hostapd_data *hapd, auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); status_code = le_to_host16(mgmt->u.auth.status_code); fc = le_to_host16(mgmt->frame_control); + seq_ctrl = le_to_host16(mgmt->seq_ctrl); if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + 2 + WLAN_AUTH_CHALLENGE_LEN && @@ -585,10 +805,12 @@ static void handle_auth(struct hostapd_data *hapd, challenge = &mgmt->u.auth.variable[2]; wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " - "auth_transaction=%d status_code=%d wep=%d%s", + "auth_transaction=%d status_code=%d wep=%d%s " + "seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code, !!(fc & WLAN_FC_ISWEP), - challenge ? " challenge" : ""); + challenge ? " challenge" : "", + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; @@ -649,11 +871,46 @@ static void handle_auth(struct hostapd_data *hapd, return; } - sta = ap_sta_add(hapd, mgmt->sa); - if (!sta) { - resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - goto fail; + sta = ap_get_sta(hapd, mgmt->sa); + if (sta) { + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == WLAN_FC_STYPE_AUTH) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated authentication frame seq_ctrl=0x%x", + seq_ctrl); + return; + } + } else { +#ifdef CONFIG_MESH + if (hapd->conf->mesh & MESH_ENABLED) { + /* if the mesh peer is not available, we don't do auth. + */ + wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR + " not yet known - drop Authentiation frame", + MAC2STR(mgmt->sa)); + /* + * Save a copy of the frame so that it can be processed + * if a new peer entry is added shortly after this. + */ + wpabuf_free(hapd->mesh_pending_auth); + hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len); + os_get_reltime(&hapd->mesh_pending_auth_time); + return; + } +#endif /* CONFIG_MESH */ + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } } + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = WLAN_FC_STYPE_AUTH; if (vlan_id > 0) { if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) { @@ -737,7 +994,23 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: - handle_auth_sae(hapd, sta, mgmt, len, auth_transaction); +#ifdef CONFIG_MESH + if (status_code == WLAN_STATUS_SUCCESS && + hapd->conf->mesh & MESH_ENABLED) { + if (sta->wpa_sm == NULL) + sta->wpa_sm = + wpa_auth_sta_init(hapd->wpa_auth, + sta->addr, NULL); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, + "SAE: Failed to initialize WPA state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_MESH */ + handle_auth_sae(hapd, sta, mgmt, len, auth_transaction, + status_code); return; #endif /* CONFIG_SAE */ } @@ -1072,9 +1345,21 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_SAE if (wpa_auth_uses_sae(sta->wpa_sm) && - sta->auth_alg != WLAN_AUTH_SAE && - !(sta->auth_alg == WLAN_AUTH_FT && - wpa_auth_uses_ft_sae(sta->wpa_sm))) { + sta->auth_alg == WLAN_AUTH_OPEN) { + struct rsn_pmksa_cache_entry *sa; + sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_DEBUG, + "SAE: No PMKSA cache entry found for " + MACSTR, MAC2STR(sta->addr)); + return WLAN_STATUS_INVALID_PMKID; + } + wpa_printf(MSG_DEBUG, "SAE: " MACSTR + " using PMKSA caching", MAC2STR(sta->addr)); + } else if (wpa_auth_uses_sae(sta->wpa_sm) && + sta->auth_alg != WLAN_AUTH_SAE && + !(sta->auth_alg == WLAN_AUTH_FT && + wpa_auth_uses_ft_sae(sta->wpa_sm))) { wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use " "SAE AKM after non-SAE auth_alg %u", MAC2STR(sta->addr), sta->auth_alg); @@ -1275,7 +1560,7 @@ static void handle_assoc(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { - u16 capab_info, listen_interval; + u16 capab_info, listen_interval, seq_ctrl, fc; u16 resp = WLAN_STATUS_SUCCESS; const u8 *pos; int left, i; @@ -1308,15 +1593,19 @@ static void handle_assoc(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ + fc = le_to_host16(mgmt->frame_control); + seq_ctrl = le_to_host16(mgmt->seq_ctrl); + if (reassoc) { capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); listen_interval = le_to_host16( mgmt->u.reassoc_req.listen_interval); wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR " capab_info=0x%02x listen_interval=%d current_ap=" - MACSTR, + MACSTR " seq_ctrl=0x%x%s", MAC2STR(mgmt->sa), capab_info, listen_interval, - MAC2STR(mgmt->u.reassoc_req.current_ap)); + MAC2STR(mgmt->u.reassoc_req.current_ap), + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); pos = mgmt->u.reassoc_req.variable; } else { @@ -1324,8 +1613,10 @@ static void handle_assoc(struct hostapd_data *hapd, listen_interval = le_to_host16( mgmt->u.assoc_req.listen_interval); wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR - " capab_info=0x%02x listen_interval=%d", - MAC2STR(mgmt->sa), capab_info, listen_interval); + " capab_info=0x%02x listen_interval=%d " + "seq_ctrl=0x%x%s", + MAC2STR(mgmt->sa), capab_info, listen_interval, + seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : ""); left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); pos = mgmt->u.assoc_req.variable; } @@ -1351,6 +1642,21 @@ static void handle_assoc(struct hostapd_data *hapd, return; } + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated association frame seq_ctrl=0x%x", + seq_ctrl); + return; + } + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ; + if (hapd->tkip_countermeasures) { resp = WLAN_REASON_MICHAEL_MIC_FAILURE; goto fail; @@ -1476,6 +1782,7 @@ static void handle_disassoc(struct hostapd_data *hapd, } ap_sta_set_authorized(hapd, sta, 0); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1486,6 +1793,9 @@ static void handle_disassoc(struct hostapd_data *hapd, * authenticated. */ accounting_sta_stop(hapd, sta); ieee802_1x_free_station(sta); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); hostapd_drv_sta_remove(hapd, sta->addr); if (sta->timeout_next == STA_NULLFUNC || @@ -1525,6 +1835,7 @@ static void handle_deauth(struct hostapd_data *hapd, } ap_sta_set_authorized(hapd, sta, 0); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); @@ -1624,6 +1935,26 @@ static int handle_action(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211W */ + if (sta) { + u16 fc = le_to_host16(mgmt->frame_control); + u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl); + + if ((fc & WLAN_FC_RETRY) && + sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && + sta->last_seq_ctrl == seq_ctrl && + sta->last_subtype == WLAN_FC_STYPE_ACTION) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Drop repeated action frame seq_ctrl=0x%x", + seq_ctrl); + return 1; + } + + sta->last_seq_ctrl = seq_ctrl; + sta->last_subtype = WLAN_FC_STYPE_ACTION; + } + switch (mgmt->u.action.category) { #ifdef CONFIG_IEEE80211R case WLAN_ACTION_FT: @@ -1758,6 +2089,9 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, !((hapd->conf->p2p & P2P_GROUP_OWNER) && stype == WLAN_FC_STYPE_ACTION) && #endif /* CONFIG_P2P */ +#ifdef CONFIG_MESH + !(hapd->conf->mesh & MESH_ENABLED) && +#endif /* CONFIG_MESH */ os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address", MAC2STR(mgmt->bssid)); diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index fe878839..3f299f3e 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -211,7 +211,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, struct ieee80211_2040_intol_chan_report *ic_report; int is_ht_allowed = 1; int i; - const u8 *data = ((const u8 *) mgmt) + 1; + const u8 *start = (const u8 *) mgmt; + const u8 *data = start + IEEE80211_HDRLEN + 2; hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", @@ -220,14 +221,22 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) return; - if (len < IEEE80211_HDRLEN + 1) + if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) return; - data++; - bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0]; - ic_report = (struct ieee80211_2040_intol_chan_report *) - (&data[0] + sizeof(*bc_ie)); + bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; + if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || + bc_ie->length < 1) { + wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", + bc_ie->element_id, bc_ie->length); + return; + } + if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) + return; + data += 2 + bc_ie->length; + wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", + bc_ie->coex_param); if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -244,22 +253,34 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, is_ht_allowed = 0; } - if (ic_report && - (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) { + if (start + len - data >= 3 && + data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + u8 ielen = data[1]; + + if (ielen > start + len - data - 2) + return; + ic_report = (struct ieee80211_2040_intol_chan_report *) data; + wpa_printf(MSG_DEBUG, + "20/40 BSS Intolerant Channel Report: Operating Class %u", + ic_report->op_class); + /* Go through the channel report to find any BSS there in the * affected channel range */ - for (i = 0; i < ic_report->length - 1; i++) { - if (is_40_allowed(iface, ic_report->variable[i])) + for (i = 0; i < ielen - 1; i++) { + u8 chan = ic_report->variable[i]; + + if (is_40_allowed(iface, chan)) continue; hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "20_40_INTOLERANT channel %d reported", - ic_report->variable[i]); + chan); is_ht_allowed = 0; - break; } } + wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d", + is_ht_allowed, iface->num_sta_ht40_intolerant); if (!is_ht_allowed && (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { @@ -279,6 +300,9 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, NULL); eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, hapd->iface, NULL); + wpa_printf(MSG_DEBUG, + "Reschedule HT 20/40 timeout to occur in %u seconds", + delay_time); } } } diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 12403f99..d462ac8b 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -174,6 +174,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x01; /* Bit 0 - Coexistence management */ break; case 1: /* Bits 8-15 */ + if (hapd->conf->proxy_arp) + *pos |= 0x10; /* Bit 12 - Proxy ARP */ break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 2d09b67b..2287b281 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -66,6 +66,20 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "EAPOL-TX " MACSTR " %s", + MAC2STR(sta->addr), hex); + os_free(hex); + } + } else +#endif /* CONFIG_TESTING_OPTIONS */ if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { @@ -282,9 +296,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, { const u8 *identity; size_t identity_len; + const struct eap_hdr *hdr = (const struct eap_hdr *) eap; if (len <= sizeof(struct eap_hdr) || - eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + (hdr->code == EAP_CODE_RESPONSE && + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) || + (hdr->code == EAP_CODE_INITIATE && + eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) || + (hdr->code != EAP_CODE_RESPONSE && + hdr->code != EAP_CODE_INITIATE)) return; identity = eap_get_identity(sm->eap, &identity_len); @@ -697,6 +717,39 @@ static void handle_eap_response(struct hostapd_data *hapd, } +static void handle_eap_initiate(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ +#ifdef CONFIG_ERP + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (len < sizeof(*eap) + 1) { + wpa_printf(MSG_INFO, + "handle_eap_initiate: too short response data"); + return; + } + + data = (u8 *) (eap + 1); + type = data[0]; + + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " + "id=%d len=%d) from STA: EAP Initiate type %u", + eap->code, eap->identifier, be_to_host16(eap->length), + type); + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +#endif /* CONFIG_ERP */ +} + + /* Process incoming EAP packet from Supplicant */ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) @@ -740,6 +793,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, " (failure)"); return; + case EAP_CODE_INITIATE: + wpa_printf(MSG_DEBUG, " (initiate)"); + handle_eap_initiate(hapd, sta, eap, eap_len); + break; + case EAP_CODE_FINISH: + wpa_printf(MSG_DEBUG, " (finish)"); + break; default: wpa_printf(MSG_DEBUG, " (unknown code)"); return; @@ -961,8 +1021,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) int key_mgmt; #ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->conf->wpa && - (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + if (hapd->conf->wps_state && + ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) || + (sta->flags & WLAN_STA_WPS))) { /* * Need to enable IEEE 802.1X/EAPOL state machines for possible * WPS handshake even if IEEE 802.1X/EAPOL is not used for @@ -1972,12 +2033,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, } +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * +ieee802_1x_erp_get_key(void *ctx, const char *keyname) +{ + struct hostapd_data *hapd = ctx; + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} + + +static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct hostapd_data *hapd = ctx; + + dl_list_add(&hapd->erp_keys, &erp->list); + return 0; +} + +#endif /* CONFIG_ERP */ + + int ieee802_1x_init(struct hostapd_data *hapd) { int i; struct eapol_auth_config conf; struct eapol_auth_cb cb; + dl_list_init(&hapd->erp_keys); + os_memset(&conf, 0, sizeof(conf)); conf.ctx = hapd; conf.eap_reauth_period = hapd->conf->eap_reauth_period; @@ -1989,6 +2081,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.eap_sim_db_priv = hapd->eap_sim_db_priv; conf.eap_req_id_text = hapd->conf->eap_req_id_text; conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; + conf.erp_domain = hapd->conf->erp_domain; + conf.erp = hapd->conf->eap_server_erp; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2021,6 +2116,10 @@ int ieee802_1x_init(struct hostapd_data *hapd) cb.abort_auth = _ieee802_1x_abort_auth; cb.tx_key = _ieee802_1x_tx_key; cb.eapol_event = ieee802_1x_eapol_event; +#ifdef CONFIG_ERP + cb.erp_get_key = ieee802_1x_erp_get_key; + cb.erp_add_key = ieee802_1x_erp_add_key; +#endif /* CONFIG_ERP */ hapd->eapol_auth = eapol_auth_init(&conf, &cb); if (hapd->eapol_auth == NULL) @@ -2052,6 +2151,18 @@ int ieee802_1x_init(struct hostapd_data *hapd) } +void ieee802_1x_erp_flush(struct hostapd_data *hapd) +{ + struct eap_server_erp_key *erp; + + while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key, + list)) != NULL) { + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); + } +} + + void ieee802_1x_deinit(struct hostapd_data *hapd) { eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); @@ -2062,6 +2173,8 @@ void ieee802_1x_deinit(struct hostapd_data *hapd) eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; + + ieee802_1x_erp_flush(hapd); } @@ -2252,7 +2365,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sta->aid, EAPOL_VERSION, sm->initialize); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2280,7 +2393,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->reAuthPeriod, bool_txt(sm->reAuthEnabled), bool_txt(sm->keyTxEnabled)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2310,7 +2423,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->dot1xAuthEapLengthErrorFramesRx, sm->dot1xAuthLastEapolFrameVersion, MAC2STR(sm->addr)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2348,7 +2461,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2370,7 +2483,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, 1 : 2, (unsigned int) diff.sec, sm->identity); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2383,7 +2496,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, name1 ? name1 : "", sm->eap_type_supp, name2 ? name2 : ""); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index e1df9405..de6e0e75 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -29,6 +29,7 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, struct sta_info *sta, int authorized); void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_erp_flush(struct hostapd_data *hapd); void ieee802_1x_deinit(struct hostapd_data *hapd); int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, const u8 *buf, size_t len, int ack); diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c new file mode 100644 index 00000000..b0d42dcd --- /dev/null +++ b/src/ap/ndisc_snoop.c @@ -0,0 +1,171 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <netinet/ip6.h> +#include <netinet/icmp6.h> + +#include "utils/common.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "list.h" +#include "x_snoop.h" + +struct ip6addr { + struct in6_addr addr; + struct dl_list list; +}; + +struct icmpv6_ndmsg { + struct ip6_hdr ipv6h; + struct icmp6_hdr icmp6h; + struct in6_addr target_addr; + u8 opt_type; + u8 len; + u8 opt_lladdr[0]; +} STRUCT_PACKED; + +#define ROUTER_ADVERTISEMENT 134 +#define NEIGHBOR_SOLICITATION 135 +#define NEIGHBOR_ADVERTISEMENT 136 +#define SOURCE_LL_ADDR 1 + +static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + ip6addr = os_zalloc(sizeof(*ip6addr)); + if (!ip6addr) + return -1; + + os_memcpy(&ip6addr->addr, addr, sizeof(*addr)); + + dl_list_add_tail(&sta->ip6addr, &ip6addr->list); + + return 0; +} + + +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct ip6addr *ip6addr, *prev; + + dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr, + list) { + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr); + os_free(ip6addr); + } +} + + +static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr) +{ + struct ip6addr *ip6addr; + + dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) { + if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] && + ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] && + ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] && + ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3]) + return 1; + } + + return 0; +} + + +static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + struct icmpv6_ndmsg *msg; + struct in6_addr *saddr; + struct sta_info *sta; + int res; + char addrtxt[INET6_ADDRSTRLEN + 1]; + + if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) + return; + msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN]; + switch (msg->icmp6h.icmp6_type) { + case NEIGHBOR_SOLICITATION: + if (len < ETH_HLEN + sizeof(*msg)) + return; + if (msg->opt_type != SOURCE_LL_ADDR) + return; + + saddr = &msg->ipv6h.ip6_src; + if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 && + saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) { + if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN) + return; + sta = ap_get_sta(hapd, msg->opt_lladdr); + if (!sta) + return; + + if (sta_has_ip6addr(sta, saddr)) + return; + + if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt)) + == NULL) + addrtxt[0] = '\0'; + wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for " + MACSTR, addrtxt, MAC2STR(sta->addr)); + hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr); + res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr, + 128, sta->addr); + if (res) { + wpa_printf(MSG_ERROR, + "ndisc_snoop: Adding ip neigh failed: %d", + res); + return; + } + + if (sta_ip6addr_add(sta, saddr)) + return; + } + break; + case ROUTER_ADVERTISEMENT: + if (!hapd->conf->disable_dgaf) + return; + /* fall through */ + case NEIGHBOR_ADVERTISEMENT: + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + continue; + x_snoop_mcast_to_ucast_convert_send(hapd, sta, + (u8 *) buf, len); + } + break; + default: + break; + } +} + + +int ndisc_snoop_init(struct hostapd_data *hapd) +{ + hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc, + L2_PACKET_FILTER_NDISC); + if (hapd->sock_ndisc == NULL) { + wpa_printf(MSG_DEBUG, + "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ + l2_packet_deinit(hapd->sock_ndisc); +} diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h new file mode 100644 index 00000000..3cc9a557 --- /dev/null +++ b/src/ap/ndisc_snoop.h @@ -0,0 +1,36 @@ +/* + * Neighbor Discovery snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef NDISC_SNOOP_H +#define NDISC_SNOOP_H + +#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6) + +int ndisc_snoop_init(struct hostapd_data *hapd); +void ndisc_snoop_deinit(struct hostapd_data *hapd); +void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_PROXYARP && CONFIG_IPV6 */ + +static inline int ndisc_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void ndisc_snoop_deinit(struct hostapd_data *hapd) +{ +} + +static inline void sta_ip6addr_del(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */ + +#endif /* NDISC_SNOOP_H */ diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c index 612babc6..efc1d7e4 100644 --- a/src/ap/peerkey_auth.c +++ b/src/ap/peerkey_auth.c @@ -79,15 +79,15 @@ static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 *buf, *pos; size_t buf_len; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); return; } @@ -253,14 +253,14 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); return; } @@ -324,15 +324,15 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth, void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key) + struct wpa_state_machine *sm, + const u8 *key_data, size_t key_data_len) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; struct rsn_error_kde error; u16 mui, error_type; - if (wpa_parse_kde_ies((const u8 *) (key + 1), - WPA_GET_BE16(key->key_data_length), &kde) < 0) { + if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return; } diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index 9de4cffe..42703821 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -146,6 +146,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, entry->eap_type_authsrv = eapol->eap_type_authsrv; entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; + + entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; + entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; } @@ -183,6 +186,9 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, eapol->eap_type_authsrv = entry->eap_type_authsrv; ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; + + eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; + eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; } @@ -227,6 +233,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes * @aa: Authenticator address * @spa: Supplicant address * @session_timeout: Session timeout @@ -242,8 +250,9 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, - const u8 *aa, const u8 *spa, int session_timeout, - struct eapol_state_machine *eapol, int akmp) + const u8 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos; struct os_reltime now; @@ -251,13 +260,19 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, if (pmk_len > PMK_LEN) return NULL; + if (wpa_key_mgmt_suite_b(akmp) && !kck) + return NULL; + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + if (wpa_key_mgmt_suite_b(akmp)) + rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); + else + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index aa90024d..519555f8 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -30,6 +30,9 @@ struct rsn_pmksa_cache_entry { u8 eap_type_authsrv; int vlan_id; int opportunistic; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; struct rsn_pmksa_cache; @@ -47,6 +50,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( struct rsn_pmksa_cache_entry * pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index efd2a724..debdc067 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -31,6 +31,7 @@ #include "ap_drv_ops.h" #include "gas_serv.h" #include "wnm_ap.h" +#include "ndisc_snoop.h" #include "sta_info.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, @@ -144,6 +145,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) } +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta_ip6addr_del(hapd, sta); +} + + void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { int set_beacon = 0; @@ -156,6 +163,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->flags & WLAN_STA_WDS) hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); + if (!hapd->iface->driver_ap_teardown && !(sta->flags & WLAN_STA_PREAUTH)) hostapd_drv_sta_remove(hapd, sta->addr); @@ -224,6 +235,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) set_beacon++; #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */ +#ifdef CONFIG_MESH + if (hapd->mesh_sta_free_cb) + hapd->mesh_sta_free_cb(sta); +#endif /* CONFIG_MESH */ + if (set_beacon) ieee802_11_set_beacons(hapd->iface); @@ -596,6 +612,8 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) ap_sta_hash_add(hapd, sta); sta->ssid = &hapd->conf->ssid; ap_sta_remove_in_other_bss(hapd, sta); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + dl_list_init(&sta->ip6addr); return sta; } @@ -605,6 +623,10 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) { ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + if (sta->ipaddr) + hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr); + ap_sta_ip6addr_del(hapd, sta); + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", MAC2STR(sta->addr)); if (hostapd_drv_sta_remove(hapd, sta->addr) && @@ -657,6 +679,7 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, { wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_DEAUTH; @@ -695,7 +718,8 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, { wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); ap_sta_set_authorized(hapd, sta, 0); sta->timeout_next = STA_REMOVE; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " @@ -904,7 +928,15 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) sta->sa_query_trans_id = nbuf; sta->sa_query_count++; - os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { + /* + * We don't really care which ID is used here, so simply + * hardcode this if the mostly theoretical os_get_random() + * failure happens. + */ + trans_id[0] = 0x12; + trans_id[1] = 0x34; + } timeout = hapd->conf->assoc_sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; @@ -949,6 +981,11 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED)) return; + if (authorized) + sta->flags |= WLAN_STA_AUTHORIZED; + else + sta->flags &= ~WLAN_STA_AUTHORIZED; + #ifdef CONFIG_P2P if (hapd->p2p_group == NULL) { if (sta->p2p_ie != NULL && @@ -964,6 +1001,10 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_P2P */ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr)); + if (hapd->sta_authorized_cb) + hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, + sta->addr, authorized, dev_addr); + if (authorized) { char ip_addr[100]; ip_addr[0] = '\0'; @@ -984,8 +1025,6 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_CONNECTED "%s%s", buf, ip_addr); - - sta->flags |= WLAN_STA_AUTHORIZED; } else { wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); @@ -993,13 +1032,7 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta, hapd->msg_ctx_parent != hapd->msg_ctx) wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO, AP_STA_DISCONNECTED "%s", buf); - - sta->flags &= ~WLAN_STA_AUTHORIZED; } - - if (hapd->sta_authorized_cb) - hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx, - sta->addr, authorized, dev_addr); } @@ -1087,6 +1120,8 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_VHT ? "[VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? "[WNM_SLEEP_MODE]" : "")); + if (os_snprintf_error(buflen, res)) + res = -1; return res; } diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index faf32d85..588a9e2f 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -9,6 +9,13 @@ #ifndef STA_INFO_H #define STA_INFO_H +#ifdef CONFIG_MESH +/* needed for mesh_plink_state enum */ +#include "common/defs.h" +#endif /* CONFIG_MESH */ + +#include "list.h" + /* STA flags */ #define WLAN_STA_AUTH BIT(0) #define WLAN_STA_ASSOC BIT(1) @@ -41,6 +48,8 @@ struct sta_info { struct sta_info *next; /* next entry in sta list */ struct sta_info *hnext; /* next entry in hash table list */ u8 addr[6]; + be32 ipaddr; + struct dl_list ip6addr; /* list head for struct ip6addr */ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ u32 flags; /* Bitfield of WLAN_STA_* */ u16 capability; @@ -49,6 +58,20 @@ struct sta_info { int supported_rates_len; u8 qosinfo; /* Valid when WLAN_STA_WMM is set */ +#ifdef CONFIG_MESH + enum mesh_plink_state plink_state; + u16 peer_lid; + u16 my_lid; + u16 mpm_close_reason; + int mpm_retries; + u8 my_nonce[32]; + u8 peer_nonce[32]; + u8 aek[32]; /* SHA256 digest length */ + u8 mtk[16]; + u8 mgtk[16]; + u8 sae_auth_retry; +#endif /* CONFIG_MESH */ + unsigned int nonerp_set:1; unsigned int no_short_slot_time_set:1; unsigned int no_short_preamble_set:1; @@ -138,6 +161,12 @@ struct sta_info { #endif /* CONFIG_SAE */ u32 session_timeout; /* valid only if session_timeout_set == 1 */ + + /* Last Authentication/(Re)Association Request/Action frame sequence + * control */ + u16 last_seq_ctrl; + /* Last Authentication/(Re)Association Request/Action frame subtype */ + u8 last_subtype; }; @@ -167,6 +196,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr); void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta); void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index cf25dbb6..7e8fb5c6 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -1,6 +1,6 @@ /* * hostapd - WNM - * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ap_config.h" @@ -358,7 +359,16 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, } wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, MAC2STR(pos)); + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR + " status_code=%u bss_termination_delay=%u target_bssid=" + MACSTR, + MAC2STR(addr), status_code, bss_termination_delay, + MAC2STR(pos)); pos += ETH_ALEN; + } else { + wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR + " status_code=%u bss_termination_delay=%u", + MAC2STR(addr), status_code, bss_termination_delay); } wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", @@ -436,6 +446,34 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd, } +static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta, + int disassoc_timer) +{ + int timeout, beacon_int; + + /* + * Prevent STA from reconnecting using cached PMKSA to force + * full authentication with the authentication server (which may + * decide to reject the connection), + */ + wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + /* Calculate timeout in ms based on beacon_int in TU */ + timeout = disassoc_timer * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR + " set to %d ms", MAC2STR(sta->addr), timeout); + + sta->timeout_next = STA_DISASSOC_FROM_CLI; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(timeout / 1000, + timeout % 1000 * 1000, + ap_handle_timer, hapd, sta); +} + + int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, const char *url, int disassoc_timer) @@ -477,30 +515,78 @@ int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, return -1; } - /* send disassociation frame after time-out */ if (disassoc_timer) { - int timeout, beacon_int; + /* send disassociation frame after time-out */ + set_disassoc_timer(hapd, sta, disassoc_timer); + } - /* - * Prevent STA from reconnecting using cached PMKSA to force - * full authentication with the authentication server (which may - * decide to reject the connection), - */ - wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); - - beacon_int = hapd->iconf->beacon_int; - if (beacon_int < 1) - beacon_int = 100; /* best guess */ - /* Calculate timeout in ms based on beacon_int in TU */ - timeout = disassoc_timer * beacon_int * 128 / 125; - wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR - " set to %d ms", MAC2STR(sta->addr), timeout); - - sta->timeout_next = STA_DISASSOC_FROM_CLI; - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(timeout / 1000, - timeout % 1000 * 1000, - ap_handle_timer, hapd, sta); + return 0; +} + + +int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, + const u8 *nei_rep, size_t nei_rep_len) +{ + u8 *buf, *pos; + struct ieee80211_mgmt *mgmt; + size_t url_len; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " + MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x", + MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int); + buf = os_zalloc(1000 + nei_rep_len); + if (buf == NULL) + return -1; + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ; + mgmt->u.action.u.bss_tm_req.dialog_token = 1; + mgmt->u.action.u.bss_tm_req.req_mode = req_mode; + mgmt->u.action.u.bss_tm_req.disassoc_timer = + host_to_le16(disassoc_timer); + mgmt->u.action.u.bss_tm_req.validity_interval = valid_int; + + pos = mgmt->u.action.u.bss_tm_req.variable; + + if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) && + bss_term_dur) { + os_memcpy(pos, bss_term_dur, 12); + pos += 12; + } + + if (url) { + /* Session Information URL */ + url_len = os_strlen(url); + if (url_len > 255) + return -1; + *pos++ = url_len; + os_memcpy(pos, url, url_len); + pos += url_len; + } + + if (nei_rep) { + os_memcpy(pos, nei_rep, nei_rep_len); + pos += nei_rep_len; + } + + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "Failed to send BSS Transition Management Request frame"); + os_free(buf); + return -1; + } + os_free(buf); + + if (disassoc_timer) { + /* send disassociation frame after time-out */ + set_disassoc_timer(hapd, sta, disassoc_timer); } return 0; diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index eeaf5eca..77893072 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -1,6 +1,6 @@ /* * IEEE 802.11v WNM related functions and structures - * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,5 +18,9 @@ int wnm_send_disassoc_imminent(struct hostapd_data *hapd, int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd, struct sta_info *sta, const char *url, int disassoc_timer); +int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, + u8 req_mode, int disassoc_timer, u8 valid_int, + const u8 *bss_term_dur, const char *url, + const u8 *nei_rep, size_t nei_rep_len); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 1a16b5c8..da2073c0 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -33,7 +33,8 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); @@ -42,6 +43,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, + const u8 *pmk, struct wpa_ptk *ptk); static const u32 dot11RSNAConfigGroupUpdateCount = 4; static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; @@ -135,6 +138,17 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, } +#ifdef CONFIG_MESH +static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.start_ampe == NULL) + return -1; + return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); +} +#endif /* CONFIG_MESH */ + + int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -782,6 +796,51 @@ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, } +static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, + size_t data_len) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + for (;;) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, + sm->p2p_dev_addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK); + + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) + == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce did not result in matching MIC"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "WPA: Earlier SNonce resulted in matching MIC"); + sm->alt_snonce_valid = 0; + os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN); + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; + + return 0; +} + + void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, u8 *data, size_t data_len) @@ -884,6 +943,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, sm->pairwise == WPA_CIPHER_GCMP) { if (wpa_use_aes_cmac(sm) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -902,6 +962,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } } + + if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); + return; + } } if (key_info & WPA_KEY_INFO_REQUEST) { @@ -937,8 +1004,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, "based on retransmitted EAPOL-Key " "1/4"); sm->update_snonce = 1; - wpa_replay_counter_mark_invalid(sm->prev_key_replay, - key->replay_counter); + os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN); + sm->alt_snonce_valid = TRUE; + os_memcpy(sm->alt_replay_counter, + sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + goto continue_processing; + } + + if (msg == PAIRWISE_4 && sm->alt_snonce_valid && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING && + os_memcmp(key->replay_counter, sm->alt_replay_counter, + WPA_REPLAY_COUNTER_LEN) == 0) { + /* + * Supplicant may still be using the old SNonce since + * there was two EAPOL-Key 2/4 messages and they had + * different SNonce values. + */ + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4"); goto continue_processing; } @@ -1123,7 +1207,10 @@ continue_processing: sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, + data_len) && + (msg != PAIRWISE_4 || !sm->alt_snonce_valid || + wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; @@ -1152,7 +1239,8 @@ continue_processing: */ if (msg == SMK_ERROR) { #ifdef CONFIG_PEERKEY - wpa_smk_error(wpa_auth, sm, key); + wpa_smk_error(wpa_auth, sm, (const u8 *) (key + 1), + key_data_length); #endif /* CONFIG_PEERKEY */ return; } else if (key_info & WPA_KEY_INFO_ERROR) { @@ -1167,7 +1255,8 @@ continue_processing: wpa_request_new_ptk(sm); #ifdef CONFIG_PEERKEY } else if (msg == SMK_M1) { - wpa_smk_m1(wpa_auth, sm, key); + wpa_smk_m1(wpa_auth, sm, key, (const u8 *) (key + 1), + key_data_length); #endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies((const u8 *) (key + 1), @@ -1209,7 +1298,8 @@ continue_processing: #ifdef CONFIG_PEERKEY if (msg == SMK_M3) { - wpa_smk_m3(wpa_auth, sm, key); + wpa_smk_m3(wpa_auth, sm, key, (const u8 *) (key + 1), + key_data_length); return; } #endif /* CONFIG_PEERKEY */ @@ -1295,7 +1385,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (force_version) version = force_version; - else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_use_aes_cmac(sm)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; @@ -1320,6 +1411,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1361,6 +1453,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); os_memcpy(key->replay_counter, sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); sm->key_replay[0].valid = TRUE; if (nonce) @@ -1389,6 +1483,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { if (aes_wrap(sm->PTK.kek, 16, (key_data_len - 8) / 8, buf, @@ -1420,8 +1515,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, os_free(hdr); return; } - wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, - key->key_mic); + wpa_eapol_key_mic(sm->PTK.kck, sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key->key_mic); #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && @@ -1473,7 +1568,8 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, } -static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, + size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; @@ -1489,7 +1585,7 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) key_info = WPA_GET_BE16(key->key_info); os_memcpy(mic, key->key_mic, 16); os_memset(key->key_mic, 0, 16); - if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + if (wpa_eapol_key_mic(PTK->kck, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, data, data_len, key->key_mic) || os_memcmp_const(mic, key->key_mic, 16) != 0) ret = -1; @@ -1520,6 +1616,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) switch (event) { case WPA_AUTH: +#ifdef CONFIG_MESH + /* PTKs are derived through AMPE */ + if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) { + /* not mesh */ + break; + } + return 0; +#endif /* CONFIG_MESH */ case WPA_ASSOC: break; case WPA_DEAUTH: @@ -1773,6 +1877,7 @@ SM_STATE(WPA_PTK, PTKSTART) SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; + sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { @@ -1795,10 +1900,13 @@ SM_STATE(WPA_PTK, PTKSTART) pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); - if (sm->pmksa) + if (sm->pmksa) { os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); - else { + } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { + /* No KCK available to derive PMKID */ + pmkid = NULL; + } else { /* * Calculate PMKID since no PMKSA cache entry was * available with pre-calculated PMKID. @@ -1814,8 +1922,8 @@ SM_STATE(WPA_PTK, PTKSTART) } -static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, - struct wpa_ptk *ptk) +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, + const u8 *pmk, struct wpa_ptk *ptk) { size_t ptk_len = wpa_cipher_key_len(sm->pairwise) + 32; #ifdef CONFIG_IEEE80211R @@ -1824,7 +1932,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, #endif /* CONFIG_IEEE80211R */ wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", - sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, (u8 *) ptk, ptk_len, wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); @@ -1854,9 +1962,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) } else pmk = sm->PMK; - wpa_derive_ptk(sm, pmk, &PTK); + wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK); - if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; @@ -2009,8 +2118,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) if (sm->wpa == WPA_VERSION_WPA && (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { - /* WPA-only STA, remove RSN IE */ + /* WPA-only STA, remove RSN IE and possible MDIE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) + wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, @@ -2331,7 +2442,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; struct wpa_group *gsm = sm->group; - u8 *kde, *pos, hdr[2]; + const u8 *kde; + u8 *kde_buf = NULL, *pos, hdr[2]; size_t kde_len; u8 *gtk, dummy_gtk[32]; @@ -2367,28 +2479,29 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) if (sm->wpa == WPA_VERSION_WPA2) { kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + ieee80211w_kde_len(sm); - kde = os_malloc(kde_len); - if (kde == NULL) + kde_buf = os_malloc(kde_len); + if (kde_buf == NULL) return; - pos = kde; + kde = pos = kde_buf; hdr[0] = gsm->GN & 0x03; hdr[1] = 0; pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, gtk, gsm->GTK_len); pos = ieee80211w_kde_add(sm, pos); + kde_len = pos - kde; } else { kde = gtk; - pos = kde + gsm->GTK_len; + kde_len = gsm->GTK_len; } wpa_send_eapol(sm->wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); - if (sm->wpa == WPA_VERSION_WPA2) - os_free(kde); + rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); } @@ -2859,7 +2972,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) wpa_bool_txt(preauth), wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), wpa_bool_txt(wpa_auth->conf.rsn_preauth)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2909,7 +3022,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, wpa_auth->dot11RSNA4WayHandshakeFailures); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2919,7 +3032,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) /* Private MIB */ ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", wpa_auth->group->wpa_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2961,7 +3074,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) RSN_SUITE_ARG(pairwise), sm->dot11RSNAStatsTKIPLocalMICFailures, sm->dot11RSNAStatsTKIPRemoteMICFailures); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -2971,7 +3084,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) "hostapdWPAPTKGroupState=%d\n", sm->wpa_ptk_state, sm->wpa_ptk_group_state); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -3055,6 +3168,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, return -1; if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->PTK.kck, sizeof(sm->PTK.kck), sm->wpa_auth->addr, sm->addr, session_timeout, eapol, sm->wpa_key_mgmt)) return 0; @@ -3071,7 +3185,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL) return -1; - if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, + NULL, 0, + wpa_auth->addr, sta_addr, session_timeout, eapol, WPA_KEY_MGMT_IEEE8021X)) return 0; @@ -3080,6 +3196,22 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, } +int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk) +{ + if (wpa_auth->conf.disable_pmksa_caching) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, + NULL, 0, + wpa_auth->addr, addr, 0, NULL, + WPA_KEY_MGMT_SAE)) + return 0; + + return -1; +} + + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 929a2535..757e49e4 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -213,6 +213,9 @@ struct wpa_auth_callbacks { int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_MESH + int (*start_ampe)(void *ctx, const u8 *sta_addr); +#endif /* CONFIG_MESH */ }; struct wpa_authenticator * wpa_init(const u8 *addr, @@ -276,6 +279,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, const u8 *pmk, size_t len, const u8 *sta_addr, int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index 781f15fb..e061b5e1 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -10,6 +10,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "crypto/aes_wrap.h" @@ -1310,7 +1311,9 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_r0kh_r1kh_pull_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r1kh *r1kh; struct ft_r0kh_r1kh_resp_frame resp, r; u8 pmk_r0[PMK_LEN]; @@ -1318,7 +1321,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r1kh = wpa_auth->conf.r1kh_list; @@ -1334,12 +1337,14 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_pull_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r1kh->key, sizeof(r1kh->key), (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - frame->nonce, f.nonce) < 0) { + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "request from " MACSTR, MAC2STR(src_addr)); return -1; @@ -1442,13 +1447,15 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_r0kh_r1kh_resp_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r0kh *r0kh; int pairwise, res; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; @@ -1464,12 +1471,14 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_resp_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, sizeof(r0kh->key), (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - frame->nonce, f.nonce) < 0) { + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " "response from " MACSTR, MAC2STR(src_addr)); return -1; @@ -1507,7 +1516,9 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len) { - struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_r0kh_r1kh_push_frame f; + const u8 *crypt; + u8 *plain; struct ft_remote_r0kh *r0kh; struct os_time now; os_time_t tsend; @@ -1515,7 +1526,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(*frame)) + if (data_len < sizeof(f)) return -1; r0kh = wpa_auth->conf.r0kh_list; @@ -1531,12 +1542,15 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, return -1; } - frame = (struct ft_r0kh_r1kh_push_frame *) data; + crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); + os_memset(&f, 0, sizeof(f)); + plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); /* aes_unwrap() does not support inplace decryption, so use a temporary * buffer for the data. */ if (aes_unwrap(r0kh->key, sizeof(r0kh->key), (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - frame->timestamp, f.timestamp) < 0) { + crypt, plain) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " MACSTR, MAC2STR(src_addr)); return -1; @@ -1710,6 +1724,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, { struct ft_r0kh_r1kh_push_frame frame, f; struct os_time now; + const u8 *plain; + u8 *crypt; os_memset(&frame, 0, sizeof(frame)); frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; @@ -1732,9 +1748,13 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, WPA_PUT_LE32(f.timestamp, now.sec); f.pairwise = host_to_le16(pairwise); os_memset(f.pad, 0, sizeof(f.pad)); + plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); + crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, + timestamp); if (aes_wrap(r1kh->key, sizeof(r1kh->key), (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - f.timestamp, frame.timestamp) < 0) + plain, crypt) < 0) return; wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 6ee9a4f8..8592b90b 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -299,6 +299,21 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, struct sta_info *sta; u32 flags = 0; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io) { + size_t hex_len = 2 * data_len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, data, data_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(addr), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + sta = ap_get_sta(hapd, addr); if (sta) flags = hostapd_sta_flags_to_drv(sta->flags); @@ -404,6 +419,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, struct l2_ethhdr *buf; int ret; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) { + size_t hex_len = 2 * data_len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, data, data_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(dst), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + #ifdef CONFIG_IEEE80211R if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 6960ff36..478bc955 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -58,6 +58,8 @@ struct wpa_state_machine { Boolean GUpdateStationKeys; u8 ANonce[WPA_NONCE_LEN]; u8 SNonce[WPA_NONCE_LEN]; + u8 alt_SNonce[WPA_NONCE_LEN]; + u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN]; struct wpa_ptk PTK; Boolean PTK_valid; @@ -84,6 +86,7 @@ struct wpa_state_machine { unsigned int mgmt_frame_prot:1; unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; + unsigned int alt_snonce_valid:1; #ifdef CONFIG_IEEE80211R unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; @@ -227,11 +230,14 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg); void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, + const u8 *key_data, size_t key_data_len); void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len); void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key); + struct wpa_state_machine *sm, struct wpa_eapol_key *key, + const u8 *key_data, size_t key_data_len); #endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 1e4defcf..c926765d 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -200,6 +200,11 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, num_suites++; } #endif /* CONFIG_SAE */ + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); + pos += RSN_SELECTOR_LEN; + num_suites++; + } #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -477,6 +482,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (0) { } + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; #ifdef CONFIG_IEEE80211R else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; @@ -555,6 +562,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } if (0) { } + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; #ifdef CONFIG_IEEE80211R else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 6f16f50e..9ba7aba8 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -185,7 +185,7 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); - if (len > 0 && len < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), len)) wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); if (hapd->conf->wps_pin_requests) { @@ -1049,7 +1049,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & WPA_CIPHER_CCMP) + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) wps->encr_types |= WPS_ENCR_AES; if (conf->rsn_pairwise & WPA_CIPHER_TKIP) wps->encr_types |= WPS_ENCR_TKIP; @@ -1583,7 +1583,7 @@ int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, int ret; ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin); - if (ret < 0 || ret >= (int) sizeof(data.pin_txt)) + if (os_snprintf_error(sizeof(data.pin_txt), ret)) return -1; data.timeout = timeout; return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data); diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c new file mode 100644 index 00000000..8f77015e --- /dev/null +++ b/src/ap/x_snoop.c @@ -0,0 +1,123 @@ +/* + * Generic Snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "x_snoop.h" + + +int x_snoop_init(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + + if (!conf->isolate) { + wpa_printf(MSG_DEBUG, + "x_snoop: ap_isolate must be enabled for x_snoop"); + return -1; + } + + if (conf->bridge[0] == '\0') { + wpa_printf(MSG_DEBUG, + "x_snoop: Bridge must be configured for x_snoop"); + return -1; + } + + if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, + 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable hairpin_mode on the bridge port"); + return -1; + } + + if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable proxyarp on the bridge port"); + return -1; + } + + if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, + 1)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to enable accepting gratuitous ARP on the bridge"); + return -1; + } + + return 0; +} + + +struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type) +{ + struct hostapd_bss_config *conf = hapd->conf; + struct l2_packet_data *l2; + + l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1); + if (l2 == NULL) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to initialize L2 packet processing %s", + strerror(errno)); + return NULL; + } + + if (l2_packet_set_packet_filter(l2, type)) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to set L2 packet filter for type: %d", + type); + l2_packet_deinit(l2); + return NULL; + } + + return l2; +} + + +void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, u8 *buf, + size_t len) +{ + int res; + u8 addr[ETH_ALEN]; + u8 *dst_addr = buf; + + if (!(dst_addr[0] & 0x01)) + return; + + wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion " + MACSTR " -> " MACSTR " (len %u)", + MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len); + + /* save the multicast destination address for restoring it later */ + os_memcpy(addr, buf, ETH_ALEN); + + os_memcpy(buf, sta->addr, ETH_ALEN); + res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "x_snoop: Failed to send mcast to ucast converted packet to " + MACSTR, MAC2STR(sta->addr)); + } + + /* restore the multicast destination address */ + os_memcpy(buf, addr, ETH_ALEN); +} + + +void x_snoop_deinit(struct hostapd_data *hapd) +{ + hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0); + hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0); + hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0); +} diff --git a/src/ap/x_snoop.h b/src/ap/x_snoop.h new file mode 100644 index 00000000..e43a78d0 --- /dev/null +++ b/src/ap/x_snoop.h @@ -0,0 +1,56 @@ +/* + * Generic Snooping for Proxy ARP + * Copyright (c) 2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef X_SNOOP_H +#define X_SNOOP_H + +#include "l2_packet/l2_packet.h" + +#ifdef CONFIG_PROXYARP + +int x_snoop_init(struct hostapd_data *hapd); +struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type); +void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, u8 *buf, + size_t len); +void x_snoop_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_PROXYARP */ + +static inline int x_snoop_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline struct l2_packet_data * +x_snoop_get_l2_packet(struct hostapd_data *hapd, + void (*handler)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + enum l2_packet_filter_type type) +{ + return NULL; +} + +static inline void +x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd, + struct sta_info *sta, void *buf, + size_t len) +{ +} + +static inline void x_snoop_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_PROXYARP */ + +#endif /* X_SNOOP_H */ diff --git a/src/common/defs.h b/src/common/defs.h index d4091e31..e1bbd509 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -49,6 +49,7 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_WAPI_CERT BIT(13) #define WPA_KEY_MGMT_CCKM BIT(14) #define WPA_KEY_MGMT_OSEN BIT(15) +#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { @@ -56,7 +57,8 @@ static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_OSEN | - WPA_KEY_MGMT_IEEE8021X_SHA256)); + WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SUITE_B)); } static inline int wpa_key_mgmt_wpa_psk(int akm) @@ -85,7 +87,13 @@ static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_OSEN)); + WPA_KEY_MGMT_OSEN | + WPA_KEY_MGMT_IEEE8021X_SUITE_B)); +} + +static inline int wpa_key_mgmt_suite_b(int akm) +{ + return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B); } static inline int wpa_key_mgmt_wpa(int akm) @@ -300,4 +308,25 @@ enum wpa_ctrl_req_type { /* Maximum number of EAP methods to store for EAP server user information */ #define EAP_MAX_METHODS 8 +/** + * enum ht_mode - channel width and offset + */ +enum ht_mode { + CHAN_UNDEFINED = 0, + CHAN_NO_HT, + CHAN_HT20, + CHAN_HT40PLUS, + CHAN_HT40MINUS, +}; + +enum mesh_plink_state { + PLINK_LISTEN = 1, + PLINK_OPEN_SENT, + PLINK_OPEN_RCVD, + PLINK_CNF_RCVD, + PLINK_ESTAB, + PLINK_HOLDING, + PLINK_BLOCKED, +}; + #endif /* DEFS_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index 173a400d..e1d45cf9 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -249,6 +249,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ht_operation = pos; elems->ht_operation_len = elen; break; + case WLAN_EID_MESH_CONFIG: + elems->mesh_config = pos; + elems->mesh_config_len = elen; + break; + case WLAN_EID_MESH_ID: + elems->mesh_id = pos; + elems->mesh_id_len = elen; + break; + case WLAN_EID_PEER_MGMT: + elems->peer_mgmt = pos; + elems->peer_mgmt_len = elen; + break; case WLAN_EID_VHT_CAP: elems->vht_capabilities = pos; elems->vht_capabilities_len = elen; @@ -290,6 +302,16 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->ssid_list = pos; elems->ssid_list_len = elen; break; + case WLAN_EID_AMPE: + elems->ampe = pos; + elems->ampe_len = elen; + break; + case WLAN_EID_MIC: + elems->mic = pos; + elems->mic_len = elen; + /* after mic everything is encrypted, so stop. */ + left = elen; + break; default: unknown++; if (!show_errors) @@ -515,6 +537,286 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel) } +static const char *us_op_class_cc[] = { + "US", "CA", NULL +}; + +static const char *eu_op_class_cc[] = { + "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", + "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", + "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", + "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL +}; + +static const char *jp_op_class_cc[] = { + "JP", NULL +}; + +static const char *cn_op_class_cc[] = { + "CN", "CA", NULL +}; + + +static int country_match(const char *cc[], const char *country) +{ + int i; + + if (country == NULL) + return 0; + for (i = 0; cc[i]; i++) { + if (cc[i][0] == country[0] && cc[i][1] == country[1]) + return 1; + } + + return 0; +} + + +static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan) +{ + switch (op_class) { + case 12: /* channels 1..11 */ + case 32: /* channels 1..7; 40 MHz */ + case 33: /* channels 5..11; 40 MHz */ + if (chan < 1 || chan > 11) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 22: /* channels 36,44; 40 MHz */ + case 23: /* channels 52,60; 40 MHz */ + case 27: /* channels 40,48; 40 MHz */ + case 28: /* channels 56,64; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 4: /* channels 100-144 */ + case 24: /* channels 100-140; 40 MHz */ + if (chan < 100 || chan > 144) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 149,153,157,161 */ + case 25: /* channels 149,157; 40 MHz */ + case 26: /* channels 149,157; 40 MHz */ + case 30: /* channels 153,161; 40 MHz */ + case 31: /* channels 153,161; 40 MHz */ + if (chan < 149 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 34: /* 60 GHz band, channels 1..3 */ + if (chan < 1 || chan > 3) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan) +{ + switch (op_class) { + case 4: /* channels 1..13 */ + case 11: /* channels 1..9; 40 MHz */ + case 12: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 5: /* channels 36,44; 40 MHz */ + case 6: /* channels 52,60; 40 MHz */ + case 8: /* channels 40,48; 40 MHz */ + case 9: /* channels 56,64; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 100-140 */ + case 7: /* channels 100-132; 40 MHz */ + case 10: /* channels 104-136; 40 MHz */ + case 16: /* channels 100-140 */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 17: /* channels 149,153,157,161,165,169 */ + if (chan < 149 || chan > 169) + return -1; + return 5000 + 5 * chan; + case 18: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 4) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan) +{ + switch (op_class) { + case 30: /* channels 1..13 */ + case 56: /* channels 1..9; 40 MHz */ + case 57: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 31: /* channel 14 */ + if (chan != 14) + return -1; + return 2414 + 5 * chan; + case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */ + case 32: /* channels 52,56,60,64 */ + case 33: /* channels 52,56,60,64 */ + case 36: /* channels 36,44; 40 MHz */ + case 37: /* channels 52,60; 40 MHz */ + case 38: /* channels 52,60; 40 MHz */ + case 41: /* channels 40,48; 40 MHz */ + case 42: /* channels 56,64; 40 MHz */ + case 43: /* channels 56,64; 40 MHz */ + if (chan < 34 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 34: /* channels 100-140 */ + case 35: /* channels 100-140 */ + case 39: /* channels 100-132; 40 MHz */ + case 40: /* channels 100-132; 40 MHz */ + case 44: /* channels 104-136; 40 MHz */ + case 45: /* channels 104-136; 40 MHz */ + case 58: /* channels 100-140 */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 59: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 3) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan) +{ + switch (op_class) { + case 7: /* channels 1..13 */ + case 8: /* channels 1..9; 40 MHz */ + case 9: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 1: /* channels 36,40,44,48 */ + case 2: /* channels 52,56,60,64; dfs */ + case 4: /* channels 36,44; 40 MHz */ + case 5: /* channels 52,60; 40 MHz */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 3: /* channels 149,153,157,161,165 */ + case 6: /* channels 149,157; 40 MHz */ + if (chan < 149 || chan > 165) + return -1; + return 5000 + 5 * chan; + } + return -1; +} + + +static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) +{ + /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ + switch (op_class) { + case 81: + /* channels 1..13 */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 82: + /* channel 14 */ + if (chan != 14) + return -1; + return 2414 + 5 * chan; + case 83: /* channels 1..9; 40 MHz */ + case 84: /* channels 5..13; 40 MHz */ + if (chan < 1 || chan > 13) + return -1; + return 2407 + 5 * chan; + case 115: /* channels 36,40,44,48; indoor only */ + case 116: /* channels 36,44; 40 MHz; indoor only */ + case 117: /* channels 40,48; 40 MHz; indoor only */ + case 118: /* channels 52,56,60,64; dfs */ + case 119: /* channels 52,60; 40 MHz; dfs */ + case 120: /* channels 56,64; 40 MHz; dfs */ + if (chan < 36 || chan > 64) + return -1; + return 5000 + 5 * chan; + case 121: /* channels 100-140 */ + case 122: /* channels 100-142; 40 MHz */ + case 123: /* channels 104-136; 40 MHz */ + if (chan < 100 || chan > 140) + return -1; + return 5000 + 5 * chan; + case 124: /* channels 149,153,157,161 */ + case 125: /* channels 149,153,157,161,165,169 */ + case 126: /* channels 149,157; 40 MHz */ + case 127: /* channels 153,161; 40 MHz */ + if (chan < 149 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ + if (chan < 36 || chan > 161) + return -1; + return 5000 + 5 * chan; + case 129: /* center freqs 50, 114; 160 MHz */ + if (chan < 50 || chan > 114) + return -1; + return 5000 + 5 * chan; + case 180: /* 60 GHz band, channels 1..4 */ + if (chan < 1 || chan > 4) + return -1; + return 56160 + 2160 * chan; + } + return -1; +} + +/** + * ieee80211_chan_to_freq - Convert channel info to frequency + * @country: Country code, if known; otherwise, global operating class is used + * @op_class: Operating class + * @chan: Channel number + * Returns: Frequency in MHz or -1 if the specified channel is unknown + */ +int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) +{ + int freq; + + if (country_match(us_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_us(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(eu_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_eu(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(jp_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_jp(op_class, chan); + if (freq > 0) + return freq; + } + + if (country_match(cn_op_class_cc, country)) { + freq = ieee80211_chan_to_freq_cn(op_class, chan); + if (freq > 0) + return freq; + } + + return ieee80211_chan_to_freq_global(op_class, chan); +} + + static int is_11b(u8 rate) { return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index cf83057b..2357afc5 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -28,6 +28,9 @@ struct ieee802_11_elems { const u8 *timeout_int; const u8 *ht_capabilities; const u8 *ht_operation; + const u8 *mesh_config; + const u8 *mesh_id; + const u8 *peer_mgmt; const u8 *vht_capabilities; const u8 *vht_operation; const u8 *vht_opmode_notif; @@ -42,6 +45,8 @@ struct ieee802_11_elems { const u8 *bss_max_idle_period; const u8 *ssid_list; const u8 *osen; + const u8 *ampe; + const u8 *mic; u8 ssid_len; u8 supp_rates_len; @@ -60,6 +65,9 @@ struct ieee802_11_elems { u8 timeout_int_len; u8 ht_capabilities_len; u8 ht_operation_len; + u8 mesh_config_len; + u8 mesh_id_len; + u8 peer_mgmt_len; u8 vht_capabilities_len; u8 vht_operation_len; u8 vendor_ht_cap_len; @@ -71,6 +79,8 @@ struct ieee802_11_elems { u8 ext_capab_len; u8 ssid_list_len; u8 osen_len; + u8 ampe_len; + u8 mic_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; @@ -95,6 +105,7 @@ struct hostapd_wmm_ac_params { int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[], const char *name, const char *val); enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel); +int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); int supp_rates_11b_only(struct ieee802_11_elems *elems); diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 6de71e9e..dfe0fafd 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -25,6 +25,8 @@ #define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2) #define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4) +#define WLAN_INVALID_MGMT_SEQ 0xFFFF + #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) #define WLAN_GET_SEQ_SEQ(seq) \ (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) @@ -194,6 +196,16 @@ #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 /* IEEE 802.11e */ #define WLAN_REASON_DISASSOC_LOW_ACK 34 +/* IEEE 802.11s */ +#define WLAN_REASON_MESH_PEERING_CANCELLED 52 +#define WLAN_REASON_MESH_MAX_PEERS 53 +#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54 +#define WLAN_REASON_MESH_CLOSE_RCVD 55 +#define WLAN_REASON_MESH_MAX_RETRIES 56 +#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57 +#define WLAN_REASON_MESH_INVALID_GTK 58 +#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59 +#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60 /* Information Element IDs */ @@ -234,6 +246,7 @@ #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 #define WLAN_EID_WAPI 68 #define WLAN_EID_TIME_ADVERTISEMENT 69 +#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 @@ -249,7 +262,12 @@ #define WLAN_EID_ADV_PROTO 108 #define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_MESH_CONFIG 113 +#define WLAN_EID_MESH_ID 114 +#define WLAN_EID_PEER_MGMT 117 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_AMPE 139 +#define WLAN_EID_MIC 140 #define WLAN_EID_CCKM 156 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 @@ -277,6 +295,7 @@ #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 +#define WLAN_ACTION_SELF_PROTECTED 15 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ #define WLAN_ACTION_VENDOR_SPECIFIC 127 @@ -321,6 +340,19 @@ #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9 #define WLAN_TDLS_DISCOVERY_REQUEST 10 +/* Radio Measurement Action codes */ +#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0 +#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1 +#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2 +#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3 +#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 +#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 + +/* Radio Measurement capabilities (from RRM Capabilities IE) */ +/* byte 1 (out of 5) */ +#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) +#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) + /* Timeout Interval Type */ #define WLAN_TIMEOUT_REASSOC_DEADLINE 1 #define WLAN_TIMEOUT_KEY_LIFETIME 2 @@ -577,6 +609,10 @@ struct ieee80211_mgmt { * Entries (optional) */ u8 variable[0]; } STRUCT_PACKED bss_tm_query; + struct { + u8 action; /* 15 */ + u8 variable[0]; + } STRUCT_PACKED slf_prot_action; } u; } STRUCT_PACKED action; } u; @@ -638,6 +674,15 @@ struct ieee80211_vht_operation { le16 vht_basic_mcs_set; } STRUCT_PACKED; +struct ieee80211_ampe_ie { + u8 selected_pairwise_suite[4]; + u8 local_nonce[32]; + u8 peer_nonce[32]; + u8 mgtk[16]; + u8 key_rsc[8]; + u8 key_expiration[4]; +} STRUCT_PACKED; + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -754,6 +799,7 @@ struct ieee80211_vht_operation { #define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0)) #define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1)) #define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1)) +#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT 0 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) @@ -767,13 +813,16 @@ struct ieee80211_vht_operation { #define VHT_CAP_RXSTBC_4 ((u32) BIT(10)) #define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \ BIT(10)) +#define VHT_CAP_RXSTBC_MASK_SHIFT 8 #define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11)) #define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12)) #define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \ BIT(14) | BIT(15)) +#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT 13 #define VHT_CAP_BEAMFORMEE_STS_OFFSET 13 #define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \ BIT(17) | BIT(18)) +#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT 16 #define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16 #define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19)) #define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20)) @@ -788,6 +837,7 @@ struct ieee80211_vht_operation { #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25)) #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((u32) BIT(23) | \ BIT(24) | BIT(25)) +#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27)) #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27)) #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) @@ -855,6 +905,8 @@ struct wmm_information_element { } STRUCT_PACKED; +#define WMM_QOSINFO_AP_UAPSD 0x80 + #define WMM_QOSINFO_STA_AC_MASK 0x0f #define WMM_QOSINFO_STA_SP_MASK 0x03 #define WMM_QOSINFO_STA_SP_SHIFT 5 @@ -922,11 +974,12 @@ struct wmm_tspec_element { /* Access Categories / ACI to AC coding */ -enum { +enum wmm_ac { WMM_AC_BE = 0 /* Best Effort */, WMM_AC_BK = 1 /* Background */, WMM_AC_VI = 2 /* Video */, - WMM_AC_VO = 3 /* Voice */ + WMM_AC_VO = 3 /* Voice */, + WMM_AC_NUM = 4 }; @@ -1087,6 +1140,19 @@ enum wifi_display_subelem { WFD_SUBELEM_SESSION_INFO = 9 }; +/* 802.11s */ +#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1 +#define MESH_SYNC_METHOD_VENDOR 255 +#define MESH_PATH_PROTOCOL_HWMP 1 +#define MESH_PATH_PROTOCOL_VENDOR 255 +#define MESH_PATH_METRIC_AIRTIME 1 +#define MESH_PATH_METRIC_VENDOR 255 + +enum plink_action_field { + PLINK_OPEN = 1, + PLINK_CONFIRM, + PLINK_CLOSE +}; #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ @@ -1122,6 +1188,7 @@ enum wifi_display_subelem { #define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 #define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 #define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 +#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11 #define WLAN_AKM_SUITE_CCKM 0x00409600 #define WLAN_AKM_SUITE_OSEN 0x506f9a01 @@ -1247,4 +1314,30 @@ enum wnm_sleep_mode_subelement_id { #define CHAN_SWITCH_MODE_ALLOW_TX 0 #define CHAN_SWITCH_MODE_BLOCK_TX 1 +struct tpc_report { + u8 eid; + u8 len; + u8 tx_power; + u8 link_margin; +} STRUCT_PACKED; + +/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */ +struct rrm_link_measurement_request { + u8 dialog_token; + s8 tx_power; + s8 max_tp; + u8 variable[0]; +} STRUCT_PACKED; + +/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */ +struct rrm_link_measurement_report { + u8 dialog_token; + struct tpc_report tpc; + u8 rx_ant_id; + u8 tx_ant_id; + u8 rcpi; + u8 rsni; + u8 variable[0]; +} STRUCT_PACKED; + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index 858b51d3..4dc34c4a 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -31,7 +31,9 @@ struct privsep_cmd_associate u8 bssid[ETH_ALEN]; u8 ssid[32]; size_t ssid_len; + int hwmode; int freq; + int channel; int pairwise_suite; int group_suite; int key_mgmt_suite; diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index ad3bdfd6..ec1be863 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -50,6 +50,25 @@ enum qca_radiotap_vendor_ids { * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass * NAN Request/Response and NAN Indication messages. These messages are * interpreted between the framework and the firmware component. + * + * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be + * used to configure PMK to the driver even when not connected. This can + * be used to request offloading of key management operations. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. + * + * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of + * NL80211_CMD_ROAM event with optional attributes including information + * from offloaded key management operation. Uses + * enum qca_wlan_vendor_attr_roam_auth attributes. Only used + * if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD. + * + * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to + * invoke the ACS function in device and pass selected channels to + * hostapd. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features + * supported by the driver. enum qca_wlan_vendor_features defines + * the possible features. */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -60,7 +79,42 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, - /* 14..49 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33, + QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34, + QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35, + QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36, + QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37, + QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38, + QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39, + QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41, + QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42, + /* 43..49 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50, + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51, + QCA_NL80211_VENDOR_SUBCMD_APFIND = 52, + /* 53 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55, }; @@ -78,6 +132,8 @@ enum qca_wlan_vendor_attr { * by enum qca_roaming_policy. */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, + /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ + QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7, /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, @@ -89,4 +145,53 @@ enum qca_roaming_policy { QCA_ROAMING_ALLOWED_WITHIN_ESS, }; +enum qca_wlan_vendor_attr_roam_auth { + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_attr_acs_offload { + QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL, + QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, + QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED, + QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ACS_MAX = + QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_acs_hw_mode { + QCA_ACS_MODE_IEEE80211B, + QCA_ACS_MODE_IEEE80211G, + QCA_ACS_MODE_IEEE80211A, + QCA_ACS_MODE_IEEE80211AD, +}; + +/** + * enum qca_wlan_vendor_features - Vendor device/driver feature flags + * + * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key + * management offload, a mechanism where the station's firmware + * does the exchange with the AP to establish the temporal keys + * after roaming, rather than having the user space wpa_supplicant do it. + * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits + */ +enum qca_wlan_vendor_features { + QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0, + NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index b67623f8..58889580 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -87,7 +87,8 @@ void sae_clear_temp_data(struct sae_data *sae) crypto_ec_point_deinit(tmp->pwe_ecc, 1); crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); - os_free(sae->tmp); + wpabuf_free(tmp->anti_clogging_token); + bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; } @@ -623,8 +624,10 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k) wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", val, sae->tmp->prime_len, keys, sizeof(keys)); + os_memset(keyseed, 0, sizeof(keyseed)); os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memset(keys, 0, sizeof(keys)); wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); @@ -656,8 +659,11 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, return; wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ - if (token) + if (token) { wpabuf_put_buf(buf, token); + wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", + wpabuf_head(token), wpabuf_len(token)); + } pos = wpabuf_put(buf, sae->tmp->prime_len); crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, sae->tmp->prime_len, sae->tmp->prime_len); @@ -682,8 +688,7 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, } -static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, - u16 group) +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) { if (allowed_groups) { int i; diff --git a/src/common/sae.h b/src/common/sae.h index d82a98e8..89d74ab1 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -35,6 +35,7 @@ struct sae_temporary_data { const struct crypto_bignum *order; struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; + struct wpabuf *anti_clogging_token; }; struct sae_data { @@ -60,5 +61,6 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups); void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); +u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); #endif /* SAE_H */ diff --git a/src/common/version.h b/src/common/version.h index 726289d9..c662270e 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -5,6 +5,6 @@ #define VERSION_STR_POSTFIX "" #endif /* VERSION_STR_POSTFIX */ -#define VERSION_STR "2.3" VERSION_STR_POSTFIX +#define VERSION_STR "2.4-devel" VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 998a51a8..a573e11e 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -14,7 +14,6 @@ #include "crypto/sha256.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" -#include "drivers/driver.h" #include "ieee802_11_defs.h" #include "defs.h" #include "wpa_common.h" @@ -23,6 +22,7 @@ /** * wpa_eapol_key_mic - Calculate EAPOL-Key MIC * @key: EAPOL-Key Key Confirmation Key (KCK) + * @akmp: WPA_KEY_MGMT_* used in key derivation * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*) * @buf: Pointer to the beginning of the EAPOL header (version field) * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame) @@ -38,10 +38,10 @@ * happened during final editing of the standard and the correct behavior is * defined in the last draft (IEEE 802.11i/D10). */ -int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, - u8 *mic) +int wpa_eapol_key_mic(const u8 *key, int akmp, int ver, const u8 *buf, + size_t len, u8 *mic) { - u8 hash[SHA1_MAC_LEN]; + u8 hash[SHA256_MAC_LEN]; switch (ver) { #ifndef CONFIG_FIPS @@ -57,11 +57,23 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, case WPA_KEY_INFO_TYPE_AES_128_CMAC: return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ -#ifdef CONFIG_HS20 case WPA_KEY_INFO_TYPE_AKM_DEFINED: - /* FIX: This should be based on negotiated AKM */ - return omac1_aes_128(key, buf, len, mic); + switch (akmp) { +#ifdef CONFIG_HS20 + case WPA_KEY_MGMT_OSEN: + return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_SUITEB + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + if (hmac_sha256(key, 16, buf, len, hash)) + return -1; + os_memcpy(mic, hash, MD5_MAC_LEN); + break; +#endif /* CONFIG_SUITEB */ + default: + return -1; + } + break; default: return -1; } @@ -399,6 +411,8 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE) return WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B) + return WPA_KEY_MGMT_IEEE8021X_SUITE_B; return 0; } @@ -496,7 +510,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) { + if (count == 0 || count > left / RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -4; @@ -524,7 +538,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * RSN_SELECTOR_LEN) { + if (count == 0 || count > left / RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -6; @@ -547,17 +561,17 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } if (left >= 2) { - data->num_pmkid = WPA_GET_LE16(pos); + u16 num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (left < (int) data->num_pmkid * PMKID_LEN) { + if (num_pmkid > (unsigned int) left / PMKID_LEN) { wpa_printf(MSG_DEBUG, "%s: PMKID underflow " - "(num_pmkid=%lu left=%d)", - __func__, (unsigned long) data->num_pmkid, - left); + "(num_pmkid=%u left=%d)", + __func__, num_pmkid, left); data->num_pmkid = 0; return -9; } else { + data->num_pmkid = num_pmkid; data->pmkid = pos; pos += data->num_pmkid * PMKID_LEN; left -= data->num_pmkid * PMKID_LEN; @@ -674,7 +688,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { + if (count == 0 || count > left / WPA_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -4; @@ -695,7 +709,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, count = WPA_GET_LE16(pos); pos += 2; left -= 2; - if (count == 0 || left < count * WPA_SELECTOR_LEN) { + if (count == 0 || count > left / WPA_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -6; @@ -928,6 +942,39 @@ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, } +#ifdef CONFIG_SUITEB +/** + * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM + * @kck: Key confirmation key + * @kck_len: Length of kck in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * Returns: 0 on success, -1 on failure + * + * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy + * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA)) + */ +int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + + if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0) + return -1; + os_memcpy(pmkid, hash, PMKID_LEN); + return 0; +} +#endif /* CONFIG_SUITEB */ + + /** * wpa_cipher_txt - Convert cipher suite to a text string * @cipher: Cipher suite (WPA_CIPHER_* enum) @@ -997,6 +1044,16 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) case WPA_KEY_MGMT_PSK_SHA256: return "WPA2-PSK-SHA256"; #endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_WPS: + return "WPS"; + case WPA_KEY_MGMT_SAE: + return "SAE"; + case WPA_KEY_MGMT_FT_SAE: + return "FT-SAE"; + case WPA_KEY_MGMT_OSEN: + return "OSEN"; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + return "WPA2-EAP-SUITE-B"; default: return "UNKNOWN"; } @@ -1023,6 +1080,8 @@ u32 wpa_akm_to_suite(int akm) return WLAN_AKM_SUITE_CCKM; if (akm & WPA_KEY_MGMT_OSEN) return WLAN_AKM_SUITE_OSEN; + if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + return WLAN_AKM_SUITE_8021X_SUITE_B; return 0; } @@ -1417,56 +1476,56 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) if (ciphers & WPA_CIPHER_CCMP_256) { ret = os_snprintf(pos, end - pos, "%sCCMP-256", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_GCMP_256) { ret = os_snprintf(pos, end - pos, "%sGCMP-256", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_CCMP) { ret = os_snprintf(pos, end - pos, "%sCCMP", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_GCMP) { ret = os_snprintf(pos, end - pos, "%sGCMP", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_TKIP) { ret = os_snprintf(pos, end - pos, "%sTKIP", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_WEP104) { ret = os_snprintf(pos, end - pos, "%sWEP104", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_WEP40) { ret = os_snprintf(pos, end - pos, "%sWEP40", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -1497,78 +1556,3 @@ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) return WPA_CIPHER_CCMP_256; return WPA_CIPHER_CCMP; } - - -static int wpa_check_wowlan_trigger(const char *start, const char *trigger, - int capa_trigger, u8 *param_trigger) -{ - if (os_strcmp(start, trigger) != 0) - return 0; - if (!capa_trigger) - return 0; - - *param_trigger = 1; - return 1; -} - - -struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers, - struct wpa_driver_capa *capa) -{ - struct wowlan_triggers *triggers; - char *start, *end, *buf; - int last; - - if (!wowlan_triggers) - return NULL; - - buf = os_strdup(wowlan_triggers); - if (buf == NULL) - return NULL; - - triggers = os_zalloc(sizeof(*triggers)); - if (triggers == NULL) - goto out; - -#define CHECK_TRIGGER(trigger) \ - wpa_check_wowlan_trigger(start, #trigger, \ - capa->wowlan_triggers.trigger, \ - &triggers->trigger) - - start = buf; - while (*start != '\0') { - while (isblank(*start)) - start++; - if (*start == '\0') - break; - end = start; - while (!isblank(*end) && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - - if (!CHECK_TRIGGER(any) && - !CHECK_TRIGGER(disconnect) && - !CHECK_TRIGGER(magic_pkt) && - !CHECK_TRIGGER(gtk_rekey_failure) && - !CHECK_TRIGGER(eap_identity_req) && - !CHECK_TRIGGER(four_way_handshake) && - !CHECK_TRIGGER(rfkill_release)) { - wpa_printf(MSG_DEBUG, - "Unknown/unsupported wowlan trigger '%s'", - start); - os_free(triggers); - triggers = NULL; - goto out; - } - - if (last) - break; - start = end + 1; - } -#undef CHECK_TRIGGER - -out: - os_free(buf); - return triggers; -} diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 0ef5a9d1..17bed34a 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -327,8 +327,8 @@ struct rsn_rdie { #endif /* _MSC_VER */ -int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, - u8 *mic); +int wpa_eapol_key_mic(const u8 *key, int akmp, int ver, const u8 *buf, + size_t len, u8 *mic); void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, @@ -374,6 +374,16 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, u8 *pmkid, int use_sha256); +#ifdef CONFIG_SUITEB +int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid); +#else /* CONFIG_SUITEB */ +static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, u8 *pmkid) +{ + return -1; +} +#endif /* CONFIG_SUITEB */ const char * wpa_cipher_txt(int cipher); const char * wpa_key_mgmt_txt(int key_mgmt, int proto); diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c index 5820a136..ccaaf1b0 100644 --- a/src/common/wpa_ctrl.c +++ b/src/common/wpa_ctrl.c @@ -94,10 +94,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) if (ctrl_path == NULL) return NULL; - ctrl = os_malloc(sizeof(*ctrl)); + ctrl = os_zalloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; - os_memset(ctrl, 0, sizeof(*ctrl)); ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); if (ctrl->s < 0) { @@ -112,7 +111,7 @@ try_again: CONFIG_CTRL_IFACE_CLIENT_DIR "/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", (int) getpid(), counter); - if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) { + if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { close(ctrl->s); os_free(ctrl); return NULL; @@ -283,10 +282,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) struct hostent *h; #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ - ctrl = os_malloc(sizeof(*ctrl)); + ctrl = os_zalloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; - os_memset(ctrl, 0, sizeof(*ctrl)); #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0); @@ -643,7 +641,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", ctrl_path); #endif /* UNICODE */ - if (ret < 0 || ret >= 256) { + if (os_snprintf_error(256, ret)) { os_free(ctrl); return NULL; } diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 4812f8df..1f747eb1 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -58,6 +58,8 @@ extern "C" { #define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** Scan command failed */ +#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED " /** wpa_supplicant state change */ #define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " /** A new BSS entry was added (followed by BSS entry id and BSSID) */ @@ -118,6 +120,17 @@ extern "C" { #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " +/* MESH events */ +#define MESH_GROUP_STARTED "MESH-GROUP-STARTED " +#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " +#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED " +#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED " + +/* WMM AC events */ +#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED " +#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED " +#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED " + /** P2P device found */ #define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " @@ -187,6 +200,9 @@ extern "C" { #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " +#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED " +#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED " + /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " @@ -204,6 +220,9 @@ extern "C" { #define AP_EVENT_ENABLED "AP-ENABLED " #define AP_EVENT_DISABLED "AP-DISABLED " +#define INTERFACE_ENABLED "INTERFACE-ENABLED " +#define INTERFACE_DISABLED "INTERFACE-DISABLED " + #define ACS_EVENT_STARTED "ACS-STARTED " #define ACS_EVENT_COMPLETED "ACS-COMPLETED " #define ACS_EVENT_FAILED "ACS-FAILED " @@ -216,6 +235,9 @@ extern "C" { #define AP_CSA_FINISHED "AP-CSA-FINISHED " +/* BSS Transition Management Response frame received */ +#define BSS_TM_RESP "BSS-TM-RESP " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF @@ -237,6 +259,7 @@ extern "C" { #define WPA_BSS_MASK_INTERNETW BIT(15) #define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) #define WPA_BSS_MASK_DELIM BIT(17) +#define WPA_BSS_MASK_MESH_SCAN BIT(18) /* VENDOR_ELEM_* frame id values */ @@ -383,8 +406,6 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl); */ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); -char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); - #ifdef ANDROID /** * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that @@ -402,6 +423,8 @@ void wpa_ctrl_cleanup(void); #define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ #define WPA_GLOBAL_CTRL_IFACE_PORT 9878 #define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ + +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); #endif /* CONFIG_CTRL_IFACE_UDP */ diff --git a/src/crypto/Makefile b/src/crypto/Makefile index 2a921098..3e90350c 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -26,6 +26,7 @@ LIB_OBJS= \ aes-internal-dec.o \ aes-internal-enc.o \ aes-omac1.o \ + aes-siv.o \ aes-unwrap.o \ aes-wrap.o \ des-internal.o \ diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c index 27895eb0..c2b06867 100644 --- a/src/crypto/aes-omac1.c +++ b/src/crypto/aes-omac1.c @@ -65,6 +65,13 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, for (i = 0; i < AES_BLOCK_SIZE; i++) { cbc[i] ^= *pos++; if (pos >= end) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == AES_BLOCK_SIZE && + left == AES_BLOCK_SIZE) + break; e++; pos = addr[e]; end = pos + len[e]; @@ -83,6 +90,12 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem, for (i = 0; i < left; i++) { cbc[i] ^= *pos++; if (pos >= end) { + /* + * Stop if there are no more bytes to process + * since there are no more entries in the array. + */ + if (i + 1 == left) + break; e++; pos = addr[e]; end = pos + len[e]; diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c new file mode 100644 index 00000000..ff4b823f --- /dev/null +++ b/src/crypto/aes-siv.c @@ -0,0 +1,187 @@ +/* + * AES SIV (RFC 5297) + * Copyright (c) 2013 Cozybit, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + + +static const u8 zero[AES_BLOCK_SIZE]; + + +static void dbl(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[AES_BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[AES_BLOCK_SIZE - 1] ^= 0x87; +} + + +static void xor(u8 *a, const u8 *b) +{ + int i; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + *a++ ^= *b++; +} + + +static void xorend(u8 *a, int alen, const u8 *b, int blen) +{ + int i; + + if (alen < blen) + return; + + for (i = 0; i < blen; i++) + a[alen - blen + i] ^= b[i]; +} + + +static void pad_block(u8 *pad, const u8 *addr, size_t len) +{ + os_memset(pad, 0, AES_BLOCK_SIZE); + os_memcpy(pad, addr, len); + + if (len < AES_BLOCK_SIZE) + pad[len] = 0x80; +} + + +int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], + size_t *len, u8 *mac) +{ + u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; + u8 *buf = NULL; + int ret; + size_t i; + + if (!num_elem) { + os_memcpy(tmp, zero, sizeof(zero)); + tmp[AES_BLOCK_SIZE - 1] = 1; + return omac1_aes_128(key, tmp, sizeof(tmp), mac); + } + + ret = omac1_aes_128(key, zero, sizeof(zero), tmp); + if (ret) + return ret; + + for (i = 0; i < num_elem - 1; i++) { + ret = omac1_aes_128(key, addr[i], len[i], tmp2); + if (ret) + return ret; + + dbl(tmp); + xor(tmp, tmp2); + } + if (len[i] >= AES_BLOCK_SIZE) { + buf = os_malloc(len[i]); + if (!buf) + return -ENOMEM; + + os_memcpy(buf, addr[i], len[i]); + xorend(buf, len[i], tmp, AES_BLOCK_SIZE); + ret = omac1_aes_128(key, buf, len[i], mac); + os_free(buf); + return ret; + } + + dbl(tmp); + pad_block(tmp2, addr[i], len[i]); + xor(tmp, tmp2); + + return omac1_aes_128(key, tmp, sizeof(tmp), mac); +} + + +int aes_siv_encrypt(const u8 *key, const u8 *pw, + size_t pwlen, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *out) +{ + const u8 *_addr[6]; + size_t _len[6]; + const u8 *k1 = key, *k2 = key + 16; + u8 v[AES_BLOCK_SIZE]; + size_t i; + u8 *iv, *crypt_pw; + + if (num_elem > ARRAY_SIZE(_addr) - 1) + return -1; + + for (i = 0; i < num_elem; i++) { + _addr[i] = addr[i]; + _len[i] = len[i]; + } + _addr[num_elem] = pw; + _len[num_elem] = pwlen; + + if (aes_s2v(k1, num_elem + 1, _addr, _len, v)) + return -1; + + iv = out; + crypt_pw = out + AES_BLOCK_SIZE; + + os_memcpy(iv, v, AES_BLOCK_SIZE); + os_memcpy(crypt_pw, pw, pwlen); + + /* zero out 63rd and 31st bits of ctr (from right) */ + v[8] &= 0x7f; + v[12] &= 0x7f; + return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen); +} + + +int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out) +{ + const u8 *_addr[6]; + size_t _len[6]; + const u8 *k1 = key, *k2 = key + 16; + size_t crypt_len; + size_t i; + int ret; + u8 iv[AES_BLOCK_SIZE]; + u8 check[AES_BLOCK_SIZE]; + + if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1) + return -1; + crypt_len = iv_c_len - AES_BLOCK_SIZE; + + for (i = 0; i < num_elem; i++) { + _addr[i] = addr[i]; + _len[i] = len[i]; + } + _addr[num_elem] = out; + _len[num_elem] = crypt_len; + + os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE); + os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len); + + iv[8] &= 0x7f; + iv[12] &= 0x7f; + + ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len); + if (ret) + return ret; + + ret = aes_s2v(k1, num_elem + 1, _addr, _len, check); + if (ret) + return ret; + if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0) + return 0; + + return -1; +} diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h new file mode 100644 index 00000000..463cf653 --- /dev/null +++ b/src/crypto/aes_siv.h @@ -0,0 +1,19 @@ +/* + * AES SIV (RFC 5297) + * Copyright (c) 2013 Cozybit, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef AES_SIV_H +#define AES_SIV_H + +int aes_siv_encrypt(const u8 *key, const u8 *pw, + size_t pwlen, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *out); +int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out); + +#endif /* AES_SIV_H */ diff --git a/src/crypto/random.c b/src/crypto/random.c index 053740e9..bc758aa5 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -232,12 +232,8 @@ int random_pool_ready(void) */ fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (fd < 0) { -#ifndef CONFIG_NO_STDOUT_DEBUG - int error = errno; - perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(error)); -#endif /* CONFIG_NO_STDOUT_DEBUG */ + strerror(errno)); return -1; } @@ -417,12 +413,8 @@ void random_init(const char *entropy_file) random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK); if (random_fd < 0) { -#ifndef CONFIG_NO_STDOUT_DEBUG - int error = errno; - perror("open(/dev/random)"); wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s", - strerror(error)); -#endif /* CONFIG_NO_STDOUT_DEBUG */ + strerror(errno)); return; } wpa_printf(MSG_DEBUG, "random: Trying to read entropy from " diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c new file mode 100644 index 00000000..d8a1beb3 --- /dev/null +++ b/src/crypto/sha256-kdf.c @@ -0,0 +1,76 @@ +/* + * HMAC-SHA256 KDF (RFC 5295) + * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" + + +/** + * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. + */ +int hmac_sha256_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA256_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA256_MAC_LEN; + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA256_MAC_LEN) + clen = SHA256_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + return -1; + } + iter++; + + if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + return -1; + } + } + + return 0; +} diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h index 7596a522..b15f5115 100644 --- a/src/crypto/sha256.h +++ b/src/crypto/sha256.h @@ -1,6 +1,6 @@ /* * SHA256 hash implementation and interface functions - * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -23,5 +23,8 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label, void tls_prf_sha256(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); +int hmac_sha256_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA256_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 65e0f797..345ebc7c 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -74,6 +74,7 @@ struct tls_config { const char *pkcs11_module_path; int fips_mode; int cert_in_cb; + const char *openssl_ciphers; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -87,6 +88,7 @@ struct tls_config { #define TLS_CONN_REQUIRE_OCSP BIT(4) #define TLS_CONN_DISABLE_TLSv1_1 BIT(5) #define TLS_CONN_DISABLE_TLSv1_2 BIT(6) +#define TLS_CONN_EAP_FAST BIT(7) /** * struct tls_connection_params - Parameters for TLS connection @@ -123,6 +125,7 @@ struct tls_config { * specific for now) * @cert_id: the certificate's id when using engine * @ca_cert_id: the CA certificate's id when using engine + * @openssl_ciphers: OpenSSL cipher configuration * @flags: Parameter options (TLS_CONN_*) * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response * or %NULL if OCSP is not enabled @@ -161,6 +164,7 @@ struct tls_connection_params { const char *key_id; const char *cert_id; const char *ca_cert_id; + const char *openssl_ciphers; unsigned int flags; const char *ocsp_stapling_response; diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index cb23eb9c..20d0a31f 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -81,7 +81,7 @@ struct tls_global { }; struct tls_connection { - gnutls_session session; + gnutls_session_t session; char *subject_match, *altsubject_match; int read_alerts, write_alerts, failed; @@ -199,7 +199,7 @@ int tls_get_errors(void *ssl_ctx) } -static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, +static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -228,7 +228,7 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, } -static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, +static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; @@ -286,7 +286,7 @@ static int tls_gnutls_init_session(struct tls_global *global, gnutls_transport_set_pull_function(conn->session, tls_pull_func); gnutls_transport_set_push_function(conn->session, tls_push_func); - gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn); return 0; @@ -563,16 +563,29 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->client_cert && params->private_key) { - /* TODO: private_key_passwd? */ +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_PEM, params->private_key_passwd, 0); +#else + /* private_key_passwd not (easily) supported here */ ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_PEM); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client cert/key " "in PEM format: %s", gnutls_strerror(ret)); +#if GNUTLS_VERSION_NUMBER >= 0x03010b + ret = gnutls_certificate_set_x509_key_file2( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER, + params->private_key_passwd, 0); +#else ret = gnutls_certificate_set_x509_key_file( conn->xcred, params->client_cert, params->private_key, GNUTLS_X509_FMT_DER); +#endif if (ret < 0) { wpa_printf(MSG_DEBUG, "Failed to read client " "cert/key in DER format: %s", diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index e1534224..c72134af 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -45,14 +45,6 @@ #define ERR_remove_thread_state(tid) ERR_remove_state(0) #endif -#if OPENSSL_VERSION_NUMBER >= 0x10000000L -/* - * Session ticket override patch was merged into OpenSSL 0.9.9 tree on - * 2008-11-15. This version uses a bit different API compared to the old patch. - */ -#define CONFIG_OPENSSL_TICKET_OVERRIDE -#endif - #if defined(OPENSSL_IS_BORINGSSL) /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */ typedef size_t stack_index_t; @@ -700,12 +692,15 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, NULL, NULL }; - if (!pkcs11_so_path || !pkcs11_module_path) + if (!pkcs11_so_path) return 0; pre_cmd[1] = pkcs11_so_path; pre_cmd[3] = engine_id; - post_cmd[1] = pkcs11_module_path; + if (pkcs11_module_path) + post_cmd[1] = pkcs11_module_path; + else + post_cmd[0] = NULL; wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); @@ -747,6 +742,7 @@ void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; struct tls_context *context; + const char *ciphers; if (tls_openssl_ref_count == 0) { tls_global = context = tls_context_new(conf); @@ -809,7 +805,7 @@ void * tls_init(const struct tls_config *conf) } tls_openssl_ref_count++; - ssl = SSL_CTX_new(TLSv1_method()); + ssl = SSL_CTX_new(SSLv23_method()); if (ssl == NULL) { tls_openssl_ref_count--; #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA @@ -823,19 +819,22 @@ void * tls_init(const struct tls_config *conf) return NULL; } + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); + SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); + SSL_CTX_set_info_callback(ssl, ssl_info_cb); #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA SSL_CTX_set_app_data(ssl, context); #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */ #ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || conf->pkcs11_module_path)) { - wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); - ERR_load_ENGINE_strings(); - ENGINE_load_dynamic(); - if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, conf->pkcs11_module_path)) { @@ -845,6 +844,18 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_NO_ENGINE */ + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "DEFAULT:!EXP:!LOW"; + if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(ssl); + return NULL; + } + return ssl; } @@ -886,16 +897,6 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); return -1; } -#ifndef ANDROID - if (pin == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); - return -1; - } -#endif - if (key_id == NULL) { - wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); - return -1; - } ERR_clear_error(); #ifdef ANDROID @@ -916,21 +917,34 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id, wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); #ifndef ANDROID - if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { + if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", ERR_error_string(ERR_get_error(), NULL)); goto err; } #endif - /* load private key first in-case PIN is required for cert */ - conn->private_key = ENGINE_load_private_key(conn->engine, - key_id, NULL, NULL); - if (!conn->private_key) { - wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" - " '%s' [%s]", key_id, - ERR_error_string(ERR_get_error(), NULL)); - ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; - goto err; + if (key_id) { + /* + * Ensure that the ENGINE does not attempt to use the OpenSSL + * UI system to obtain a PIN, if we didn't provide one. + */ + struct { + const void *password; + const char *prompt_info; + } key_cb = { "", NULL }; + + /* load private key first in-case PIN is required for cert */ + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, + &key_cb); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, + "ENGINE: cannot load private key with id '%s' [%s]", + key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } } /* handle a certificate and/or CA certificate */ @@ -2852,7 +2866,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, return -1; } ret = os_snprintf(pos, end - pos, ":%s", suite); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -2907,15 +2921,9 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || conn->ssl == NULL || ext_type != 35) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE if (SSL_set_session_ticket_ext(conn->ssl, (void *) data, data_len) != 1) return -1; -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ - if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, - data_len) != 1) - return -1; -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ return 0; } @@ -3201,20 +3209,64 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, { int ret; unsigned long err; + int can_pkcs11 = 0; + const char *key_id = params->key_id; + const char *cert_id = params->cert_id; + const char *ca_cert_id = params->ca_cert_id; + const char *engine_id = params->engine ? params->engine_id : NULL; if (conn == NULL) return -1; + /* + * If the engine isn't explicitly configured, and any of the + * cert/key fields are actually PKCS#11 URIs, then automatically + * use the PKCS#11 ENGINE. + */ + if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0) + can_pkcs11 = 1; + + if (!key_id && params->private_key && can_pkcs11 && + os_strncmp(params->private_key, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + key_id = params->private_key; + } + + if (!cert_id && params->client_cert && can_pkcs11 && + os_strncmp(params->client_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + cert_id = params->client_cert; + } + + if (!ca_cert_id && params->ca_cert && can_pkcs11 && + os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) { + can_pkcs11 = 2; + ca_cert_id = params->ca_cert; + } + + /* If we need to automatically enable the PKCS#11 ENGINE, do so. */ + if (can_pkcs11 == 2 && !engine_id) + engine_id = "pkcs11"; + + if (params->flags & TLS_CONN_EAP_FAST) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Use TLSv1_method() for EAP-FAST"); + if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) { + tls_show_errors(MSG_INFO, __func__, + "Failed to set TLSv1_method() for EAP-FAST"); + return -1; + } + } + while ((err = ERR_get_error())) { wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", __func__, ERR_error_string(err, NULL)); } - if (params->engine) { + if (engine_id) { wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); - ret = tls_engine_init(conn, params->engine_id, params->pin, - params->key_id, params->cert_id, - params->ca_cert_id); + ret = tls_engine_init(conn, engine_id, params->pin, + key_id, cert_id, ca_cert_id); if (ret) return ret; } @@ -3224,9 +3276,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->suffix_match)) return -1; - if (params->engine && params->ca_cert_id) { + if (engine_id && ca_cert_id) { if (tls_connection_engine_ca_cert(tls_ctx, conn, - params->ca_cert_id)) + ca_cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, params->ca_cert_blob, @@ -3234,15 +3286,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->ca_path)) return -1; - if (params->engine && params->cert_id) { - if (tls_connection_engine_client_cert(conn, params->cert_id)) + if (engine_id && cert_id) { + if (tls_connection_engine_client_cert(conn, cert_id)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) return -1; - if (params->engine && params->key_id) { + if (engine_id && key_id) { wpa_printf(MSG_DEBUG, "TLS: Using private key from engine"); if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; @@ -3262,6 +3314,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } + if (params->openssl_ciphers && + SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(conn->ssl, SSL_OP_NO_TICKET); @@ -3328,6 +3388,14 @@ int tls_global_set_params(void *tls_ctx, return -1; } + if (params->openssl_ciphers && + SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); @@ -3432,7 +3500,6 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, } -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, int len, void *arg) { @@ -3458,62 +3525,6 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, return 1; } -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET -static void tls_hello_ext_cb(SSL *s, int client_server, int type, - unsigned char *data, int len, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - type, len); - - if (type == TLSEXT_TYPE_session_ticket && !client_server) { - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", data, len); - conn->session_ticket = os_malloc(len); - if (conn->session_ticket == NULL) - return; - - os_memcpy(conn->session_ticket, data, len); - conn->session_ticket_len = len; - } -} -#else /* SSL_OP_NO_TICKET */ -static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) -{ - struct tls_connection *conn = arg; - - if (conn == NULL || conn->session_ticket_cb == NULL) - return 0; - - wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__, - ext->type, ext->length); - - os_free(conn->session_ticket); - conn->session_ticket = NULL; - - if (ext->type == 35) { - wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " - "extension", ext->data, ext->length); - conn->session_ticket = os_malloc(ext->length); - if (conn->session_ticket == NULL) - return SSL_AD_INTERNAL_ERROR; - - os_memcpy(conn->session_ticket, ext->data, ext->length); - conn->session_ticket_len = ext->length; - } - - return 0; -} -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ @@ -3530,33 +3541,12 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, conn) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, tls_session_ticket_ext_cb, conn); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb, - conn) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } else { if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) return -1; -#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL); -#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#ifdef SSL_OP_NO_TICKET - SSL_set_tlsext_debug_callback(conn->ssl, NULL); - SSL_set_tlsext_debug_arg(conn->ssl, conn); -#else /* SSL_OP_NO_TICKET */ - if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1) - return -1; -#endif /* SSL_OP_NO_TICKET */ -#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ } return 0; diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 6af72943..eeaba668 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -23,8 +23,7 @@ #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 -#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 -#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_NO_IR 0x00000002 #define HOSTAPD_CHAN_RADAR 0x00000008 #define HOSTAPD_CHAN_HT40PLUS 0x00000010 #define HOSTAPD_CHAN_HT40MINUS 0x00000020 @@ -42,6 +41,12 @@ #define HOSTAPD_CHAN_VHT_50_30 0x00002000 #define HOSTAPD_CHAN_VHT_70_10 0x00004000 +#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000 +#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000 + +/** + * enum reg_change_initiator - Regulatory change initiator + */ enum reg_change_initiator { REGDOM_SET_BY_CORE, REGDOM_SET_BY_USER, @@ -50,6 +55,9 @@ enum reg_change_initiator { REGDOM_BEACON_HINT, }; +/** + * enum reg_type - Regulatory change types + */ enum reg_type { REGDOM_TYPE_UNKNOWN, REGDOM_TYPE_COUNTRY, @@ -82,8 +90,8 @@ struct hostapd_channel_data { */ u8 max_tx_power; - /* - * survey_list - Linked list of surveys + /** + * survey_list - Linked list of surveys (struct freq_survey) */ struct dl_list survey_list; @@ -102,7 +110,9 @@ struct hostapd_channel_data { long double interference_factor; #endif /* CONFIG_ACS */ - /* DFS CAC time in milliseconds */ + /** + * dfs_cac_ms - DFS CAC time in milliseconds + */ unsigned int dfs_cac_ms; }; @@ -170,10 +180,12 @@ struct hostapd_hw_modes { #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 #define IEEE80211_MODE_AP 2 +#define IEEE80211_MODE_MESH 5 #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 +#define IEEE80211_CAP_RRM 0x1000 /* DMG (60 GHz) IEEE 802.11ad */ /* type - bits 0..1 */ @@ -213,6 +225,11 @@ struct hostapd_hw_modes { * constructed of the IEs that are available. This field will also need to * include SSID in IE format. All drivers are encouraged to be extended to * report all IEs to make it easier to support future additions. + * + * This structure data is followed by ie_len octets of IEs from Probe Response + * frame (or if the driver does not indicate source of IEs, these may also be + * from Beacon frame). After the first set of IEs, another set of IEs may follow + * (with beacon_ie_len octets of data) if the driver provides both IE sets. */ struct wpa_scan_res { unsigned int flags; @@ -227,13 +244,7 @@ struct wpa_scan_res { unsigned int age; size_t ie_len; size_t beacon_ie_len; - /* - * Followed by ie_len octets of IEs from Probe Response frame (or if - * the driver does not indicate source of IEs, these may also be from - * Beacon frame). After the first set of IEs, another set of IEs may - * follow (with beacon_ie_len octets of data) if the driver provides - * both IE sets. - */ + /* Followed by ie_len + beacon_ie_len octets of IE data */ }; /** @@ -370,6 +381,27 @@ struct wpa_driver_scan_params { */ unsigned int low_priority:1; + /** + * mac_addr_rand - Requests driver to randomize MAC address + */ + unsigned int mac_addr_rand:1; + + /** + * mac_addr - MAC address used with randomization. The address cannot be + * a multicast one, i.e., bit 0 of byte 0 should not be set. + */ + const u8 *mac_addr; + + /** + * mac_addr_mask - MAC address mask used with randomization. + * + * Bits that are 0 in the mask should be randomized. Bits that are 1 in + * the mask should be taken as is from mac_addr. The mask should not + * allow the generation of a multicast address, i.e., bit 0 of byte 0 + * must be set. + */ + const u8 *mac_addr_mask; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -399,34 +431,95 @@ struct wpa_driver_auth_params { */ int p2p; + /** + * sae_data - SAE elements for Authentication frame + * + * This buffer starts with the Authentication transaction sequence + * number field. If SAE is not used, this pointer is %NULL. + */ const u8 *sae_data; - size_t sae_data_len; + /** + * sae_data_len - Length of sae_data buffer in octets + */ + size_t sae_data_len; }; +/** + * enum wps_mode - WPS mode + */ enum wps_mode { - WPS_MODE_NONE /* no WPS provisioning being used */, - WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */, - WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection - */ + /** + * WPS_MODE_NONE - No WPS provisioning being used + */ + WPS_MODE_NONE, + + /** + * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode + */ + WPS_MODE_OPEN, + + /** + * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection + */ + WPS_MODE_PRIVACY }; +/** + * struct hostapd_freq_params - Channel parameters + */ struct hostapd_freq_params { - int mode; + /** + * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..) + */ + enum hostapd_hw_mode mode; + + /** + * freq - Primary channel center frequency in MHz + */ int freq; + + /** + * channel - Channel number + */ int channel; - /* for HT */ + + /** + * ht_enabled - Whether HT is enabled + */ int ht_enabled; - int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, - * secondary channel below primary, 1 = HT40 - * enabled, secondary channel above primary */ - /* for VHT */ + /** + * sec_channel_offset - Secondary channel offset for HT40 + * + * 0 = HT40 disabled, + * -1 = HT40 enabled, secondary channel below primary, + * 1 = HT40 enabled, secondary channel above primary + */ + int sec_channel_offset; + + /** + * vht_enabled - Whether VHT is enabled + */ int vht_enabled; - /* valid for both HT and VHT, center_freq2 is non-zero - * only for bandwidth 80 and an 80+80 channel */ - int center_freq1, center_freq2; + /** + * center_freq1 - Segment 0 center frequency in MHz + * + * Valid for both HT and VHT. + */ + int center_freq1; + + /** + * center_freq2 - Segment 1 center frequency in MHz + * + * Non-zero only for bandwidth 80 and an 80+80 channel + */ + int center_freq2; + + /** + * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160) + */ int bandwidth; }; @@ -680,12 +773,21 @@ struct wpa_driver_associate_params { int disable_ht; /** - * HT Capabilities over-rides. Only bits set in the mask will be used, - * and not all values are used by the kernel anyway. Currently, MCS, - * MPDU and MSDU fields are used. + * htcaps - HT Capabilities over-rides + * + * Only bits set in the mask will be used, and not all values are used + * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used. + * + * Pointer to struct ieee80211_ht_capabilities. */ - const u8 *htcaps; /* struct ieee80211_ht_capabilities * */ - const u8 *htcaps_mask; /* struct ieee80211_ht_capabilities * */ + const u8 *htcaps; + + /** + * htcaps_mask - HT Capabilities over-rides mask + * + * Pointer to struct ieee80211_ht_capabilities. + */ + const u8 *htcaps_mask; #ifdef CONFIG_VHT_OVERRIDES /** @@ -699,6 +801,20 @@ struct wpa_driver_associate_params { const struct ieee80211_vht_capabilities *vhtcaps; const struct ieee80211_vht_capabilities *vhtcaps_mask; #endif /* CONFIG_VHT_OVERRIDES */ + + /** + * req_key_mgmt_offload - Request key management offload for connection + * + * Request key management offload for this connection if the device + * supports it. + */ + int req_key_mgmt_offload; + + /** + * Flag for indicating whether this association includes support for + * RRM (Radio Resource Measurements) + */ + int rrm_used; }; enum hide_ssid { @@ -895,6 +1011,14 @@ struct wpa_driver_ap_params { int ap_max_inactivity; /** + * smps_mode - SMPS mode + * + * SMPS mode to be used by the AP, specified as the relevant bits of + * ht_capab (i.e. HT_CAP_INFO_SMPS_*). + */ + unsigned int smps_mode; + + /** * disable_dgaf - Whether group-addressed frames are disabled */ int disable_dgaf; @@ -910,6 +1034,33 @@ struct wpa_driver_ap_params { struct hostapd_freq_params *freq; }; +struct wpa_driver_mesh_bss_params { +#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001 + /* + * TODO: Other mesh configuration parameters would go here. + * See NL80211_MESHCONF_* for all the mesh config parameters. + */ + unsigned int flags; +}; + +struct wpa_driver_mesh_join_params { + const u8 *meshid; + int meshid_len; + const int *basic_rates; + const u8 *ies; + int ie_len; + int freq; + int beacon_int; + int max_peer_links; + enum ht_mode ht_mode; + struct wpa_driver_mesh_bss_params conf; +#define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001 +#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002 +#define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004 +#define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008 + unsigned int flags; +}; + /** * struct wpa_driver_capa - Driver capability information */ @@ -922,6 +1073,7 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 + /** Bitfield of supported key management suites */ unsigned int key_mgmt; #define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001 @@ -937,94 +1089,121 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400 #define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800 #define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000 + /** Bitfield of supported cipher suites */ unsigned int enc; #define WPA_DRIVER_AUTH_OPEN 0x00000001 #define WPA_DRIVER_AUTH_SHARED 0x00000002 #define WPA_DRIVER_AUTH_LEAP 0x00000004 + /** Bitfield of supported IEEE 802.11 authentication algorithms */ unsigned int auth; -/* Driver generated WPA/RSN IE */ +/** Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 -/* Driver needs static WEP key setup after association command */ +/** Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 -/* Driver takes care of all DFS operations */ +/** Driver takes care of all DFS operations */ #define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004 -/* Driver takes care of RSN 4-way handshake internally; PMK is configured with +/** Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 +/** Driver is for a wired Ethernet interface */ #define WPA_DRIVER_FLAGS_WIRED 0x00000010 -/* Driver provides separate commands for authentication and association (SME in +/** Driver provides separate commands for authentication and association (SME in * wpa_supplicant). */ #define WPA_DRIVER_FLAGS_SME 0x00000020 -/* Driver supports AP mode */ +/** Driver supports AP mode */ #define WPA_DRIVER_FLAGS_AP 0x00000040 -/* Driver needs static WEP key setup after association has been completed */ +/** Driver needs static WEP key setup after association has been completed */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 -/* Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */ +/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */ #define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100 -/* Driver supports concurrent P2P operations */ +/** Driver supports concurrent P2P operations */ #define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200 -/* +/** * Driver uses the initial interface as a dedicated management interface, i.e., * it cannot be used for P2P group operations or non-P2P purposes. */ #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400 -/* This interface is P2P capable (P2P GO or P2P Client) */ +/** This interface is P2P capable (P2P GO or P2P Client) */ #define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800 -/* Driver supports station and key removal when stopping an AP */ +/** Driver supports station and key removal when stopping an AP */ #define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000 -/* +/** * Driver uses the initial interface for P2P management interface and non-P2P * purposes (e.g., connect to infra AP), but this interface cannot be used for * P2P group operations. */ #define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000 -/* +/** * Driver is known to use sane error codes, i.e., when it indicates that * something (e.g., association) fails, there was indeed a failure and the * operation does not end up getting completed successfully later. */ #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000 -/* Driver supports off-channel TX */ +/** Driver supports off-channel TX */ #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000 -/* Driver indicates TX status events for EAPOL Data frames */ +/** Driver indicates TX status events for EAPOL Data frames */ #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000 -/* Driver indicates TX status events for Deauth/Disassoc frames */ +/** Driver indicates TX status events for Deauth/Disassoc frames */ #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000 -/* Driver supports roaming (BSS selection) in firmware */ +/** Driver supports roaming (BSS selection) in firmware */ #define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000 -/* Driver supports operating as a TDLS peer */ +/** Driver supports operating as a TDLS peer */ #define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000 -/* Driver requires external TDLS setup/teardown/discovery */ +/** Driver requires external TDLS setup/teardown/discovery */ #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000 -/* Driver indicates support for Probe Response offloading in AP mode */ +/** Driver indicates support for Probe Response offloading in AP mode */ #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000 -/* Driver supports U-APSD in AP mode */ +/** Driver supports U-APSD in AP mode */ #define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000 -/* Driver supports inactivity timer in AP mode */ +/** Driver supports inactivity timer in AP mode */ #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000 -/* Driver expects user space implementation of MLME in AP mode */ +/** Driver expects user space implementation of MLME in AP mode */ #define WPA_DRIVER_FLAGS_AP_MLME 0x01000000 -/* Driver supports SAE with user space SME */ +/** Driver supports SAE with user space SME */ #define WPA_DRIVER_FLAGS_SAE 0x02000000 -/* Driver makes use of OBSS scan mechanism in wpa_supplicant */ +/** Driver makes use of OBSS scan mechanism in wpa_supplicant */ #define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000 -/* Driver supports IBSS (Ad-hoc) mode */ +/** Driver supports IBSS (Ad-hoc) mode */ #define WPA_DRIVER_FLAGS_IBSS 0x08000000 -/* Driver supports radar detection */ +/** Driver supports radar detection */ #define WPA_DRIVER_FLAGS_RADAR 0x10000000 -/* Driver supports a dedicated interface for P2P Device */ +/** Driver supports a dedicated interface for P2P Device */ #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000 -/* Driver supports QoS Mapping */ +/** Driver supports QoS Mapping */ #define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000 -/* Driver supports CSA in AP mode */ +/** Driver supports CSA in AP mode */ #define WPA_DRIVER_FLAGS_AP_CSA 0x80000000 - unsigned int flags; - +/** Driver supports mesh */ +#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL +/** Driver support ACS offload */ +#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL +/** Driver supports key management offload */ +#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL +/** Driver supports TDLS channel switching */ +#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL + u64 flags; + +#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001 +#define WPA_DRIVER_SMPS_MODE_DYNAMIC 0x00000002 + unsigned int smps_modes; + + unsigned int wmm_ac_supported:1; + + unsigned int mac_addr_rand_scan_supported:1; + unsigned int mac_addr_rand_sched_scan_supported:1; + + /** Maximum number of supported active probe SSIDs */ int max_scan_ssids; + + /** Maximum number of supported active probe SSIDs for sched_scan */ int max_sched_scan_ssids; + + /** Whether sched_scan (offloaded scanning) is supported */ int sched_scan_supported; + + /** Maximum number of supported match sets for sched_scan */ int max_match_sets; /** @@ -1042,13 +1221,13 @@ struct wpa_driver_capa { * probe_resp_offloads - Bitmap of supported protocols by the driver * for Probe Response offloading. */ -/* Driver Probe Response offloading support for WPS ver. 1 */ +/** Driver Probe Response offloading support for WPS ver. 1 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001 -/* Driver Probe Response offloading support for WPS ver. 2 */ +/** Driver Probe Response offloading support for WPS ver. 2 */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002 -/* Driver Probe Response offloading support for P2P */ +/** Driver Probe Response offloading support for P2P */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004 -/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ +/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */ #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008 unsigned int probe_resp_offloads; @@ -1069,6 +1248,24 @@ struct wpa_driver_capa { unsigned int extended_capa_len; struct wowlan_triggers wowlan_triggers; + +/** Driver adds the DS Params Set IE in Probe Request frames */ +#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001 +/** Driver adds the WFA TPC IE in Probe Request frames */ +#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002 +/** Driver handles quiet period requests */ +#define WPA_DRIVER_FLAGS_QUIET 0x00000004 +/** + * Driver is capable of inserting the current TX power value into the body of + * transmitted frames. + * Background: Some Action frames include a TPC Report IE. This IE contains a + * TX power field, which has to be updated by lower layers. One such Action + * frame is Link Measurement Report (part of RRM). Another is TPC Report (part + * of spectrum management). Note that this insertion takes place at a fixed + * offset, namely the 6th byte in the Action frame body. + */ +#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008 + u32 rrm_flags; }; @@ -1098,6 +1295,10 @@ struct hostapd_sta_add_params { int vht_opmode_enabled; u8 vht_opmode; u32 flags; /* bitmask of WPA_STA_* flags */ + u32 flags_mask; /* unset bits in flags */ +#ifdef CONFIG_MESH + enum mesh_plink_state plink_state; +#endif /* CONFIG_MESH */ int set; /* Set STA parameters instead of add */ u8 qosinfo; const u8 *ext_capab; @@ -1159,16 +1360,19 @@ enum wpa_driver_if_type { * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the * abstracted P2P Device function in the driver */ - WPA_IF_P2P_DEVICE + WPA_IF_P2P_DEVICE, + + /* + * WPA_IF_MESH - Mesh interface + */ + WPA_IF_MESH, }; struct wpa_init_params { void *global_priv; const u8 *bssid; const char *ifname; - const u8 *ssid; - size_t ssid_len; - const char *test_socket; + const char *driver_params; int use_pae_group_addr; char **bridge; size_t num_bridge; @@ -1197,6 +1401,7 @@ struct wpa_bss_params { #define WPA_STA_SHORT_PREAMBLE BIT(2) #define WPA_STA_MFP BIT(3) #define WPA_STA_TDLS_PEER BIT(4) +#define WPA_STA_AUTHENTICATED BIT(5) enum tdls_oper { TDLS_DISCOVERY_REQ, @@ -1311,6 +1516,23 @@ enum tdls_peer_capability { TDLS_PEER_WMM = BIT(2), }; +/* valid info in the wmm_params struct */ +enum wmm_params_valid_info { + WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0), +}; + +/** + * struct wmm_params - WMM parameterss configured for this association + * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields + * of the struct contain valid information. + * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if + * %WMM_PARAMS_UAPSD_QUEUES_INFO is set) + */ +struct wmm_params { + u8 info_bitmap; + u8 uapsd_queues; +}; + #ifdef CONFIG_MACSEC struct macsec_init_params { Boolean always_include_sci; @@ -1319,6 +1541,26 @@ struct macsec_init_params { }; #endif /* CONFIG_MACSEC */ +enum drv_br_port_attr { + DRV_BR_PORT_ATTR_PROXYARP, + DRV_BR_PORT_ATTR_HAIRPIN_MODE, +}; + +enum drv_br_net_param { + DRV_BR_NET_PARAM_GARP_ACCEPT, +}; + +struct drv_acs_params { + /* Selected mode (HOSTAPD_MODE_*) */ + enum hostapd_hw_mode hw_mode; + + /* Indicates whether HT is enabled */ + int ht_enabled; + + /* Indicates whether HT40 is enabled */ + int ht40_enabled; +}; + /** * struct wpa_driver_ops - Driver interface API definition @@ -1605,27 +1847,6 @@ struct wpa_driver_ops { const u8 * (*get_mac_addr)(void *priv); /** - * send_eapol - Optional function for sending EAPOL packets - * @priv: private driver interface data - * @dest: Destination MAC address - * @proto: Ethertype - * @data: EAPOL packet starting with IEEE 802.1X header - * @data_len: Size of the EAPOL packet - * - * Returns: 0 on success, -1 on failure - * - * This optional function can be used to override l2_packet operations - * with driver specific functionality. If this function pointer is set, - * l2_packet module is not used at all and the driver interface code is - * responsible for receiving and sending all EAPOL packets. The - * received EAPOL packets are sent to core code with EVENT_EAPOL_RX - * event. The driver interface is required to implement get_mac_addr() - * handler if send_eapol() is used. - */ - int (*send_eapol)(void *priv, const u8 *dest, u16 proto, - const u8 *data, size_t data_len); - - /** * set_operstate - Sets device operating state to DORMANT or UP * @priv: private driver interface data * @state: 0 = dormant, 1 = up @@ -1700,22 +1921,6 @@ struct wpa_driver_ops { size_t ies_len); /** - * send_ft_action - Send FT Action frame (IEEE 802.11r) - * @priv: Private driver interface data - * @action: Action field value - * @target_ap: Target AP address - * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body) - * @ies_len: Length of FT IEs in bytes - * Returns: 0 on success, -1 on failure - * - * The supplicant uses this callback to request the driver to transmit - * an FT Action frame (action category 6) for over-the-DS fast BSS - * transition. - */ - int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len); - - /** * get_scan_results2 - Fetch the latest scan results * @priv: private driver interface data * @@ -2547,6 +2752,45 @@ struct wpa_driver_ops { u8 qos_map_set_len); /** + * br_add_ip_neigh - Add a neigh to the bridge ip neigh table + * @priv: Private driver interface data + * @version: IP version of the IP address, 4 or 6 + * @ipaddr: IP address for the neigh entry + * @prefixlen: IP address prefix length + * @addr: Corresponding MAC address + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr, + int prefixlen, const u8 *addr); + + /** + * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table + * @priv: Private driver interface data + * @version: IP version of the IP address, 4 or 6 + * @ipaddr: IP address for the neigh entry + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr); + + /** + * br_port_set_attr - Set a bridge port attribute + * @attr: Bridge port attribute to set + * @val: Value to be set + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr, + unsigned int val); + + /** + * br_port_set_attr - Set a bridge network parameter + * @param: Bridge parameter to set + * @val: Value to be set + * Returns: 0 on success, negative (<0) on failure + */ + int (*br_set_net_param)(void *priv, enum drv_br_net_param param, + unsigned int val); + + /** * set_wowlan - Set wake-on-wireless triggers * @priv: Private driver interface data * @triggers: wowlan triggers @@ -2751,6 +2995,55 @@ struct wpa_driver_ops { int (*switch_channel)(void *priv, struct csa_settings *settings); /** + * add_tx_ts - Add traffic stream + * @priv: Private driver interface data + * @tsid: Traffic stream ID + * @addr: Receiver address + * @user_prio: User priority of the traffic stream + * @admitted_time: Admitted time for this TS in units of + * 32 microsecond periods (per second). + * Returns: 0 on success, -1 on failure + */ + int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio, + u16 admitted_time); + + /** + * del_tx_ts - Delete traffic stream + * @priv: Private driver interface data + * @tsid: Traffic stream ID + * @addr: Receiver address + * Returns: 0 on success, -1 on failure + */ + int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr); + + /** + * Enable channel-switching with TDLS peer + * @priv: Private driver interface data + * @addr: MAC address of the TDLS peer + * @oper_class: Operating class of the switch channel + * @params: Channel specification + * Returns: 0 on success, -1 on failure + * + * The function indicates to driver that it can start switching to a + * different channel with a specified TDLS peer. The switching is + * assumed on until canceled with tdls_disable_channel_switch(). + */ + int (*tdls_enable_channel_switch)( + void *priv, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params); + + /** + * Disable channel switching with TDLS peer + * @priv: Private driver interface data + * @addr: MAC address of the TDLS peer + * Returns: 0 on success, -1 on failure + * + * This function indicates to the driver that it should stop switching + * with a given TDLS peer. + */ + int (*tdls_disable_channel_switch)(void *priv, const u8 *addr); + + /** * start_dfs_cac - Listen for radar interference on the channel * @priv: Private driver interface data * @freq: Channel parameters @@ -3023,6 +3316,40 @@ struct wpa_driver_ops { */ int (*disable_transmit_sa)(void *priv, u32 channel, u8 an); #endif /* CONFIG_MACSEC */ + + /** + * init_mesh - Driver specific initialization for mesh + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + */ + int (*init_mesh)(void *priv); + + /** + * join_mesh - Join a mesh network + * @priv: Private driver interface data + * @params: Mesh configuration parameters + * Returns: 0 on success, -1 on failure + */ + int (*join_mesh)(void *priv, + struct wpa_driver_mesh_join_params *params); + + /** + * leave_mesh - Leave a mesh network + * @priv: Private driver interface data + * Returns 0 on success, -1 on failure + */ + int (*leave_mesh)(void *priv); + + /** + * do_acs - Automatically select channel + * @priv: Private driver interface data + * @params: Parameters for ACS + * Returns 0 on success, -1 on failure + * + * This command can be used to offload ACS to the driver if the driver + * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD). + */ + int (*do_acs)(void *priv, struct drv_acs_params *params); }; @@ -3211,11 +3538,6 @@ enum wpa_event_type { EVENT_ASSOC_TIMED_OUT, /** - * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received - */ - EVENT_FT_RRB_RX, - - /** * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS */ EVENT_WPS_BUTTON_PUSHED, @@ -3255,13 +3577,6 @@ enum wpa_event_type { EVENT_CANCEL_REMAIN_ON_CHANNEL, /** - * EVENT_MLME_RX - Report reception of frame for MLME (test use only) - * - * This event is used only by driver_test.c and userspace MLME. - */ - EVENT_MLME_RX, - - /** * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame * * This event is used to indicate when a Probe Request frame has been @@ -3289,9 +3604,7 @@ enum wpa_event_type { * EVENT_EAPOL_RX - Report received EAPOL frame * * When in AP mode with hostapd, this event is required to be used to - * deliver the receive EAPOL frames from the driver. With - * %wpa_supplicant, this event is used only if the send_eapol() handler - * is used to override the use of l2_packet for EAPOL frame TX. + * deliver the receive EAPOL frames from the driver. */ EVENT_EAPOL_RX, @@ -3498,7 +3811,20 @@ enum wpa_event_type { * to reduce issues due to interference or internal co-existence * information in the driver. */ - EVENT_AVOID_FREQUENCIES + EVENT_AVOID_FREQUENCIES, + + /** + * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification + */ + EVENT_NEW_PEER_CANDIDATE, + + /** + * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS + * + * Indicates a pair of primary and secondary channels chosen by ACS + * in device. + */ + EVENT_ACS_CHANNEL_SELECTED, }; @@ -3618,9 +3944,62 @@ union wpa_event_data { unsigned int freq; /** + * wmm_params - WMM parameters used in this association. + */ + struct wmm_params wmm_params; + + /** * addr - Station address (for AP mode) */ const u8 *addr; + + /** + * The following is the key management offload information + * @authorized + * @key_replay_ctr + * @key_replay_ctr_len + * @ptk_kck + * @ptk_kek_len + * @ptk_kek + * @ptk_kek_len + */ + + /** + * authorized - Status of key management offload, + * 1 = successful + */ + int authorized; + + /** + * key_replay_ctr - Key replay counter value last used + * in a valid EAPOL-Key frame + */ + const u8 *key_replay_ctr; + + /** + * key_replay_ctr_len - The length of key_replay_ctr + */ + size_t key_replay_ctr_len; + + /** + * ptk_kck - The derived PTK KCK + */ + const u8 *ptk_kck; + + /** + * ptk_kek_len - The length of ptk_kck + */ + size_t ptk_kck_len; + + /** + * ptk_kek - The derived PTK KEK + */ + const u8 *ptk_kek; + + /** + * ptk_kek_len - The length of ptk_kek + */ + size_t ptk_kek_len; } assoc_info; /** @@ -3830,15 +4209,6 @@ union wpa_event_data { } timeout_event; /** - * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events - */ - struct ft_rrb_rx { - const u8 *src; - const u8 *data; - size_t data_len; - } ft_rrb_rx; - - /** * struct tx_status - Data for EVENT_TX_STATUS events */ struct tx_status { @@ -3922,17 +4292,6 @@ union wpa_event_data { } scan_info; /** - * struct mlme_rx - Data for EVENT_MLME_RX events - */ - struct mlme_rx { - const u8 *buf; - size_t len; - int freq; - int channel; - int ssi; - } mlme_rx; - - /** * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events */ struct rx_probe_req { @@ -4112,7 +4471,7 @@ union wpa_event_data { * survey_results - Survey result data for EVENT_SURVEY * @freq_filter: Requested frequency survey filter, 0 if request * was for all survey data - * @survey_list: Linked list of survey data + * @survey_list: Linked list of survey data (struct freq_survey) */ struct survey_results { unsigned int freq_filter; @@ -4137,6 +4496,31 @@ union wpa_event_data { * This is used as the data with EVENT_AVOID_FREQUENCIES. */ struct wpa_freq_range_list freq_range; + + /** + * struct mesh_peer + * + * @peer: Peer address + * @ies: Beacon IEs + * @ie_len: Length of @ies + * + * Notification of new candidate mesh peer. + */ + struct mesh_peer { + const u8 *peer; + const u8 *ies; + size_t ie_len; + } mesh_peer; + + /** + * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED + * @pri_channel: Selected primary channel + * @sec_channel: Selected secondary channel + */ + struct acs_selected_channels { + u8 pri_channel; + u8 sec_channel; + } acs_selected_channels; }; /** @@ -4198,6 +4582,13 @@ const char * event_to_string(enum wpa_event_type event); /* Convert chan_width to a string for logging and control interfaces */ const char * channel_width_to_string(enum chan_width width); +int ht_supported(const struct hostapd_hw_modes *mode); +int vht_supported(const struct hostapd_hw_modes *mode); + +struct wowlan_triggers * +wpa_get_wowlan_triggers(const char *wowlan_triggers, + const struct wpa_driver_capa *capa); + /* NULL terminated array of linked in driver wrappers */ extern struct wpa_driver_ops *wpa_drivers[]; diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index b569a0a7..350d5059 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -224,10 +224,10 @@ set80211param(struct atheros_driver_data *drv, int op, int arg) 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: %s: Failed to set parameter (op %d " - "(%s) arg %d)", __func__, drv->iface, op, - athr_get_param_name(op), arg); + wpa_printf(MSG_INFO, + "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s", + __func__, drv->iface, op, athr_get_param_name(op), + arg, strerror(errno)); return -1; } return 0; @@ -290,14 +290,15 @@ atheros_configure_wpa(struct atheros_driver_data *drv, } 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); + wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v); return -1; } if (v == IEEE80211_CIPHER_WEP) { /* key length is done only for specific ciphers */ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { - printf("Unable to set group key length to %u\n", v); + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", v); return -1; } } @@ -319,7 +320,8 @@ atheros_configure_wpa(struct atheros_driver_data *drv, 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); + wpa_printf(MSG_INFO, + "Unable to set pairwise key ciphers to 0x%x", v); return -1; } @@ -327,8 +329,9 @@ atheros_configure_wpa(struct atheros_driver_data *drv, __func__, params->wpa_key_mgmt); if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - params->wpa_key_mgmt); + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + params->wpa_key_mgmt); return -1; } @@ -345,13 +348,14 @@ atheros_configure_wpa(struct atheros_driver_data *drv, wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v); if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { - printf("Unable to set RSN capabilities to 0x%x\n", v); + wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", + v); return -1; } wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { - printf("Unable to set WPA to %u\n", params->wpa); + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); return -1; } return 0; @@ -518,14 +522,14 @@ atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg, #endif /* ATH_GCM_SUPPORT */ #endif /* CONFIG_IEEE80211W */ default: - printf("%s: unknown/unsupported algorithm %d\n", - __func__, alg); + wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d", + __func__, alg); return -1; } if (key_len > sizeof(wk.ik_keydata)) { - printf("%s: key length %lu too big\n", __func__, - (unsigned long) key_len); + wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__, + (unsigned long) key_len); return -3; } @@ -636,7 +640,8 @@ atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, return 0; } - printf("Failed to get station stats information element.\n"); + wpa_printf(MSG_INFO, + "Failed to get station stats information element"); return -1; } @@ -769,145 +774,6 @@ atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, return ret; } -#ifdef CONFIG_WPS -static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - u16 fc; - union wpa_event_data event; - - /* 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; - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); -} -#endif /* CONFIG_WPS */ - -#ifdef CONFIG_IEEE80211R -static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - union wpa_event_data event; - const struct ieee80211_mgmt *mgmt; - u16 fc; - u16 stype; - int ielen; - const u8 *iebuf; - - /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */ - if (len < IEEE80211_HDRLEN) - return; - mgmt = (const struct ieee80211_mgmt *) buf; - - fc = le_to_host16(mgmt->frame_control); - - if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) - return; - stype = WLAN_FC_GET_STYPE(fc); - - wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, - (int) len); - - if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", - __func__); - return; - } - switch (stype) { - case WLAN_FC_STYPE_ASSOC_REQ: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req)) - break; - ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); - iebuf = mgmt->u.assoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); - break; - case WLAN_FC_STYPE_REASSOC_REQ: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req)) - break; - ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); - iebuf = mgmt->u.reassoc_req.variable; - drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); - break; - case WLAN_FC_STYPE_ACTION: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); - break; - case WLAN_FC_STYPE_AUTH: - if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth)) - break; - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); - os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); - event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); - event.auth.status_code = - le_to_host16(mgmt->u.auth.status_code); - event.auth.auth_transaction = - le_to_host16(mgmt->u.auth.auth_transaction); - event.auth.ies = mgmt->u.auth.variable; - event.auth.ies_len = len - IEEE80211_HDRLEN - - sizeof(mgmt->u.auth); - wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); - break; - default: - break; - } -} -#endif /* CONFIG_IEEE80211R */ - -#ifdef CONFIG_HS20 -static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct atheros_driver_data *drv = ctx; - const struct ieee80211_mgmt *mgmt; - u16 fc; - union wpa_event_data event; - - /* Send the Action frame for HS20 processing */ - - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) + - sizeof(mgmt->u.action.u.public_action)) - 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_ACTION || - mgmt->u.action.category != WLAN_ACTION_PUBLIC) - return; - - wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__); - - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = (const u8 *) mgmt; - event.rx_mgmt.frame_len = len; - wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); -} - -#endif /* CONFIG_HS20 */ - - static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, u8 qos_map_set_len) { @@ -947,9 +813,9 @@ static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, } if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) { - perror("ioctl[IEEE80211_IOCTL_DBGREQ]"); - wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map", - __func__, drv->iface); + wpa_printf(MSG_ERROR, + "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s", + __func__, drv->iface, strerror(errno)); return -1; } #endif /* CONFIG_ATHEROS_QOS_MAP */ @@ -957,30 +823,47 @@ static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set, return 0; } -#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) -static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20) +static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) { struct atheros_driver_data *drv = ctx; - union wpa_event_data event; const struct ieee80211_mgmt *mgmt; - u16 fc; - u16 stype; + union wpa_event_data event; + u16 fc, stype; + int ielen; + const u8 *iebuf; - /* Do 11R processing for WNM ACTION frames */ if (len < IEEE80211_HDRLEN) return; + mgmt = (const struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) return; + stype = WLAN_FC_GET_STYPE(fc); wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype, (int) len); + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.da = mgmt->da; + event.rx_probe_req.bssid = mgmt->bssid; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); + return; + } + if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore", __func__); @@ -988,36 +871,47 @@ static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf, } switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + iebuf = mgmt->u.assoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)) + break; + ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + iebuf = mgmt->u.reassoc_req.variable; + drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1); + break; case WLAN_FC_STYPE_ACTION: os_memset(&event, 0, sizeof(event)); event.rx_mgmt.frame = buf; event.rx_mgmt.frame_len = len; wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); break; + case WLAN_FC_STYPE_AUTH: + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) + break; + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.status_code = + le_to_host16(mgmt->u.auth.status_code); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth); + wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event); + break; default: break; } } -#endif /* CONFIG_WNM */ - -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) -static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ -#ifdef CONFIG_WPS - atheros_raw_recv_wps(ctx, src_addr, buf, len); -#endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R - atheros_raw_recv_11r(ctx, src_addr, buf, len); -#endif /* CONFIG_IEEE80211R */ -#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R) - atheros_raw_recv_11v(ctx, src_addr, buf, len); -#endif /* CONFIG_WNM */ -#ifdef CONFIG_HS20 - atheros_raw_recv_hs20(ctx, src_addr, buf, len); -#endif /* CONFIG_HS20 */ -} -#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ +#endif static int atheros_receive_pkt(struct atheros_driver_data *drv) { @@ -1606,8 +1500,9 @@ atheros_get_we_version(struct atheros_driver_data *drv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - free(range); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { @@ -1667,8 +1562,9 @@ atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, 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); + wpa_printf(MSG_INFO, + "EAPOL frame discarded, cannot malloc temp buffer of size %lu!", + (unsigned long) len); return -1; } } @@ -1705,14 +1601,16 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv = os_zalloc(sizeof(struct atheros_driver_data)); if (drv == NULL) { - printf("Could not allocate memory for atheros driver data\n"); + wpa_printf(MSG_INFO, + "Could not allocate memory for atheros driver data"); 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]"); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); goto bad; } memcpy(drv->iface, params->ifname, sizeof(drv->iface)); @@ -1720,7 +1618,8 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) 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)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); goto bad; } drv->ifindex = ifr.ifr_ifindex; @@ -1756,8 +1655,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) 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"); + wpa_printf(MSG_ERROR, + "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s", + strerror(errno)); goto bad; } @@ -1823,8 +1723,8 @@ atheros_set_ssid(void *priv, const u8 *buf, int len) iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); return -1; } return 0; @@ -1844,7 +1744,8 @@ atheros_get_ssid(void *priv, u8 *buf, int len) IW_ESSID_MAX_SIZE : len; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", + strerror(errno)); ret = -1; } else ret = iwr.u.essid.length; diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index ca64d5c3..c377970a 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -264,7 +264,8 @@ bsd_ctrl_iface(void *priv, int enable) os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) { - perror("ioctl[SIOCGIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); return -1; } @@ -279,7 +280,8 @@ bsd_ctrl_iface(void *priv, int enable) } if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) { - perror("ioctl[SIOCSIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); return -1; } @@ -404,22 +406,24 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) v = IEEE80211_CIPHER_NONE; break; default: - printf("Unknown group key cipher %u\n", - params->wpa_group); + wpa_printf(MSG_INFO, "Unknown group key cipher %u", + params->wpa_group); return -1; } wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)", __func__, ciphernames[v], v); if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) { - printf("Unable to set group key cipher to %u (%s)\n", - v, ciphernames[v]); + wpa_printf(MSG_INFO, + "Unable to set group key cipher to %u (%s)", + v, ciphernames[v]); return -1; } if (v == IEEE80211_CIPHER_WEP) { /* key length is done only for specific ciphers */ v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) { - printf("Unable to set group key length to %u\n", v); + wpa_printf(MSG_INFO, + "Unable to set group key length to %u", v); return -1; } } @@ -433,7 +437,8 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) v |= 1<<IEEE80211_CIPHER_NONE; wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) { - printf("Unable to set pairwise key ciphers to 0x%x\n", v); + wpa_printf(MSG_INFO, + "Unable to set pairwise key ciphers to 0x%x", v); return -1; } @@ -441,8 +446,9 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) __func__, params->wpa_key_mgmt); if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS, params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - params->wpa_key_mgmt); + wpa_printf(MSG_INFO, + "Unable to set key management algorithms to 0x%x", + params->wpa_key_mgmt); return -1; } @@ -452,14 +458,15 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, params->rsn_preauth); if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) { - printf("Unable to set RSN capabilities to 0x%x\n", v); + wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x", + v); return -1; } #endif /* IEEE80211_IOC_APPIE */ wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa); if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) { - printf("Unable to set WPA to %u\n", params->wpa); + wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa); return -1; } return 0; @@ -507,7 +514,8 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN]) memset(&ie, 0, sizeof(ie)); memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) { - printf("Failed to get WPA/RSN information element.\n"); + wpa_printf(MSG_INFO, + "Failed to get WPA/RSN information element"); goto no_ie; } iebuf = ie.wpa_ie; @@ -594,7 +602,7 @@ rtbuf_len(void) int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { - wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__, + wpa_printf(MSG_WARNING, "%s failed: %s", __func__, strerror(errno)); len = 2048; } @@ -652,7 +660,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, wk.ik_keyix = idx; if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) { - printf("Failed to get encryption.\n"); + wpa_printf(MSG_INFO, "Failed to get encryption"); return -1; } @@ -734,7 +742,7 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx) n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + wpa_printf(MSG_ERROR, "%s read() failed: %s", __func__, strerror(errno)); return; } @@ -814,7 +822,8 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv->hapd = hapd; drv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); goto bad; } os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); @@ -832,7 +841,8 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params) drv->route = socket(PF_ROUTE, SOCK_RAW, 0); if (drv->route < 0) { - perror("socket(PF_ROUTE,SOCK_RAW)"); + wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s", + strerror(errno)); goto bad; } eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv, @@ -1189,7 +1199,7 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) n = read(sock, drv->event_buf, drv->event_buf_len); if (n < 0) { if (errno != EINTR && errno != EAGAIN) - wpa_printf(MSG_ERROR, "%s read() failed: %s\n", + wpa_printf(MSG_ERROR, "%s read() failed: %s", __func__, strerror(errno)); return; } diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 77e6905d..f897c114 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -44,14 +44,12 @@ const char * event_to_string(enum wpa_event_type event) E2S(ASSOC_REJECT); E2S(AUTH_TIMED_OUT); E2S(ASSOC_TIMED_OUT); - E2S(FT_RRB_RX); E2S(WPS_BUTTON_PUSHED); E2S(TX_STATUS); E2S(RX_FROM_UNKNOWN); E2S(RX_MGMT); E2S(REMAIN_ON_CHANNEL); E2S(CANCEL_REMAIN_ON_CHANNEL); - E2S(MLME_RX); E2S(RX_PROBE_REQ); E2S(NEW_STA); E2S(EAPOL_RX); @@ -79,6 +77,8 @@ const char * event_to_string(enum wpa_event_type event) E2S(SURVEY); E2S(SCAN_STARTED); E2S(AVOID_FREQUENCIES); + E2S(NEW_PEER_CANDIDATE); + E2S(ACS_CHANNEL_SELECTED); } return "UNKNOWN"; @@ -105,3 +105,115 @@ const char * channel_width_to_string(enum chan_width width) return "unknown"; } } + + +int ht_supported(const struct hostapd_hw_modes *mode) +{ + if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports HT. Assume + * it does to avoid connection issues. + */ + return 1; + } + + /* + * IEEE Std 802.11n-2009 20.1.1: + * An HT non-AP STA shall support all EQM rates for one spatial stream. + */ + return mode->mcs_set[0] == 0xff; +} + + +int vht_supported(const struct hostapd_hw_modes *mode) +{ + if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) { + /* + * The driver did not indicate whether it supports VHT. Assume + * it does to avoid connection issues. + */ + return 1; + } + + /* + * A VHT non-AP STA shall support MCS 0-7 for one spatial stream. + * TODO: Verify if this complies with the standard + */ + return (mode->vht_mcs_set[0] & 0x3) != 3; +} + + +static int wpa_check_wowlan_trigger(const char *start, const char *trigger, + int capa_trigger, u8 *param_trigger) +{ + if (os_strcmp(start, trigger) != 0) + return 0; + if (!capa_trigger) + return 0; + + *param_trigger = 1; + return 1; +} + + +struct wowlan_triggers * +wpa_get_wowlan_triggers(const char *wowlan_triggers, + const struct wpa_driver_capa *capa) +{ + struct wowlan_triggers *triggers; + char *start, *end, *buf; + int last; + + if (!wowlan_triggers) + return NULL; + + buf = os_strdup(wowlan_triggers); + if (buf == NULL) + return NULL; + + triggers = os_zalloc(sizeof(*triggers)); + if (triggers == NULL) + goto out; + +#define CHECK_TRIGGER(trigger) \ + wpa_check_wowlan_trigger(start, #trigger, \ + capa->wowlan_triggers.trigger, \ + &triggers->trigger) + + start = buf; + while (*start != '\0') { + while (isblank(*start)) + start++; + if (*start == '\0') + break; + end = start; + while (!isblank(*end) && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + + if (!CHECK_TRIGGER(any) && + !CHECK_TRIGGER(disconnect) && + !CHECK_TRIGGER(magic_pkt) && + !CHECK_TRIGGER(gtk_rekey_failure) && + !CHECK_TRIGGER(eap_identity_req) && + !CHECK_TRIGGER(four_way_handshake) && + !CHECK_TRIGGER(rfkill_release)) { + wpa_printf(MSG_DEBUG, + "Unknown/unsupported wowlan trigger '%s'", + start); + os_free(triggers); + triggers = NULL; + goto out; + } + + if (last) + break; + start = end + 1; + } +#undef CHECK_TRIGGER + +out: + os_free(buf); + return triggers; +} diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 16f5563a..84b98fb8 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -214,7 +214,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -229,19 +229,21 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); return -1; } if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) { - printf("Could not register read socket\n"); + wpa_printf(MSG_ERROR, "Could not register read socket"); 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)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); return -1; } @@ -256,7 +258,7 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr) addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -361,9 +363,9 @@ static int hostap_set_iface_flags(void *priv, int dev_up) os_strlcpy(ifr.ifr_name, ifname, 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"); + wpa_printf(MSG_INFO, + "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s", + strerror(errno)); } } @@ -383,7 +385,8 @@ static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param, iwr.u.data.length = len; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s", + strerror(errno)); return -1; } @@ -497,7 +500,8 @@ static int hostap_ioctl_prism2param(void *priv, int param, int value) *i++ = value; if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { - perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s", + strerror(errno)); return -1; } @@ -554,8 +558,8 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len) iwr.u.essid.length = len + 1; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); - printf("len=%d\n", len); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", + len, strerror(errno)); return -1; } @@ -919,8 +923,9 @@ static int hostap_get_we_version(struct hostap_driver_data *drv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); - free(range); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); + os_free(range); return -1; } else if (iwr.u.data.length >= minlen && range->we_version_compiled >= 18) { @@ -975,23 +980,25 @@ static void * hostap_init(struct hostapd_data *hapd, drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { - perror("socket[PF_INET,SOCK_DGRAM]"); - free(drv); + wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s", + strerror(errno)); + os_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); + wpa_printf(MSG_ERROR, + "Could not enable hostapd mode for interface %s", + drv->iface); close(drv->ioctl_sock); - free(drv); + os_free(drv); return NULL; } if (hostap_init_sockets(drv, params->own_addr) || hostap_wireless_event_init(drv)) { close(drv->ioctl_sock); - free(drv); + os_free(drv); return NULL; } @@ -1060,7 +1067,8 @@ static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq) iwr.u.freq.e = 0; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", + strerror(errno)); return -1; } diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c index cf247992..3eae2f89 100644 --- a/src/drivers/driver_macsec_qca.c +++ b/src/drivers/driver_macsec_qca.c @@ -91,7 +91,7 @@ static int macsec_qca_multicast_membership(int sock, int ifindex, if (setsockopt(sock, SOL_PACKET, add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt"); + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); return -1; } return 0; @@ -131,14 +131,15 @@ static int macsec_qca_get_ifflags(const char *ifname, int *flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); 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]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -155,7 +156,7 @@ static int macsec_qca_set_ifflags(const char *ifname, int flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -163,7 +164,8 @@ static int macsec_qca_set_ifflags(const char *ifname, int flags) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_flags = flags & 0xffff; if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCSIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -180,14 +182,15 @@ static int macsec_qca_get_ifstatus(const char *ifname, int *status) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_print(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - perror("ioctl[SIOCGIFMEDIA]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", + strerror(errno)); close(s); return -1; } @@ -211,7 +214,7 @@ static int macsec_qca_multi(const char *ifname, const u8 *addr, int add) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -245,7 +248,8 @@ static int macsec_qca_multi(const char *ifname, const u8 *addr, int add) #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOC{ADD/DEL}MULTI]"); + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); close(s); return -1; } @@ -323,7 +327,7 @@ static void * macsec_qca_init(void *ctx, const char *ifname) #ifdef __linux__ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); if (drv->pf_sock < 0) - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); #else /* __linux__ */ drv->pf_sock = -1; #endif /* __linux__ */ diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c deleted file mode 100644 index 1635c1fb..00000000 --- a/src/drivers/driver_madwifi.c +++ /dev/null @@ -1,1309 +0,0 @@ -/* - * hostapd - driver interaction with MADWIFI 802.11 driver - * Copyright (c) 2004, Sam Leffler <sam@errno.com> - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - * - * This driver wrapper is only for hostapd AP mode functionality. Station - * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c - * wrapper. - */ - -#include "includes.h" -#include <sys/ioctl.h> - -#include "common.h" -#include "driver.h" -#include "driver_wext.h" -#include "eloop.h" -#include "common/ieee802_11_defs.h" -#include "linux_wext.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 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 */ - -#define WPA_KEY_RSC_LEN 8 - -#include "priv_netlink.h" -#include "netlink.h" -#include "linux_ioctl.h" -#include "l2_packet/l2_packet.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 */ - struct netlink_data *netlink; - 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 *own_addr, 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) ARRAY_SIZE(opnames) && - 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; -} - -#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 wpa_bss_params *params) -{ - int v; - - switch (params->wpa_group) { - case WPA_CIPHER_CCMP: - v = IEEE80211_CIPHER_AES_CCM; - break; - case WPA_CIPHER_TKIP: - v = IEEE80211_CIPHER_TKIP; - break; - case WPA_CIPHER_WEP104: - v = IEEE80211_CIPHER_WEP; - break; - case WPA_CIPHER_WEP40: - v = IEEE80211_CIPHER_WEP; - break; - case WPA_CIPHER_NONE: - v = IEEE80211_CIPHER_NONE; - break; - default: - wpa_printf(MSG_ERROR, "Unknown group key cipher %u", - params->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 = (params->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 (params->wpa_pairwise & WPA_CIPHER_CCMP) - v |= 1<<IEEE80211_CIPHER_AES_CCM; - if (params->wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<<IEEE80211_CIPHER_TKIP; - if (params->wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<<IEEE80211_CIPHER_NONE; - wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); - if (set80211param(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__, params->wpa_key_mgmt); - if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, - params->wpa_key_mgmt)) { - printf("Unable to set key management algorithms to 0x%x\n", - params->wpa_key_mgmt); - return -1; - } - - v = 0; - if (params->rsn_preauth) - v |= BIT(0); - wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", - __func__, params->rsn_preauth); - if (set80211param(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__, params->wpa); - if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { - printf("Unable to set WPA to %u\n", params->wpa); - return -1; - } - return 0; -} - -static int -madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) -{ - struct madwifi_driver_data *drv = priv; - - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); - - if (!params->enabled) { - /* XXX restore state */ - return set80211param(priv, IEEE80211_PARAM_AUTHMODE, - IEEE80211_AUTH_AUTO); - } - if (!params->wpa && !params->ieee802_1x) { - wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!"); - return -1; - } - if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { - wpa_printf(MSG_WARNING, "Error configuring WPA state!"); - return -1; - } - if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, - (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { - wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!"); - return -1; - } - - return 0; -} - -static int -madwifi_set_privacy(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 & WPA_STA_AUTHORIZED) - return madwifi_set_sta_authorized(priv, addr, 1); - if (!(flags_and & WPA_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 -wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, - const u8 *addr, int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - struct madwifi_driver_data *drv = priv; - struct ieee80211req_key wk; - u_int8_t cipher; - int ret; - - if (alg == WPA_ALG_NONE) - return madwifi_del_key(drv, addr, key_idx); - - wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", - __func__, alg, ether_sprintf(addr), key_idx); - - if (alg == WPA_ALG_WEP) - cipher = IEEE80211_CIPHER_WEP; - else if (alg == WPA_ALG_TKIP) - cipher = IEEE80211_CIPHER_TKIP; - else if (alg == WPA_ALG_CCMP) - cipher = IEEE80211_CIPHER_AES_CCM; - else { - printf("%s: unknown/unsupported algorithm %d\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 || is_broadcast_ether_addr(addr)) { - 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 %d key_len %lu set_tx %d)", - __func__, ether_sprintf(wk.ik_macaddr), key_idx, - alg, (unsigned long) key_len, set_tx); - } - - 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, NULL, 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(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 *own_addr, 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 *own_addr, 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; - u16 fc; - union wpa_event_data event; - - /* 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; - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); - wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); -} -#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; -} - -#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_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, - beacon ? wpabuf_len(beacon) : 0, - IEEE80211_APPIE_FRAME_BEACON) < 0) - return -1; - return madwifi_set_wps_ie(priv, - proberesp ? wpabuf_head(proberesp) : NULL, - proberesp ? wpabuf_len(proberesp) : 0, - IEEE80211_APPIE_FRAME_PROBE_RESP); -} -#else /* CONFIG_WPS */ -#define madwifi_set_ap_wps_ie NULL -#endif /* CONFIG_WPS */ - -static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq) -{ - struct madwifi_driver_data *drv = priv; - struct iwreq iwr; - - os_memset(&iwr, 0, sizeof(iwr)); - os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); - iwr.u.freq.m = freq->channel; - iwr.u.freq.e = 0; - - if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); - return -1; - } - - return 0; -} - -static void -madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -{ - struct hostapd_data *hapd = drv->hapd; - struct ieee80211req_wpaie ie; - int ielen = 0; - 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))) { - wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE", - __func__); - goto no_ie; - } - 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) - iebuf = NULL; - else - ielen += 2; - -no_ie: - drv_event_assoc(hapd, addr, iebuf, ielen, 0); - - 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)); - } -} - -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) { - union wpa_event_data data; - os_memset(&data, 0, sizeof(data)); - data.michael_mic_failure.unicast = 1; - data.michael_mic_failure.src = addr; - wpa_supplicant_event(drv->hapd, - EVENT_MICHAEL_MIC_FAILURE, &data); - } 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: - drv_event_disassoc(drv->hapd, - (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(void *ctx, struct ifinfomsg *ifi, - u8 *buf, size_t len) -{ - struct madwifi_driver_data *drv = ctx; - int attrlen, rta_len; - struct rtattr *attr; - - if (ifi->ifi_index != drv->ifindex) - return; - - attrlen = len; - attr = (struct rtattr *) buf; - - 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 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(struct madwifi_driver_data *drv) -{ - struct netlink_config *cfg; - - madwifi_get_we_version(drv); - - cfg = os_zalloc(sizeof(*cfg)); - if (cfg == NULL) - return -1; - cfg->ctx = drv; - cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; - drv->netlink = netlink_init(cfg); - if (drv->netlink == NULL) { - os_free(cfg); - return -1; - } - - return 0; -} - - -static int -madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, - int encrypt, const u8 *own_addr, u32 flags) -{ - struct madwifi_driver_data *drv = priv; - 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; - drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), - len - sizeof(struct l2_ethhdr)); -} - -static void * -madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) -{ - struct madwifi_driver_data *drv; - struct ifreq ifr; - struct iwreq iwr; - char brname[IFNAMSIZ]; - - 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, params->ifname, 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, params->own_addr)) - goto bad; - if (params->bridge[0]) { - wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", - params->bridge[0]); - drv->sock_recv = l2_packet_init(params->bridge[0], NULL, - ETH_P_EAPOL, handle_read, drv, - 1); - if (drv->sock_recv == NULL) - goto bad; - } else if (linux_br_get(brname, drv->iface) == 0) { - wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " - "EAPOL receive", brname); - drv->sock_recv = l2_packet_init(brname, 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; - } - - /* mark down during setup */ - linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); - madwifi_set_privacy(drv, 0); /* default to no privacy */ - - madwifi_receive_probe_req(drv); - - if (madwifi_wireless_event_init(drv)) - goto bad; - - 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; - - netlink_deinit(drv->netlink); - (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 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(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(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) -{ - struct madwifi_driver_data *drv = priv; - return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); -} - - -const struct wpa_driver_ops wpa_driver_madwifi_ops = { - .name = "madwifi", - .desc = "MADWIFI 802.11 support (Atheros, etc.)", - .set_key = wpa_driver_madwifi_set_key, - .hapd_init = madwifi_init, - .hapd_deinit = madwifi_deinit, - .set_ieee8021x = madwifi_set_ieee8021x, - .set_privacy = madwifi_set_privacy, - .get_seqnum = madwifi_get_seqnum, - .flush = madwifi_flush, - .set_generic_elem = madwifi_set_opt_ie, - .sta_set_flags = madwifi_sta_set_flags, - .read_sta_data = madwifi_read_sta_driver_data, - .hapd_send_eapol = madwifi_send_eapol, - .sta_disassoc = madwifi_sta_disassoc, - .sta_deauth = madwifi_sta_deauth, - .hapd_set_ssid = madwifi_set_ssid, - .hapd_get_ssid = madwifi_get_ssid, - .hapd_set_countermeasures = madwifi_set_countermeasures, - .sta_clear_stats = madwifi_sta_clear_stats, - .commit = madwifi_commit, - .set_ap_wps_ie = madwifi_set_ap_wps_ie, - .set_freq = madwifi_set_freq, -}; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 4c8f29f3..8527e90c 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -11,67 +11,35 @@ */ #include "includes.h" -#include <sys/ioctl.h> #include <sys/types.h> -#include <sys/stat.h> #include <fcntl.h> #include <net/if.h> #include <netlink/genl/genl.h> -#include <netlink/genl/family.h> #include <netlink/genl/ctrl.h> #ifdef CONFIG_LIBNL3_ROUTE #include <netlink/route/neighbour.h> #endif /* CONFIG_LIBNL3_ROUTE */ #include <linux/rtnetlink.h> #include <netpacket/packet.h> -#include <linux/filter.h> #include <linux/errqueue.h> -#include "nl80211_copy.h" #include "common.h" #include "eloop.h" -#include "utils/list.h" #include "common/qca-vendor.h" #include "common/qca-vendor-attr.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "l2_packet/l2_packet.h" #include "netlink.h" +#include "linux_defines.h" #include "linux_ioctl.h" #include "radiotap.h" #include "radiotap_iter.h" #include "rfkill.h" -#include "driver.h" - -#ifndef SO_WIFI_STATUS -# if defined(__sparc__) -# define SO_WIFI_STATUS 0x0025 -# elif defined(__parisc__) -# define SO_WIFI_STATUS 0x4022 -# else -# define SO_WIFI_STATUS 41 -# endif - -# define SCM_WIFI_STATUS SO_WIFI_STATUS -#endif - -#ifndef SO_EE_ORIGIN_TXSTATUS -#define SO_EE_ORIGIN_TXSTATUS 4 -#endif +#include "driver_nl80211.h" -#ifndef PACKET_TX_TIMESTAMP -#define PACKET_TX_TIMESTAMP 16 -#endif -#ifdef ANDROID -#include "android_drv.h" -#endif /* ANDROID */ -#ifdef CONFIG_LIBNL20 -/* libnl 2.0 compatibility code */ -#define nl_handle nl_sock -#define nl80211_handle_alloc nl_socket_alloc_cb -#define nl80211_handle_destroy nl_socket_free -#else +#ifndef CONFIG_LIBNL20 /* * libnl 1.1 has a bug, it tries to allocate socket numbers densely * but when you free a socket again it will mess up its bitmap and @@ -116,12 +84,10 @@ static void nl80211_handle_destroy(struct nl_handle *handle) #ifdef ANDROID /* system/core/libnl_2 does not include nl_socket_set_nonblocking() */ -static int android_nl_socket_set_nonblocking(struct nl_handle *handle) -{ - return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); -} #undef nl_socket_set_nonblocking #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h) + +#define genl_ctrl_resolve android_genl_ctrl_resolve #endif /* ANDROID */ @@ -181,374 +147,41 @@ static void nl80211_destroy_eloop_handle(struct nl_handle **handle) } -#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 nl80211_global { - struct dl_list interfaces; - int if_add_ifindex; - u64 if_add_wdevid; - int if_add_wdevid_set; - struct netlink_data *netlink; - struct nl_cb *nl_cb; - struct nl_handle *nl; - int nl80211_id; - int ioctl_sock; /* socket for ioctl() use */ - - struct nl_handle *nl_event; -}; - -struct nl80211_wiphy_data { - struct dl_list list; - struct dl_list bsss; - struct dl_list drvs; - - struct nl_handle *nl_beacons; - struct nl_cb *nl_cb; - - int wiphy_idx; -}; - static void nl80211_global_deinit(void *priv); -struct i802_bss { - struct wpa_driver_nl80211_data *drv; - struct i802_bss *next; - int ifindex; - u64 wdev_id; - char ifname[IFNAMSIZ + 1]; - char brname[IFNAMSIZ]; - unsigned int beacon_set:1; - unsigned int added_if_into_bridge:1; - unsigned int added_bridge:1; - unsigned int in_deinit:1; - unsigned int wdev_id_set:1; - unsigned int added_if:1; - unsigned int static_ap:1; - - u8 addr[ETH_ALEN]; - - int freq; - int bandwidth; - int if_dynamic; - - void *ctx; - struct nl_handle *nl_preq, *nl_mgmt; - struct nl_cb *nl_cb; - - struct nl80211_wiphy_data *wiphy_data; - struct dl_list wiphy_list; -}; - -struct wpa_driver_nl80211_data { - struct nl80211_global *global; - struct dl_list list; - struct dl_list wiphy_list; - char phyname[32]; - u8 perm_addr[ETH_ALEN]; - void *ctx; - int ifindex; - int if_removed; - int if_disabled; - int ignore_if_down_event; - struct rfkill_data *rfkill; - struct wpa_driver_capa capa; - u8 *extended_capa, *extended_capa_mask; - unsigned int extended_capa_len; - int has_capability; - - int operstate; - - int scan_complete_events; - enum scan_states { - NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, - SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, - SCHED_SCAN_RESULTS - } scan_state; - - struct nl_cb *nl_cb; - - u8 auth_bssid[ETH_ALEN]; - u8 auth_attempt_bssid[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - u8 prev_bssid[ETH_ALEN]; - int associated; - u8 ssid[32]; - size_t ssid_len; - enum nl80211_iftype nlmode; - enum nl80211_iftype ap_scan_as_station; - unsigned int assoc_freq; - - int monitor_sock; - int monitor_ifidx; - int monitor_refcount; - - unsigned int disabled_11b_rates:1; - unsigned int pending_remain_on_chan:1; - unsigned int in_interface_list:1; - unsigned int device_ap_sme:1; - unsigned int poll_command_supported:1; - unsigned int data_tx_status:1; - unsigned int scan_for_auth:1; - unsigned int retry_auth:1; - unsigned int use_monitor:1; - unsigned int ignore_next_local_disconnect:1; - unsigned int ignore_next_local_deauth:1; - unsigned int allow_p2p_device:1; - unsigned int hostapd:1; - unsigned int start_mode_ap:1; - unsigned int start_iface_up:1; - unsigned int test_use_roc_tx:1; - unsigned int ignore_deauth_event:1; - unsigned int roaming_vendor_cmd_avail:1; - unsigned int dfs_vendor_cmd_avail:1; - unsigned int have_low_prio_scan:1; - unsigned int force_connect_cmd:1; - unsigned int addr_changed:1; - - u64 remain_on_chan_cookie; - u64 send_action_cookie; - - unsigned int last_mgmt_freq; - - struct wpa_driver_scan_filter *filter_ssids; - size_t num_filter_ssids; - - struct i802_bss *first_bss; - - int eapol_tx_sock; - - int eapol_sock; /* socket for EAPOL frames */ - - struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ - - int default_if_indices[16]; - int *if_indices; - int num_if_indices; - - /* From failed authentication command */ - int auth_freq; - u8 auth_bssid_[ETH_ALEN]; - u8 auth_ssid[32]; - size_t auth_ssid_len; - int auth_alg; - u8 *auth_ie; - size_t auth_ie_len; - u8 auth_wep_key[4][16]; - size_t auth_wep_key_len[4]; - int auth_wep_tx_keyidx; - int auth_local_state_change; - int auth_p2p; -}; - - static void wpa_driver_nl80211_deinit(struct i802_bss *bss); -static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, - void *timeout_ctx); -static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, - enum nl80211_iftype nlmode); static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss, struct hostapd_freq_params *freq); static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, - const u8 *set_addr, int first); -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code, - int local_state_change); -static void nl80211_remove_monitor_interface( - struct wpa_driver_nl80211_data *drv); + const u8 *set_addr, int first, + const char *driver_params); static int nl80211_send_frame_cmd(struct i802_bss *bss, unsigned int freq, unsigned int wait, const u8 *buf, size_t buf_len, u64 *cookie, int no_cck, int no_ack, int offchanok); -static int nl80211_register_frame(struct i802_bss *bss, - struct nl_handle *hl_handle, - u16 type, const u8 *match, size_t match_len); static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report); -#ifdef ANDROID -static int android_pno_start(struct i802_bss *bss, - struct wpa_driver_scan_params *params); -static int android_pno_stop(struct i802_bss *bss); -extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, - size_t buf_len); -#endif /* ANDROID */ -#ifdef ANDROID_P2P -#ifdef ANDROID_P2P_STUB -int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) { - return 0; -} -int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) { - return 0; -} -int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) { - return -1; -} -int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) { - return 0; -} -#else /* ANDROID_P2P_STUB */ -int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); -int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); -int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); -int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp); -#endif /* ANDROID_P2P_STUB */ -#endif /* ANDROID_P2P */ static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx); -static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, - enum wpa_driver_if_type type, - const char *ifname); static int nl80211_set_channel(struct i802_bss *bss, struct hostapd_freq_params *freq, int set_chan); static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, int ifindex, int disabled); -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv); -static int wpa_driver_nl80211_authenticate_retry( - struct wpa_driver_nl80211_data *drv); +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, + int reset_mode); -static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); static int i802_set_iface_flags(struct i802_bss *bss, int up); - - -static const char * nl80211_command_to_string(enum nl80211_commands cmd) -{ -#define C2S(x) case x: return #x; - switch (cmd) { - C2S(NL80211_CMD_UNSPEC) - C2S(NL80211_CMD_GET_WIPHY) - C2S(NL80211_CMD_SET_WIPHY) - C2S(NL80211_CMD_NEW_WIPHY) - C2S(NL80211_CMD_DEL_WIPHY) - C2S(NL80211_CMD_GET_INTERFACE) - C2S(NL80211_CMD_SET_INTERFACE) - C2S(NL80211_CMD_NEW_INTERFACE) - C2S(NL80211_CMD_DEL_INTERFACE) - C2S(NL80211_CMD_GET_KEY) - C2S(NL80211_CMD_SET_KEY) - C2S(NL80211_CMD_NEW_KEY) - C2S(NL80211_CMD_DEL_KEY) - C2S(NL80211_CMD_GET_BEACON) - C2S(NL80211_CMD_SET_BEACON) - C2S(NL80211_CMD_START_AP) - C2S(NL80211_CMD_STOP_AP) - C2S(NL80211_CMD_GET_STATION) - C2S(NL80211_CMD_SET_STATION) - C2S(NL80211_CMD_NEW_STATION) - C2S(NL80211_CMD_DEL_STATION) - C2S(NL80211_CMD_GET_MPATH) - C2S(NL80211_CMD_SET_MPATH) - C2S(NL80211_CMD_NEW_MPATH) - C2S(NL80211_CMD_DEL_MPATH) - C2S(NL80211_CMD_SET_BSS) - C2S(NL80211_CMD_SET_REG) - C2S(NL80211_CMD_REQ_SET_REG) - C2S(NL80211_CMD_GET_MESH_CONFIG) - C2S(NL80211_CMD_SET_MESH_CONFIG) - C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) - C2S(NL80211_CMD_GET_REG) - C2S(NL80211_CMD_GET_SCAN) - C2S(NL80211_CMD_TRIGGER_SCAN) - C2S(NL80211_CMD_NEW_SCAN_RESULTS) - C2S(NL80211_CMD_SCAN_ABORTED) - C2S(NL80211_CMD_REG_CHANGE) - C2S(NL80211_CMD_AUTHENTICATE) - C2S(NL80211_CMD_ASSOCIATE) - C2S(NL80211_CMD_DEAUTHENTICATE) - C2S(NL80211_CMD_DISASSOCIATE) - C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) - C2S(NL80211_CMD_REG_BEACON_HINT) - C2S(NL80211_CMD_JOIN_IBSS) - C2S(NL80211_CMD_LEAVE_IBSS) - C2S(NL80211_CMD_TESTMODE) - C2S(NL80211_CMD_CONNECT) - C2S(NL80211_CMD_ROAM) - C2S(NL80211_CMD_DISCONNECT) - C2S(NL80211_CMD_SET_WIPHY_NETNS) - C2S(NL80211_CMD_GET_SURVEY) - C2S(NL80211_CMD_NEW_SURVEY_RESULTS) - C2S(NL80211_CMD_SET_PMKSA) - C2S(NL80211_CMD_DEL_PMKSA) - C2S(NL80211_CMD_FLUSH_PMKSA) - C2S(NL80211_CMD_REMAIN_ON_CHANNEL) - C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) - C2S(NL80211_CMD_SET_TX_BITRATE_MASK) - C2S(NL80211_CMD_REGISTER_FRAME) - C2S(NL80211_CMD_FRAME) - C2S(NL80211_CMD_FRAME_TX_STATUS) - C2S(NL80211_CMD_SET_POWER_SAVE) - C2S(NL80211_CMD_GET_POWER_SAVE) - C2S(NL80211_CMD_SET_CQM) - C2S(NL80211_CMD_NOTIFY_CQM) - C2S(NL80211_CMD_SET_CHANNEL) - C2S(NL80211_CMD_SET_WDS_PEER) - C2S(NL80211_CMD_FRAME_WAIT_CANCEL) - C2S(NL80211_CMD_JOIN_MESH) - C2S(NL80211_CMD_LEAVE_MESH) - C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) - C2S(NL80211_CMD_UNPROT_DISASSOCIATE) - C2S(NL80211_CMD_NEW_PEER_CANDIDATE) - C2S(NL80211_CMD_GET_WOWLAN) - C2S(NL80211_CMD_SET_WOWLAN) - C2S(NL80211_CMD_START_SCHED_SCAN) - C2S(NL80211_CMD_STOP_SCHED_SCAN) - C2S(NL80211_CMD_SCHED_SCAN_RESULTS) - C2S(NL80211_CMD_SCHED_SCAN_STOPPED) - C2S(NL80211_CMD_SET_REKEY_OFFLOAD) - C2S(NL80211_CMD_PMKSA_CANDIDATE) - C2S(NL80211_CMD_TDLS_OPER) - C2S(NL80211_CMD_TDLS_MGMT) - C2S(NL80211_CMD_UNEXPECTED_FRAME) - C2S(NL80211_CMD_PROBE_CLIENT) - C2S(NL80211_CMD_REGISTER_BEACONS) - C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) - C2S(NL80211_CMD_SET_NOACK_MAP) - C2S(NL80211_CMD_CH_SWITCH_NOTIFY) - C2S(NL80211_CMD_START_P2P_DEVICE) - C2S(NL80211_CMD_STOP_P2P_DEVICE) - C2S(NL80211_CMD_CONN_FAILED) - C2S(NL80211_CMD_SET_MCAST_RATE) - C2S(NL80211_CMD_SET_MAC_ACL) - C2S(NL80211_CMD_RADAR_DETECT) - C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) - C2S(NL80211_CMD_UPDATE_FT_IES) - C2S(NL80211_CMD_FT_EVENT) - C2S(NL80211_CMD_CRIT_PROTOCOL_START) - C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) - C2S(NL80211_CMD_GET_COALESCE) - C2S(NL80211_CMD_SET_COALESCE) - C2S(NL80211_CMD_CHANNEL_SWITCH) - C2S(NL80211_CMD_VENDOR) - C2S(NL80211_CMD_SET_QOS_MAP) - default: - return "NL80211_CMD_UNKNOWN"; - } -#undef C2S -} +static int nl80211_set_param(void *priv, const char *param); /* Converts nl80211_chan_width to a common format */ -static enum chan_width convert2width(int width) +enum chan_width convert2width(int width) { switch (width) { case NL80211_CHAN_WIDTH_20_NOHT: @@ -568,14 +201,14 @@ static enum chan_width convert2width(int width) } -static int is_ap_interface(enum nl80211_iftype nlmode) +int is_ap_interface(enum nl80211_iftype nlmode) { return nlmode == NL80211_IFTYPE_AP || nlmode == NL80211_IFTYPE_P2P_GO; } -static int is_sta_interface(enum nl80211_iftype nlmode) +int is_sta_interface(enum nl80211_iftype nlmode) { return nlmode == NL80211_IFTYPE_STATION || nlmode == NL80211_IFTYPE_P2P_CLIENT; @@ -589,8 +222,8 @@ static int is_p2p_net_interface(enum nl80211_iftype nlmode) } -static struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, - int ifindex) +struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex) { struct i802_bss *bss; @@ -603,7 +236,13 @@ static struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, } -static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) +static int is_mesh_interface(enum nl80211_iftype nlmode) +{ + return nlmode == NL80211_IFTYPE_MESH_POINT; +} + + +void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) { if (drv->associated) os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); @@ -612,17 +251,6 @@ static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv) } -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; - unsigned int assoc_freq; - unsigned int ibss_freq; - u8 assoc_bssid[ETH_ALEN]; -}; - -static int bss_info_handler(struct nl_msg *msg, void *arg); - - /* nl80211 code */ static int ack_handler(struct nl_msg *msg, void *arg) { @@ -653,6 +281,28 @@ static int no_seq_check(struct nl_msg *msg, void *arg) } +static void nl80211_nlmsg_clear(struct nl_msg *msg) +{ + /* + * Clear nlmsg data, e.g., to make sure key material is not left in + * heap memory for unnecessarily long time. + */ + if (msg) { + struct nlmsghdr *hdr = nlmsg_hdr(msg); + void *data = nlmsg_data(hdr); + /* + * This would use nlmsg_datalen() or the older nlmsg_len() if + * only libnl were to maintain a stable API.. Neither will work + * with all released versions, so just calculate the length + * here. + */ + int len = hdr->nlmsg_len - NLMSG_HDRLEN; + + os_memset(data, 0, len); + } +} + + static int send_and_recv(struct nl80211_global *global, struct nl_handle *nl_handle, struct nl_msg *msg, int (*valid_handler)(struct nl_msg *, void *), @@ -661,6 +311,9 @@ static int send_and_recv(struct nl80211_global *global, struct nl_cb *cb; int err = -ENOMEM; + if (!msg) + return -ENOMEM; + cb = nl_cb_clone(global->nl_cb); if (!cb) goto out; @@ -689,25 +342,17 @@ static int send_and_recv(struct nl80211_global *global, } out: nl_cb_put(cb); + if (!valid_handler && valid_data == (void *) -1) + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return err; } -static int send_and_recv_msgs_global(struct nl80211_global *global, - struct nl_msg *msg, - int (*valid_handler)(struct nl_msg *, void *), - void *valid_data) -{ - return send_and_recv(global, global->nl, msg, valid_handler, - valid_data); -} - - -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) +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) { return send_and_recv(drv->global, drv->global->nl, msg, valid_handler, valid_data); @@ -720,19 +365,6 @@ struct family_data { }; -static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) -{ - if (bss->wdev_id_set) - NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); - else - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - return 0; - -nla_put_failure: - return -1; -} - - static int family_handler(struct nl_msg *msg, void *arg) { struct family_data *res = arg; @@ -768,35 +400,93 @@ static int nl_get_multicast_id(struct nl80211_global *global, const char *family, const char *group) { struct nl_msg *msg; - int ret = -1; + int ret; struct family_data res = { group, -ENOENT }; msg = nlmsg_alloc(); if (!msg) return -ENOMEM; - genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), - 0, 0, CTRL_CMD_GETFAMILY, 0); - NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0) || + nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) { + nlmsg_free(msg); + return -1; + } - ret = send_and_recv_msgs_global(global, msg, family_handler, &res); - msg = NULL; + ret = send_and_recv(global, global->nl, msg, family_handler, &res); if (ret == 0) ret = res.id; - -nla_put_failure: - nlmsg_free(msg); return ret; } -static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, - struct nl_msg *msg, int flags, uint8_t cmd) +void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd) { return genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, flags, cmd, 0); } +static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss) +{ + if (bss->wdev_id_set) + return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); +} + + +struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (!nl80211_cmd(bss->drv, msg, flags, cmd) || + nl80211_set_iface_id(msg, bss) < 0) { + nlmsg_free(msg); + return NULL; + } + + return msg; +} + + +static struct nl_msg * +nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex, + int flags, uint8_t cmd) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (!nl80211_cmd(drv, msg, flags, cmd) || + nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) { + nlmsg_free(msg); + return NULL; + } + + return msg; +} + + +struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, + uint8_t cmd) +{ + return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd); +} + + +struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd) +{ + return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd); +} + + struct wiphy_idx_data { int wiphy_idx; enum nl80211_iftype nlmode; @@ -827,7 +517,7 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg) } -static int nl80211_get_wiphy_index(struct i802_bss *bss) +int nl80211_get_wiphy_index(struct i802_bss *bss) { struct nl_msg *msg; struct wiphy_idx_data data = { @@ -835,20 +525,11 @@ static int nl80211_get_wiphy_index(struct i802_bss *bss) .macaddr = NULL, }; - msg = nlmsg_alloc(); - if (!msg) - return NL80211_IFTYPE_UNSPECIFIED; - - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) + return -1; if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.wiphy_idx; - msg = NULL; -nla_put_failure: - nlmsg_free(msg); return -1; } @@ -861,20 +542,11 @@ static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss) .macaddr = NULL, }; - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) + return NL80211_IFTYPE_UNSPECIFIED; if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0) return data.nlmode; - msg = NULL; -nla_put_failure: - nlmsg_free(msg); return NL80211_IFTYPE_UNSPECIFIED; } @@ -886,19 +558,10 @@ static int nl80211_get_macaddr(struct i802_bss *bss) .macaddr = bss->addr, }; - msg = nlmsg_alloc(); - if (!msg) - return NL80211_IFTYPE_UNSPECIFIED; - - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE); - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE))) + return -1; return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data); - -nla_put_failure: - nlmsg_free(msg); - return NL80211_IFTYPE_UNSPECIFIED; } @@ -906,27 +569,24 @@ static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv, struct nl80211_wiphy_data *w) { struct nl_msg *msg; - int ret = -1; + int ret; msg = nlmsg_alloc(); if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS); - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx); + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) || + nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) { + nlmsg_free(msg); + return -1; + } ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register beacons command " "failed: ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; } - ret = 0; -nla_put_failure: - nlmsg_free(msg); return ret; } @@ -1104,7 +764,7 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) static void wpa_driver_nl80211_event_newlink( - struct wpa_driver_nl80211_data *drv, char *ifname) + struct wpa_driver_nl80211_data *drv, const char *ifname) { union wpa_event_data event; @@ -1130,7 +790,7 @@ static void wpa_driver_nl80211_event_newlink( static void wpa_driver_nl80211_event_dellink( - struct wpa_driver_nl80211_data *drv, char *ifname) + struct wpa_driver_nl80211_data *drv, const char *ifname) { union wpa_event_data event; @@ -1190,7 +850,7 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) { wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - wpa_driver_nl80211_finish_drv_init(drv, NULL, 0); + wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL); return 1; } @@ -1281,6 +941,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, drv->first_bss->ifname) > 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); + drv->ignore_if_down_event = 0; return; } wpa_printf(MSG_DEBUG, "nl80211: Interface down"); @@ -1368,11 +1029,25 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, wpa_driver_nl80211_event_newlink(drv, ifname); if (ifi->ifi_family == AF_BRIDGE && brid) { + struct i802_bss *bss; + /* device has been added to bridge */ - if_indextoname(brid, namebuf); + if (!if_indextoname(brid, namebuf)) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not find bridge ifname for ifindex %u", + brid); + return; + } wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s", brid, namebuf); add_ifidx(drv, brid); + + for (bss = drv->first_bss; bss; bss = bss->next) { + if (os_strcmp(ifname, bss->ifname) == 0) { + os_strlcpy(bss->brname, namebuf, IFNAMSIZ); + break; + } + } } } @@ -1442,73 +1117,31 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, if (ifi->ifi_family == AF_BRIDGE && brid) { /* device has been removed from bridge */ char namebuf[IFNAMSIZ]; - if_indextoname(brid, namebuf); - wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge " - "%s", brid, namebuf); - del_ifidx(drv, brid); - } -} - - -static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len) -{ - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && - drv->force_connect_cmd) { - /* - * Avoid reporting two association events that would confuse - * the core code. - */ - wpa_printf(MSG_DEBUG, - "nl80211: Ignore auth event when using driver SME"); - return; - } - - wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24 + sizeof(mgmt->u.auth)) { - wpa_printf(MSG_DEBUG, "nl80211: Too short association event " - "frame"); - return; - } - - os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); - os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); - event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); - event.auth.auth_transaction = - le_to_host16(mgmt->u.auth.auth_transaction); - event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); - if (len > 24 + sizeof(mgmt->u.auth)) { - event.auth.ies = mgmt->u.auth.variable; - event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); + if (!if_indextoname(brid, namebuf)) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not find bridge ifname for ifindex %u", + brid); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Remove ifindex %u for bridge %s", + brid, namebuf); + } + del_ifidx(drv, brid); } - - wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); } -static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) +unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret; struct nl80211_bss_info_arg arg; + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); os_memset(&arg, 0, sizeof(arg)); - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - arg.drv = drv; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); - msg = NULL; if (ret == 0) { unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ? arg.ibss_freq : arg.assoc_freq; @@ -1520,804 +1153,10 @@ static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " "(%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return drv->assoc_freq; } -static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, - const u8 *frame, size_t len) -{ - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 status; - - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && - drv->force_connect_cmd) { - /* - * Avoid reporting two association events that would confuse - * the core code. - */ - wpa_printf(MSG_DEBUG, - "nl80211: Ignore assoc event when using driver SME"); - return; - } - - wpa_printf(MSG_DEBUG, "nl80211: Associate event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24 + sizeof(mgmt->u.assoc_resp)) { - wpa_printf(MSG_DEBUG, "nl80211: Too short association event " - "frame"); - return; - } - - status = le_to_host16(mgmt->u.assoc_resp.status_code); - if (status != WLAN_STATUS_SUCCESS) { - os_memset(&event, 0, sizeof(event)); - event.assoc_reject.bssid = mgmt->bssid; - if (len > 24 + sizeof(mgmt->u.assoc_resp)) { - event.assoc_reject.resp_ies = - (u8 *) mgmt->u.assoc_resp.variable; - event.assoc_reject.resp_ies_len = - len - 24 - sizeof(mgmt->u.assoc_resp); - } - event.assoc_reject.status_code = status; - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); - return; - } - - drv->associated = 1; - os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); - os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); - - os_memset(&event, 0, sizeof(event)); - if (len > 24 + sizeof(mgmt->u.assoc_resp)) { - event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; - event.assoc_info.resp_ies_len = - len - 24 - sizeof(mgmt->u.assoc_resp); - } - - event.assoc_info.freq = drv->assoc_freq; - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); -} - - -static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, - enum nl80211_commands cmd, struct nlattr *status, - struct nlattr *addr, struct nlattr *req_ie, - struct nlattr *resp_ie) -{ - union wpa_event_data event; - u16 status_code; - - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two association events that would confuse - * the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " - "when using userspace SME", cmd); - return; - } - - status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; - - if (cmd == NL80211_CMD_CONNECT) { - wpa_printf(MSG_DEBUG, - "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)", - status_code, drv->ignore_next_local_disconnect); - } else if (cmd == NL80211_CMD_ROAM) { - wpa_printf(MSG_DEBUG, "nl80211: Roam event"); - } - - os_memset(&event, 0, sizeof(event)); - if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) { - if (addr) - event.assoc_reject.bssid = nla_data(addr); - if (drv->ignore_next_local_disconnect) { - drv->ignore_next_local_disconnect = 0; - if (!event.assoc_reject.bssid || - (os_memcmp(event.assoc_reject.bssid, - drv->auth_attempt_bssid, - ETH_ALEN) != 0)) { - /* - * Ignore the event that came without a BSSID or - * for the old connection since this is likely - * not relevant to the new Connect command. - */ - wpa_printf(MSG_DEBUG, - "nl80211: Ignore connection failure event triggered during reassociation"); - return; - } - } - if (resp_ie) { - event.assoc_reject.resp_ies = nla_data(resp_ie); - event.assoc_reject.resp_ies_len = nla_len(resp_ie); - } - event.assoc_reject.status_code = status_code; - wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); - return; - } - - drv->associated = 1; - if (addr) { - os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); - os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); - } - - if (req_ie) { - event.assoc_info.req_ies = nla_data(req_ie); - event.assoc_info.req_ies_len = nla_len(req_ie); - } - if (resp_ie) { - event.assoc_info.resp_ies = nla_data(resp_ie); - event.assoc_info.resp_ies_len = nla_len(resp_ie); - } - - event.assoc_info.freq = nl80211_get_assoc_freq(drv); - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); -} - - -static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, - struct nlattr *reason, struct nlattr *addr, - struct nlattr *by_ap) -{ - union wpa_event_data data; - unsigned int locally_generated = by_ap == NULL; - - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - /* - * Avoid reporting two disassociation events that could - * confuse the core code. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event when using userspace SME"); - return; - } - - if (drv->ignore_next_local_disconnect) { - drv->ignore_next_local_disconnect = 0; - if (locally_generated) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " - "event triggered during reassociation"); - return; - } - wpa_printf(MSG_WARNING, "nl80211: Was expecting local " - "disconnect but got another disconnect " - "event first"); - } - - wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); - nl80211_mark_disconnected(drv); - os_memset(&data, 0, sizeof(data)); - if (reason) - data.deauth_info.reason_code = nla_get_u16(reason); - data.deauth_info.locally_generated = by_ap == NULL; - wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); -} - - -static int calculate_chan_offset(int width, int freq, int cf1, int cf2) -{ - int freq1 = 0; - - switch (convert2width(width)) { - case CHAN_WIDTH_20_NOHT: - case CHAN_WIDTH_20: - return 0; - case CHAN_WIDTH_40: - freq1 = cf1 - 10; - break; - case CHAN_WIDTH_80: - freq1 = cf1 - 30; - break; - case CHAN_WIDTH_160: - freq1 = cf1 - 70; - break; - case CHAN_WIDTH_UNKNOWN: - case CHAN_WIDTH_80P80: - /* FIXME: implement this */ - return 0; - } - - return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; -} - - -static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, - struct nlattr *ifindex, struct nlattr *freq, - struct nlattr *type, struct nlattr *bw, - struct nlattr *cf1, struct nlattr *cf2) -{ - struct i802_bss *bss; - union wpa_event_data data; - int ht_enabled = 1; - int chan_offset = 0; - int ifidx; - - wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); - - if (!freq) - return; - - ifidx = nla_get_u32(ifindex); - bss = get_bss_ifindex(drv, ifidx); - if (bss == NULL) { - wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", - ifidx); - return; - } - - if (type) { - switch (nla_get_u32(type)) { - case NL80211_CHAN_NO_HT: - ht_enabled = 0; - break; - case NL80211_CHAN_HT20: - break; - case NL80211_CHAN_HT40PLUS: - chan_offset = 1; - break; - case NL80211_CHAN_HT40MINUS: - chan_offset = -1; - break; - } - } else if (bw && cf1) { - /* This can happen for example with VHT80 ch switch */ - chan_offset = calculate_chan_offset(nla_get_u32(bw), - nla_get_u32(freq), - nla_get_u32(cf1), - cf2 ? nla_get_u32(cf2) : 0); - } else { - wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); - } - - os_memset(&data, 0, sizeof(data)); - data.ch_switch.freq = nla_get_u32(freq); - data.ch_switch.ht_enabled = ht_enabled; - data.ch_switch.ch_offset = chan_offset; - if (bw) - data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); - if (cf1) - data.ch_switch.cf1 = nla_get_u32(cf1); - if (cf2) - data.ch_switch.cf2 = nla_get_u32(cf2); - - bss->freq = data.ch_switch.freq; - - wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); -} - - -static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, - enum nl80211_commands cmd, struct nlattr *addr) -{ - union wpa_event_data event; - enum wpa_event_type ev; - - if (nla_len(addr) != ETH_ALEN) - return; - - wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, - cmd, MAC2STR((u8 *) nla_data(addr))); - - if (cmd == NL80211_CMD_AUTHENTICATE) - ev = EVENT_AUTH_TIMED_OUT; - else if (cmd == NL80211_CMD_ASSOCIATE) - ev = EVENT_ASSOC_TIMED_OUT; - else - return; - - os_memset(&event, 0, sizeof(event)); - os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); - wpa_supplicant_event(drv->ctx, ev, &event); -} - - -static void mlme_event_mgmt(struct i802_bss *bss, - struct nlattr *freq, struct nlattr *sig, - const u8 *frame, size_t len) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 fc, stype; - int ssi_signal = 0; - int rx_freq = 0; - - wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); - mgmt = (const struct ieee80211_mgmt *) frame; - if (len < 24) { - wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); - return; - } - - fc = le_to_host16(mgmt->frame_control); - stype = WLAN_FC_GET_STYPE(fc); - - if (sig) - ssi_signal = (s32) nla_get_u32(sig); - - os_memset(&event, 0, sizeof(event)); - if (freq) { - event.rx_mgmt.freq = nla_get_u32(freq); - rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; - } - wpa_printf(MSG_DEBUG, - "nl80211: RX frame sa=" MACSTR - " freq=%d ssi_signal=%d stype=%u (%s) len=%u", - MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc), - (unsigned int) len); - event.rx_mgmt.frame = frame; - event.rx_mgmt.frame_len = len; - event.rx_mgmt.ssi_signal = ssi_signal; - event.rx_mgmt.drv_priv = bss; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); -} - - -static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, - struct nlattr *cookie, const u8 *frame, - size_t len, struct nlattr *ack) -{ - union wpa_event_data event; - const struct ieee80211_hdr *hdr; - u16 fc; - - wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); - if (!is_ap_interface(drv->nlmode)) { - u64 cookie_val; - - if (!cookie) - return; - - cookie_val = nla_get_u64(cookie); - wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" - " cookie=0%llx%s (ack=%d)", - (long long unsigned int) cookie_val, - cookie_val == drv->send_action_cookie ? - " (match)" : " (unknown)", ack != NULL); - if (cookie_val != drv->send_action_cookie) - return; - } - - hdr = (const struct ieee80211_hdr *) frame; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = frame; - event.tx_status.data_len = len; - event.tx_status.ack = ack != NULL; - wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); -} - - -static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, - enum wpa_event_type type, - const u8 *frame, size_t len) -{ - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - const u8 *bssid = NULL; - u16 reason_code = 0; - - if (type == EVENT_DEAUTH) - wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); - else - wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); - - mgmt = (const struct ieee80211_mgmt *) frame; - if (len >= 24) { - bssid = mgmt->bssid; - - if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && - !drv->associated && - os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && - os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && - os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { - /* - * Avoid issues with some roaming cases where - * disconnection event for the old AP may show up after - * we have started connection with the new AP. - */ - wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, - MAC2STR(bssid), - MAC2STR(drv->auth_attempt_bssid)); - return; - } - - if (drv->associated != 0 && - os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && - os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { - /* - * We have presumably received this deauth as a - * response to a clear_state_mismatch() outgoing - * deauth. Don't let it take us offline! - */ - wpa_printf(MSG_DEBUG, "nl80211: Deauth received " - "from Unknown BSSID " MACSTR " -- ignoring", - MAC2STR(bssid)); - return; - } - } - - nl80211_mark_disconnected(drv); - os_memset(&event, 0, sizeof(event)); - - /* Note: Same offset for Reason Code in both frame subtypes */ - if (len >= 24 + sizeof(mgmt->u.deauth)) - reason_code = le_to_host16(mgmt->u.deauth.reason_code); - - if (type == EVENT_DISASSOC) { - event.disassoc_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); - event.disassoc_info.addr = bssid; - event.disassoc_info.reason_code = reason_code; - if (frame + len > mgmt->u.disassoc.variable) { - event.disassoc_info.ie = mgmt->u.disassoc.variable; - event.disassoc_info.ie_len = frame + len - - mgmt->u.disassoc.variable; - } - } else { - if (drv->ignore_deauth_event) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); - drv->ignore_deauth_event = 0; - return; - } - event.deauth_info.locally_generated = - !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); - if (drv->ignore_next_local_deauth) { - drv->ignore_next_local_deauth = 0; - if (event.deauth_info.locally_generated) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request"); - return; - } - wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first"); - } - event.deauth_info.addr = bssid; - event.deauth_info.reason_code = reason_code; - if (frame + len > mgmt->u.deauth.variable) { - event.deauth_info.ie = mgmt->u.deauth.variable; - event.deauth_info.ie_len = frame + len - - mgmt->u.deauth.variable; - } - } - - wpa_supplicant_event(drv->ctx, type, &event); -} - - -static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, - enum wpa_event_type type, - const u8 *frame, size_t len) -{ - const struct ieee80211_mgmt *mgmt; - union wpa_event_data event; - u16 reason_code = 0; - - if (type == EVENT_UNPROT_DEAUTH) - wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); - else - wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); - - if (len < 24) - return; - - mgmt = (const struct ieee80211_mgmt *) frame; - - os_memset(&event, 0, sizeof(event)); - /* Note: Same offset for Reason Code in both frame subtypes */ - if (len >= 24 + sizeof(mgmt->u.deauth)) - reason_code = le_to_host16(mgmt->u.deauth.reason_code); - - if (type == EVENT_UNPROT_DISASSOC) { - event.unprot_disassoc.sa = mgmt->sa; - event.unprot_disassoc.da = mgmt->da; - event.unprot_disassoc.reason_code = reason_code; - } else { - event.unprot_deauth.sa = mgmt->sa; - event.unprot_deauth.da = mgmt->da; - event.unprot_deauth.reason_code = reason_code; - } - - wpa_supplicant_event(drv->ctx, type, &event); -} - - -static void mlme_event(struct i802_bss *bss, - enum nl80211_commands cmd, struct nlattr *frame, - struct nlattr *addr, struct nlattr *timed_out, - struct nlattr *freq, struct nlattr *ack, - struct nlattr *cookie, struct nlattr *sig) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - const u8 *data; - size_t len; - - if (timed_out && addr) { - mlme_timeout_event(drv, cmd, addr); - return; - } - - if (frame == NULL) { - wpa_printf(MSG_DEBUG, - "nl80211: MLME event %d (%s) without frame data", - cmd, nl80211_command_to_string(cmd)); - return; - } - - data = nla_data(frame); - len = nla_len(frame); - if (len < 4 + 2 * ETH_ALEN) { - wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" - MACSTR ") - too short", - cmd, nl80211_command_to_string(cmd), bss->ifname, - MAC2STR(bss->addr)); - return; - } - wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR - ") A1=" MACSTR " A2=" MACSTR, cmd, - nl80211_command_to_string(cmd), bss->ifname, - MAC2STR(bss->addr), MAC2STR(data + 4), - MAC2STR(data + 4 + ETH_ALEN)); - if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && - os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && - os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { - wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " - "for foreign address", bss->ifname); - return; - } - wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", - nla_data(frame), nla_len(frame)); - - switch (cmd) { - case NL80211_CMD_AUTHENTICATE: - mlme_event_auth(drv, nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_ASSOCIATE: - mlme_event_assoc(drv, nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_DEAUTHENTICATE: - mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_DISASSOCIATE: - mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_FRAME: - mlme_event_mgmt(bss, freq, sig, nla_data(frame), - nla_len(frame)); - break; - case NL80211_CMD_FRAME_TX_STATUS: - mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), - nla_len(frame), ack); - break; - case NL80211_CMD_UNPROT_DEAUTHENTICATE: - mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, - nla_data(frame), nla_len(frame)); - break; - case NL80211_CMD_UNPROT_DISASSOCIATE: - mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, - nla_data(frame), nla_len(frame)); - break; - default: - break; - } -} - - -static void mlme_event_michael_mic_failure(struct i802_bss *bss, - struct nlattr *tb[]) -{ - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); - os_memset(&data, 0, sizeof(data)); - if (tb[NL80211_ATTR_MAC]) { - wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", - nla_data(tb[NL80211_ATTR_MAC]), - nla_len(tb[NL80211_ATTR_MAC])); - data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); - } - if (tb[NL80211_ATTR_KEY_SEQ]) { - wpa_hexdump(MSG_DEBUG, "nl80211: TSC", - nla_data(tb[NL80211_ATTR_KEY_SEQ]), - nla_len(tb[NL80211_ATTR_KEY_SEQ])); - } - if (tb[NL80211_ATTR_KEY_TYPE]) { - enum nl80211_key_type key_type = - nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); - wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); - if (key_type == NL80211_KEYTYPE_PAIRWISE) - data.michael_mic_failure.unicast = 1; - } else - data.michael_mic_failure.unicast = 1; - - if (tb[NL80211_ATTR_KEY_IDX]) { - u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); - wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); - } - - wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); -} - - -static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) -{ - unsigned int freq; - - if (tb[NL80211_ATTR_MAC] == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " - "event"); - return; - } - os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - - drv->associated = 1; - wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", - MAC2STR(drv->bssid)); - - freq = nl80211_get_assoc_freq(drv); - if (freq) { - wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz", - freq); - drv->first_bss->freq = freq; - } - - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); -} - - -static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, - int cancel_event, struct nlattr *tb[]) -{ - unsigned int freq, chan_type, duration; - union wpa_event_data data; - u64 cookie; - - if (tb[NL80211_ATTR_WIPHY_FREQ]) - freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); - else - freq = 0; - - if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) - chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - else - chan_type = 0; - - if (tb[NL80211_ATTR_DURATION]) - duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); - else - duration = 0; - - if (tb[NL80211_ATTR_COOKIE]) - cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); - else - cookie = 0; - - wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " - "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", - cancel_event, freq, chan_type, duration, - (long long unsigned int) cookie, - cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); - - if (cookie != drv->remain_on_chan_cookie) - return; /* not for us */ - - if (cancel_event) - drv->pending_remain_on_chan = 0; - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = freq; - data.remain_on_channel.duration = duration; - wpa_supplicant_event(drv->ctx, cancel_event ? - EVENT_CANCEL_REMAIN_ON_CHANNEL : - EVENT_REMAIN_ON_CHANNEL, &data); -} - - -static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) -{ - union wpa_event_data data; - - os_memset(&data, 0, sizeof(data)); - - if (tb[NL80211_ATTR_IE]) { - data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); - data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); - } - - if (tb[NL80211_ATTR_IE_RIC]) { - data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); - data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); - } - - if (tb[NL80211_ATTR_MAC]) - os_memcpy(data.ft_ies.target_ap, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - - wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, - MAC2STR(data.ft_ies.target_ap)); - - wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); -} - - -static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, - struct nlattr *tb[]) -{ - union wpa_event_data event; - struct nlattr *nl; - int rem; - struct scan_info *info; -#define MAX_REPORT_FREQS 50 - int freqs[MAX_REPORT_FREQS]; - int num_freqs = 0; - - if (drv->scan_for_auth) { - drv->scan_for_auth = 0; - wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " - "cfg80211 BSS entry"); - wpa_driver_nl80211_authenticate_retry(drv); - return; - } - - os_memset(&event, 0, sizeof(event)); - info = &event.scan_info; - info->aborted = aborted; - - if (tb[NL80211_ATTR_SCAN_SSIDS]) { - nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { - struct wpa_driver_scan_ssid *s = - &info->ssids[info->num_ssids]; - s->ssid = nla_data(nl); - s->ssid_len = nla_len(nl); - wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", - wpa_ssid_txt(s->ssid, s->ssid_len)); - info->num_ssids++; - if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) - break; - } - } - if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { - char msg[200], *pos, *end; - int res; - - pos = msg; - end = pos + sizeof(msg); - *pos = '\0'; - - nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) - { - freqs[num_freqs] = nla_get_u32(nl); - res = os_snprintf(pos, end - pos, " %d", - freqs[num_freqs]); - if (res > 0 && end - pos > res) - pos += res; - num_freqs++; - if (num_freqs == MAX_REPORT_FREQS - 1) - break; - } - info->freqs = freqs; - info->num_freqs = num_freqs; - wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", - msg); - } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); -} - - static int get_link_signal(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -2372,27 +1211,21 @@ static int get_link_signal(struct nl_msg *msg, void *arg) } -static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, - struct wpa_signal_info *sig) +int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig) { struct nl_msg *msg; sig->current_signal = -9999; sig->current_txrate = 0; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) { + nlmsg_free(msg); + return -ENOBUFS; + } return send_and_recv_msgs(drv, msg, get_link_signal, sig); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -2440,946 +1273,16 @@ static int get_link_noise(struct nl_msg *msg, void *arg) } -static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, - struct wpa_signal_info *sig_change) +int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change) { struct nl_msg *msg; sig_change->current_noise = 9999; sig_change->frequency = drv->assoc_freq; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); return send_and_recv_msgs(drv, msg, get_link_noise, sig_change); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} - - -static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; - static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { - [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, - [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, - }; - struct wpa_scan_results *scan_results = arg; - struct wpa_scan_res *scan_res; - size_t i; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb[NL80211_ATTR_SURVEY_INFO]) { - wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); - return NL_SKIP; - } - - if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, - tb[NL80211_ATTR_SURVEY_INFO], - survey_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " - "attributes"); - return NL_SKIP; - } - - if (!sinfo[NL80211_SURVEY_INFO_NOISE]) - return NL_SKIP; - - if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) - return NL_SKIP; - - for (i = 0; i < scan_results->num; ++i) { - scan_res = scan_results->res[i]; - if (!scan_res) - continue; - if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != - scan_res->freq) - continue; - if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) - continue; - scan_res->noise = (s8) - nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); - scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; - } - - return NL_SKIP; -} - - -static int nl80211_get_noise_for_scan_results( - struct wpa_driver_nl80211_data *drv, - struct wpa_scan_results *scan_res) -{ - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, - scan_res); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; -} - - -static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) -{ - static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, - [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, - }; - struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; - enum nl80211_cqm_rssi_threshold_event event; - union wpa_event_data ed; - struct wpa_signal_info sig; - int res; - - if (tb[NL80211_ATTR_CQM] == NULL || - nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], - cqm_policy)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); - return; - } - - os_memset(&ed, 0, sizeof(ed)); - - if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { - if (!tb[NL80211_ATTR_MAC]) - return; - os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), - ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); - return; - } - - if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) - return; - event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); - - if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI high"); - ed.signal_change.above_threshold = 1; - } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { - wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " - "event: RSSI low"); - ed.signal_change.above_threshold = 0; - } else - return; - - res = nl80211_get_link_signal(drv, &sig); - if (res == 0) { - ed.signal_change.current_signal = sig.current_signal; - ed.signal_change.current_txrate = sig.current_txrate; - wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", - sig.current_signal, sig.current_txrate); - } - - res = nl80211_get_link_noise(drv, &sig); - if (res == 0) { - ed.signal_change.current_noise = sig.current_noise; - wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", - sig.current_noise); - } - - wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); -} - - -static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - u8 *addr; - union wpa_event_data data; - - if (tb[NL80211_ATTR_MAC] == NULL) - return; - addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); - - if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - u8 *ies = NULL; - size_t ies_len = 0; - if (tb[NL80211_ATTR_IE]) { - ies = nla_data(tb[NL80211_ATTR_IE]); - ies_len = nla_len(tb[NL80211_ATTR_IE]); - } - wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); - drv_event_assoc(drv->ctx, addr, ies, ies_len, 0); - return; - } - - if (drv->nlmode != NL80211_IFTYPE_ADHOC) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data); -} - - -static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - u8 *addr; - union wpa_event_data data; - - if (tb[NL80211_ATTR_MAC] == NULL) - return; - addr = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, - MAC2STR(addr)); - - if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { - drv_event_disassoc(drv->ctx, addr); - return; - } - - if (drv->nlmode != NL80211_IFTYPE_ADHOC) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); - wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); -} - - -static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; - static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { - [NL80211_REKEY_DATA_KEK] = { - .minlen = NL80211_KEK_LEN, - .maxlen = NL80211_KEK_LEN, - }, - [NL80211_REKEY_DATA_KCK] = { - .minlen = NL80211_KCK_LEN, - .maxlen = NL80211_KCK_LEN, - }, - [NL80211_REKEY_DATA_REPLAY_CTR] = { - .minlen = NL80211_REPLAY_CTR_LEN, - .maxlen = NL80211_REPLAY_CTR_LEN, - }, - }; - union wpa_event_data data; - - if (!tb[NL80211_ATTR_MAC]) - return; - if (!tb[NL80211_ATTR_REKEY_DATA]) - return; - if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, - tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) - return; - if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) - return; - - os_memset(&data, 0, sizeof(data)); - data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); - wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, - MAC2STR(data.driver_gtk_rekey.bssid)); - data.driver_gtk_rekey.replay_ctr = - nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); - wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", - data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); - wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); -} - - -static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; - static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { - [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, - [NL80211_PMKSA_CANDIDATE_BSSID] = { - .minlen = ETH_ALEN, - .maxlen = ETH_ALEN, - }, - [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, - }; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); - - if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) - return; - if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, - tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) - return; - if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || - !cand[NL80211_PMKSA_CANDIDATE_BSSID]) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.pmkid_candidate.bssid, - nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); - data.pmkid_candidate.index = - nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); - data.pmkid_candidate.preauth = - cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; - wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); -} - - -static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.client_poll.addr, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - - wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); -} - - -static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { - case NL80211_TDLS_SETUP: - wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " - MACSTR, MAC2STR(data.tdls.peer)); - data.tdls.oper = TDLS_REQUEST_SETUP; - break; - case NL80211_TDLS_TEARDOWN: - wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " - MACSTR, MAC2STR(data.tdls.peer)); - data.tdls.oper = TDLS_REQUEST_TEARDOWN; - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " - "event"); - return; - } - if (tb[NL80211_ATTR_REASON_CODE]) { - data.tdls.reason_code = - nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); - } - - wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); -} - - -static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); -} - - -static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - union wpa_event_data data; - u32 reason; - - wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); - - if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.connect_failed_reason.addr, - nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); - - reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); - switch (reason) { - case NL80211_CONN_FAIL_MAX_CLIENTS: - wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); - data.connect_failed_reason.code = MAX_CLIENT_REACHED; - break; - case NL80211_CONN_FAIL_BLOCKED_CLIENT: - wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR - " tried to connect", - MAC2STR(data.connect_failed_reason.addr)); - data.connect_failed_reason.code = BLOCKED_CLIENT; - break; - default: - wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " - "%u", reason); - return; - } - - wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); -} - - -static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - union wpa_event_data data; - enum nl80211_radar_event event_type; - - if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) - return; - - os_memset(&data, 0, sizeof(data)); - data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); - event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); - - /* Check HT params */ - if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - data.dfs_event.ht_enabled = 1; - data.dfs_event.chan_offset = 0; - - switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { - case NL80211_CHAN_NO_HT: - data.dfs_event.ht_enabled = 0; - break; - case NL80211_CHAN_HT20: - break; - case NL80211_CHAN_HT40PLUS: - data.dfs_event.chan_offset = 1; - break; - case NL80211_CHAN_HT40MINUS: - data.dfs_event.chan_offset = -1; - break; - } - } - - /* Get VHT params */ - if (tb[NL80211_ATTR_CHANNEL_WIDTH]) - data.dfs_event.chan_width = - convert2width(nla_get_u32( - tb[NL80211_ATTR_CHANNEL_WIDTH])); - if (tb[NL80211_ATTR_CENTER_FREQ1]) - data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); - if (tb[NL80211_ATTR_CENTER_FREQ2]) - data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); - - wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", - data.dfs_event.freq, data.dfs_event.ht_enabled, - data.dfs_event.chan_offset, data.dfs_event.chan_width, - data.dfs_event.cf1, data.dfs_event.cf2); - - switch (event_type) { - case NL80211_RADAR_DETECTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); - break; - case NL80211_RADAR_CAC_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); - break; - case NL80211_RADAR_CAC_ABORTED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); - break; - case NL80211_RADAR_NOP_FINISHED: - wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " - "received", event_type); - break; - } -} - - -static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, - int wds) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - union wpa_event_data event; - - if (!tb[NL80211_ATTR_MAC]) - return; - - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = bss->addr; - event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); - event.rx_from_unknown.wds = wds; - - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); -} - - -static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, - const u8 *data, size_t len) -{ - u32 i, count; - union wpa_event_data event; - struct wpa_freq_range *range = NULL; - const struct qca_avoid_freq_list *freq_range; - - freq_range = (const struct qca_avoid_freq_list *) data; - if (len < sizeof(freq_range->count)) - return; - - count = freq_range->count; - if (len < sizeof(freq_range->count) + - count * sizeof(struct qca_avoid_freq_range)) { - wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", - (unsigned int) len); - return; - } - - if (count > 0) { - range = os_calloc(count, sizeof(struct wpa_freq_range)); - if (range == NULL) - return; - } - - os_memset(&event, 0, sizeof(event)); - for (i = 0; i < count; i++) { - unsigned int idx = event.freq_range.num; - range[idx].min = freq_range->range[i].start_freq; - range[idx].max = freq_range->range[i].end_freq; - wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", - range[idx].min, range[idx].max); - if (range[idx].min > range[idx].max) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); - continue; - } - event.freq_range.num++; - } - event.freq_range.range = range; - - wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); - - os_free(range); -} - - -static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, - u32 subcmd, u8 *data, size_t len) -{ - switch (subcmd) { - case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: - qca_nl80211_avoid_freq(drv, data, len); - break; - default: - wpa_printf(MSG_DEBUG, - "nl80211: Ignore unsupported QCA vendor event %u", - subcmd); - break; - } -} - - -static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, - struct nlattr **tb) -{ - u32 vendor_id, subcmd, wiphy = 0; - int wiphy_idx; - u8 *data = NULL; - size_t len = 0; - - if (!tb[NL80211_ATTR_VENDOR_ID] || - !tb[NL80211_ATTR_VENDOR_SUBCMD]) - return; - - vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); - subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); - - if (tb[NL80211_ATTR_WIPHY]) - wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); - - wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", - wiphy, vendor_id, subcmd); - - if (tb[NL80211_ATTR_VENDOR_DATA]) { - data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); - len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); - wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); - } - - wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); - if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { - wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", - wiphy, wiphy_idx); - return; - } - - switch (vendor_id) { - case OUI_QCA: - nl80211_vendor_event_qca(drv, subcmd, data, len); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); - break; - } -} - - -static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, - struct nlattr *tb[]) -{ - union wpa_event_data data; - enum nl80211_reg_initiator init; - - wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); - - if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) - return; - - os_memset(&data, 0, sizeof(data)); - init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]); - wpa_printf(MSG_DEBUG, " * initiator=%d", init); - switch (init) { - case NL80211_REGDOM_SET_BY_CORE: - data.channel_list_changed.initiator = REGDOM_SET_BY_CORE; - break; - case NL80211_REGDOM_SET_BY_USER: - data.channel_list_changed.initiator = REGDOM_SET_BY_USER; - break; - case NL80211_REGDOM_SET_BY_DRIVER: - data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER; - break; - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE; - break; - } - - if (tb[NL80211_ATTR_REG_TYPE]) { - enum nl80211_reg_type type; - type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); - wpa_printf(MSG_DEBUG, " * type=%d", type); - switch (type) { - case NL80211_REGDOM_TYPE_COUNTRY: - data.channel_list_changed.type = REGDOM_TYPE_COUNTRY; - break; - case NL80211_REGDOM_TYPE_WORLD: - data.channel_list_changed.type = REGDOM_TYPE_WORLD; - break; - case NL80211_REGDOM_TYPE_CUSTOM_WORLD: - data.channel_list_changed.type = - REGDOM_TYPE_CUSTOM_WORLD; - break; - case NL80211_REGDOM_TYPE_INTERSECTION: - data.channel_list_changed.type = - REGDOM_TYPE_INTERSECTION; - break; - } - } - - if (tb[NL80211_ATTR_REG_ALPHA2]) { - os_strlcpy(data.channel_list_changed.alpha2, - nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), - sizeof(data.channel_list_changed.alpha2)); - wpa_printf(MSG_DEBUG, " * alpha2=%s", - data.channel_list_changed.alpha2); - } - - wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); -} - - -static void do_process_drv_event(struct i802_bss *bss, int cmd, - struct nlattr **tb) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", - cmd, nl80211_command_to_string(cmd), bss->ifname); - - if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && - (cmd == NL80211_CMD_NEW_SCAN_RESULTS || - cmd == NL80211_CMD_SCAN_ABORTED)) { - wpa_driver_nl80211_set_mode(drv->first_bss, - drv->ap_scan_as_station); - drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - } - - switch (cmd) { - case NL80211_CMD_TRIGGER_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); - drv->scan_state = SCAN_STARTED; - if (drv->scan_for_auth) { - /* - * Cannot indicate EVENT_SCAN_STARTED here since we skip - * EVENT_SCAN_RESULTS in scan_for_auth case and the - * upper layer implementation could get confused about - * scanning state. - */ - wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); - break; - } - wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); - break; - case NL80211_CMD_START_SCHED_SCAN: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); - drv->scan_state = SCHED_SCAN_STARTED; - break; - case NL80211_CMD_SCHED_SCAN_STOPPED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); - drv->scan_state = SCHED_SCAN_STOPPED; - wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); - break; - case NL80211_CMD_NEW_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: New scan results available"); - drv->scan_state = SCAN_COMPLETED; - drv->scan_complete_events = 1; - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 0, tb); - break; - case NL80211_CMD_SCHED_SCAN_RESULTS: - wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: New sched scan results available"); - drv->scan_state = SCHED_SCAN_RESULTS; - send_scan_event(drv, 0, tb); - break; - case NL80211_CMD_SCAN_ABORTED: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - drv->scan_state = SCAN_ABORTED; - /* - * Need to indicate that scan results are available in order - * not to make wpa_supplicant stop its scanning. - */ - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, - drv->ctx); - send_scan_event(drv, 1, tb); - break; - case NL80211_CMD_AUTHENTICATE: - case NL80211_CMD_ASSOCIATE: - case NL80211_CMD_DEAUTHENTICATE: - case NL80211_CMD_DISASSOCIATE: - case NL80211_CMD_FRAME_TX_STATUS: - case NL80211_CMD_UNPROT_DEAUTHENTICATE: - case NL80211_CMD_UNPROT_DISASSOCIATE: - mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], - tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], - tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE], - tb[NL80211_ATTR_RX_SIGNAL_DBM]); - break; - case NL80211_CMD_CONNECT: - case NL80211_CMD_ROAM: - mlme_event_connect(drv, cmd, - tb[NL80211_ATTR_STATUS_CODE], - tb[NL80211_ATTR_MAC], - tb[NL80211_ATTR_REQ_IE], - tb[NL80211_ATTR_RESP_IE]); - break; - case NL80211_CMD_CH_SWITCH_NOTIFY: - mlme_event_ch_switch(drv, - tb[NL80211_ATTR_IFINDEX], - tb[NL80211_ATTR_WIPHY_FREQ], - tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], - tb[NL80211_ATTR_CHANNEL_WIDTH], - tb[NL80211_ATTR_CENTER_FREQ1], - tb[NL80211_ATTR_CENTER_FREQ2]); - break; - case NL80211_CMD_DISCONNECT: - mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], - tb[NL80211_ATTR_MAC], - tb[NL80211_ATTR_DISCONNECTED_BY_AP]); - break; - case NL80211_CMD_MICHAEL_MIC_FAILURE: - mlme_event_michael_mic_failure(bss, tb); - break; - case NL80211_CMD_JOIN_IBSS: - mlme_event_join_ibss(drv, tb); - break; - case NL80211_CMD_REMAIN_ON_CHANNEL: - mlme_event_remain_on_channel(drv, 0, tb); - break; - case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: - mlme_event_remain_on_channel(drv, 1, tb); - break; - case NL80211_CMD_NOTIFY_CQM: - nl80211_cqm_event(drv, tb); - break; - case NL80211_CMD_REG_CHANGE: - nl80211_reg_change_event(drv, tb); - break; - case NL80211_CMD_REG_BEACON_HINT: - wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); - os_memset(&data, 0, sizeof(data)); - data.channel_list_changed.initiator = REGDOM_BEACON_HINT; - wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, - &data); - break; - case NL80211_CMD_NEW_STATION: - nl80211_new_station_event(drv, tb); - break; - case NL80211_CMD_DEL_STATION: - nl80211_del_station_event(drv, tb); - break; - case NL80211_CMD_SET_REKEY_OFFLOAD: - nl80211_rekey_offload_event(drv, tb); - break; - case NL80211_CMD_PMKSA_CANDIDATE: - nl80211_pmksa_candidate_event(drv, tb); - break; - case NL80211_CMD_PROBE_CLIENT: - nl80211_client_probe_event(drv, tb); - break; - case NL80211_CMD_TDLS_OPER: - nl80211_tdls_oper_event(drv, tb); - break; - case NL80211_CMD_CONN_FAILED: - nl80211_connect_failed_event(drv, tb); - break; - case NL80211_CMD_FT_EVENT: - mlme_event_ft_event(drv, tb); - break; - case NL80211_CMD_RADAR_DETECT: - nl80211_radar_event(drv, tb); - break; - case NL80211_CMD_STOP_AP: - nl80211_stop_ap(drv, tb); - break; - case NL80211_CMD_VENDOR: - nl80211_vendor_event(drv, tb); - break; - default: - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " - "(cmd=%d)", cmd); - break; - } -} - - -static int process_drv_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]; - struct i802_bss *bss; - int ifidx = -1; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX]) { - ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - - for (bss = drv->first_bss; bss; bss = bss->next) - if (ifidx == -1 || ifidx == bss->ifindex) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; - } - wpa_printf(MSG_DEBUG, - "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)", - gnlh->cmd, ifidx); - } else if (tb[NL80211_ATTR_WDEV]) { - u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); - wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device"); - for (bss = drv->first_bss; bss; bss = bss->next) { - if (bss->wdev_id_set && wdev_id == bss->wdev_id) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; - } - } - wpa_printf(MSG_DEBUG, - "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)", - gnlh->cmd, (long long unsigned int) wdev_id); - } - - return NL_SKIP; -} - - -static int process_global_event(struct nl_msg *msg, void *arg) -{ - struct nl80211_global *global = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct wpa_driver_nl80211_data *drv, *tmp; - int ifidx = -1; - struct i802_bss *bss; - u64 wdev_id = 0; - int wdev_id_set = 0; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_IFINDEX]) - ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); - else if (tb[NL80211_ATTR_WDEV]) { - wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); - wdev_id_set = 1; - } - - dl_list_for_each_safe(drv, tmp, &global->interfaces, - struct wpa_driver_nl80211_data, list) { - for (bss = drv->first_bss; bss; bss = bss->next) { - if ((ifidx == -1 && !wdev_id_set) || - ifidx == bss->ifindex || - (wdev_id_set && bss->wdev_id_set && - wdev_id == bss->wdev_id)) { - do_process_drv_event(bss, gnlh->cmd, tb); - return NL_SKIP; - } - } - } - - return NL_SKIP; -} - - -static int process_bss_event(struct nl_msg *msg, void *arg) -{ - struct i802_bss *bss = arg; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", - gnlh->cmd, nl80211_command_to_string(gnlh->cmd), - bss->ifname); - - switch (gnlh->cmd) { - case NL80211_CMD_FRAME: - case NL80211_CMD_FRAME_TX_STATUS: - mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], - tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], - tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], - tb[NL80211_ATTR_COOKIE], - tb[NL80211_ATTR_RX_SIGNAL_DBM]); - break; - case NL80211_CMD_UNEXPECTED_FRAME: - nl80211_spurious_frame(bss, tb, 0); - break; - case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: - nl80211_spurious_frame(bss, tb, 1); - break; - default: - wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " - "(cmd=%d)", gnlh->cmd); - break; - } - - return NL_SKIP; } @@ -3423,15 +1326,14 @@ static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) alpha2[1] = alpha2_arg[1]; alpha2[2] = '\0'; - nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG); - - NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) || + nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) { + nlmsg_free(msg); + return -EINVAL; + } if (send_and_recv_msgs(drv, msg, NULL, NULL)) return -EINVAL; return 0; -nla_put_failure: - nlmsg_free(msg); - return -EINVAL; } @@ -3473,709 +1375,6 @@ static int wpa_driver_nl80211_get_country(void *priv, char *alpha2) } -static int protocol_feature_handler(struct nl_msg *msg, void *arg) -{ - u32 *feat = arg; - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) - *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); - - return NL_SKIP; -} - - -static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) -{ - u32 feat = 0; - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES); - if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) - return feat; - - msg = NULL; -nla_put_failure: - nlmsg_free(msg); - return 0; -} - - -struct wiphy_info_data { - struct wpa_driver_nl80211_data *drv; - struct wpa_driver_capa *capa; - - unsigned int num_multichan_concurrent; - - unsigned int error:1; - unsigned int device_ap_sme:1; - unsigned int poll_command_supported:1; - unsigned int data_tx_status:1; - unsigned int monitor_supported:1; - unsigned int auth_supported:1; - unsigned int connect_supported:1; - unsigned int p2p_go_supported:1; - unsigned int p2p_client_supported:1; - unsigned int p2p_concurrent:1; - unsigned int channel_switch_supported:1; - unsigned int set_qos_map_supported:1; - unsigned int have_low_prio_scan:1; -}; - - -static unsigned int probe_resp_offload_support(int supp_protocols) -{ - unsigned int prot = 0; - - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; - if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) - prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; - - return prot; -} - - -static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, - struct nlattr *tb) -{ - struct nlattr *nl_mode; - int i; - - if (tb == NULL) - return; - - nla_for_each_nested(nl_mode, tb, i) { - switch (nla_type(nl_mode)) { - case NL80211_IFTYPE_AP: - info->capa->flags |= WPA_DRIVER_FLAGS_AP; - break; - case NL80211_IFTYPE_ADHOC: - info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; - break; - case NL80211_IFTYPE_P2P_DEVICE: - info->capa->flags |= - WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; - break; - case NL80211_IFTYPE_P2P_GO: - info->p2p_go_supported = 1; - break; - case NL80211_IFTYPE_P2P_CLIENT: - info->p2p_client_supported = 1; - break; - case NL80211_IFTYPE_MONITOR: - info->monitor_supported = 1; - break; - } - } -} - - -static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, - struct nlattr *nl_combi) -{ - struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; - struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; - struct nlattr *nl_limit, *nl_mode; - int err, rem_limit, rem_mode; - int combination_has_p2p = 0, combination_has_mgd = 0; - static struct nla_policy - iface_combination_policy[NUM_NL80211_IFACE_COMB] = { - [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, - [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, - [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, - [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, - [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, - }, - iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { - [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, - [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, - }; - - err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, - nl_combi, iface_combination_policy); - if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || - !tb_comb[NL80211_IFACE_COMB_MAXNUM] || - !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) - return 0; /* broken combination */ - - if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) - info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; - - nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], - rem_limit) { - err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, - nl_limit, iface_limit_policy); - if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) - return 0; /* broken combination */ - - nla_for_each_nested(nl_mode, - tb_limit[NL80211_IFACE_LIMIT_TYPES], - rem_mode) { - int ift = nla_type(nl_mode); - if (ift == NL80211_IFTYPE_P2P_GO || - ift == NL80211_IFTYPE_P2P_CLIENT) - combination_has_p2p = 1; - if (ift == NL80211_IFTYPE_STATION) - combination_has_mgd = 1; - } - if (combination_has_p2p && combination_has_mgd) - break; - } - - if (combination_has_p2p && combination_has_mgd) { - unsigned int num_channels = - nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); - - info->p2p_concurrent = 1; - if (info->num_multichan_concurrent < num_channels) - info->num_multichan_concurrent = num_channels; - } - - return 0; -} - - -static void wiphy_info_iface_comb(struct wiphy_info_data *info, - struct nlattr *tb) -{ - struct nlattr *nl_combi; - int rem_combi; - - if (tb == NULL) - return; - - nla_for_each_nested(nl_combi, tb, rem_combi) { - if (wiphy_info_iface_comb_process(info, nl_combi) > 0) - break; - } -} - - -static void wiphy_info_supp_cmds(struct wiphy_info_data *info, - struct nlattr *tb) -{ - struct nlattr *nl_cmd; - int i; - - if (tb == NULL) - return; - - nla_for_each_nested(nl_cmd, tb, i) { - switch (nla_get_u32(nl_cmd)) { - case NL80211_CMD_AUTHENTICATE: - info->auth_supported = 1; - break; - case NL80211_CMD_CONNECT: - info->connect_supported = 1; - break; - case NL80211_CMD_START_SCHED_SCAN: - info->capa->sched_scan_supported = 1; - break; - case NL80211_CMD_PROBE_CLIENT: - info->poll_command_supported = 1; - break; - case NL80211_CMD_CHANNEL_SWITCH: - info->channel_switch_supported = 1; - break; - case NL80211_CMD_SET_QOS_MAP: - info->set_qos_map_supported = 1; - break; - } - } -} - - -static void wiphy_info_cipher_suites(struct wiphy_info_data *info, - struct nlattr *tb) -{ - int i, num; - u32 *ciphers; - - if (tb == NULL) - return; - - num = nla_len(tb) / sizeof(u32); - ciphers = nla_data(tb); - for (i = 0; i < num; i++) { - u32 c = ciphers[i]; - - wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", - c >> 24, (c >> 16) & 0xff, - (c >> 8) & 0xff, c & 0xff); - switch (c) { - case WLAN_CIPHER_SUITE_CCMP_256: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; - break; - case WLAN_CIPHER_SUITE_GCMP_256: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; - break; - case WLAN_CIPHER_SUITE_CCMP: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; - break; - case WLAN_CIPHER_SUITE_GCMP: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; - break; - case WLAN_CIPHER_SUITE_TKIP: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; - break; - case WLAN_CIPHER_SUITE_WEP104: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; - break; - case WLAN_CIPHER_SUITE_WEP40: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; - break; - case WLAN_CIPHER_SUITE_BIP_GMAC_128: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; - break; - case WLAN_CIPHER_SUITE_BIP_GMAC_256: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; - break; - case WLAN_CIPHER_SUITE_BIP_CMAC_256: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; - break; - case WLAN_CIPHER_SUITE_NO_GROUP_ADDR: - info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; - break; - } - } -} - - -static void wiphy_info_max_roc(struct wpa_driver_capa *capa, - struct nlattr *tb) -{ - if (tb) - capa->max_remain_on_chan = nla_get_u32(tb); -} - - -static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, - struct nlattr *ext_setup) -{ - if (tdls == NULL) - return; - - wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); - capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; - - if (ext_setup) { - wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); - capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; - } -} - - -static void wiphy_info_feature_flags(struct wiphy_info_data *info, - struct nlattr *tb) -{ - u32 flags; - struct wpa_driver_capa *capa = info->capa; - - if (tb == NULL) - return; - - flags = nla_get_u32(tb); - - if (flags & NL80211_FEATURE_SK_TX_STATUS) - info->data_tx_status = 1; - - if (flags & NL80211_FEATURE_INACTIVITY_TIMER) - capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; - - if (flags & NL80211_FEATURE_SAE) - capa->flags |= WPA_DRIVER_FLAGS_SAE; - - if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) - capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; - - if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) - capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; - - if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN) - info->have_low_prio_scan = 1; -} - - -static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, - struct nlattr *tb) -{ - u32 protocols; - - if (tb == NULL) - return; - - protocols = nla_get_u32(tb); - wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " - "mode"); - capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; - capa->probe_resp_offloads = probe_resp_offload_support(protocols); -} - - -static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, - struct nlattr *tb) -{ - struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1]; - - if (tb == NULL) - return; - - if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG, - tb, NULL)) - return; - - if (triggers[NL80211_WOWLAN_TRIG_ANY]) - capa->wowlan_triggers.any = 1; - if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT]) - capa->wowlan_triggers.disconnect = 1; - if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT]) - capa->wowlan_triggers.magic_pkt = 1; - if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) - capa->wowlan_triggers.gtk_rekey_failure = 1; - if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) - capa->wowlan_triggers.eap_identity_req = 1; - if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) - capa->wowlan_triggers.four_way_handshake = 1; - if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) - capa->wowlan_triggers.rfkill_release = 1; -} - - -static int wiphy_info_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct wiphy_info_data *info = arg; - struct wpa_driver_capa *capa = info->capa; - struct wpa_driver_nl80211_data *drv = info->drv; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_WIPHY_NAME]) - os_strlcpy(drv->phyname, - nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), - sizeof(drv->phyname)); - if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) - capa->max_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) - capa->max_sched_scan_ssids = - nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); - - if (tb[NL80211_ATTR_MAX_MATCH_SETS]) - capa->max_match_sets = - nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); - - if (tb[NL80211_ATTR_MAC_ACL_MAX]) - capa->max_acl_mac_addrs = - nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); - - wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); - wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); - wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); - wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); - - if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " - "off-channel TX"); - capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; - } - - if (tb[NL80211_ATTR_ROAM_SUPPORT]) { - wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); - capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; - } - - wiphy_info_max_roc(capa, - tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); - - if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) - capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; - - wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], - tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); - - if (tb[NL80211_ATTR_DEVICE_AP_SME]) - info->device_ap_sme = 1; - - wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); - wiphy_info_probe_resp_offload(capa, - tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); - - if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && - drv->extended_capa == NULL) { - drv->extended_capa = - os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); - if (drv->extended_capa) { - os_memcpy(drv->extended_capa, - nla_data(tb[NL80211_ATTR_EXT_CAPA]), - nla_len(tb[NL80211_ATTR_EXT_CAPA])); - drv->extended_capa_len = - nla_len(tb[NL80211_ATTR_EXT_CAPA]); - } - drv->extended_capa_mask = - os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); - if (drv->extended_capa_mask) { - os_memcpy(drv->extended_capa_mask, - nla_data(tb[NL80211_ATTR_EXT_CAPA]), - nla_len(tb[NL80211_ATTR_EXT_CAPA])); - } else { - os_free(drv->extended_capa); - drv->extended_capa = NULL; - drv->extended_capa_len = 0; - } - } - - if (tb[NL80211_ATTR_VENDOR_DATA]) { - struct nlattr *nl; - int rem; - - nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { - struct nl80211_vendor_cmd_info *vinfo; - if (nla_len(nl) != sizeof(*vinfo)) { - wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); - continue; - } - vinfo = nla_data(nl); - switch (vinfo->subcmd) { - case QCA_NL80211_VENDOR_SUBCMD_ROAMING: - drv->roaming_vendor_cmd_avail = 1; - break; - case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: - drv->dfs_vendor_cmd_avail = 1; - break; - } - - wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", - vinfo->vendor_id, vinfo->subcmd); - } - } - - if (tb[NL80211_ATTR_VENDOR_EVENTS]) { - struct nlattr *nl; - int rem; - - nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { - struct nl80211_vendor_cmd_info *vinfo; - if (nla_len(nl) != sizeof(*vinfo)) { - wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); - continue; - } - vinfo = nla_data(nl); - wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", - vinfo->vendor_id, vinfo->subcmd); - } - } - - wiphy_info_wowlan_triggers(capa, - tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]); - - if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA]) - capa->max_stations = - nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); - - return NL_SKIP; -} - - -static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, - struct wiphy_info_data *info) -{ - u32 feat; - struct nl_msg *msg; - - os_memset(info, 0, sizeof(*info)); - info->capa = &drv->capa; - info->drv = drv; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - feat = get_nl80211_protocol_features(drv); - if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); - else - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); - - NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); - if (nl80211_set_iface_id(msg, drv->first_bss) < 0) - goto nla_put_failure; - - if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) - return -1; - - if (info->auth_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_SME; - else if (!info->connect_supported) { - wpa_printf(MSG_INFO, "nl80211: Driver does not support " - "authentication/association or connect commands"); - info->error = 1; - } - - if (info->p2p_go_supported && info->p2p_client_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; - if (info->p2p_concurrent) { - wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " - "interface (driver advertised support)"); - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; - } - if (info->num_multichan_concurrent > 1) { - wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " - "concurrent (driver advertised support)"); - drv->capa.num_multichan_concurrent = - info->num_multichan_concurrent; - } - - /* default to 5000 since early versions of mac80211 don't set it */ - if (!drv->capa.max_remain_on_chan) - drv->capa.max_remain_on_chan = 5000; - - if (info->channel_switch_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; - - return 0; -nla_put_failure: - nlmsg_free(msg); - return -1; -} - - -static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) -{ - struct wiphy_info_data info; - if (wpa_driver_nl80211_get_info(drv, &info)) - return -1; - - if (info.error) - return -1; - - drv->has_capability = 1; - drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; - drv->capa.auth = WPA_DRIVER_AUTH_OPEN | - WPA_DRIVER_AUTH_SHARED | - WPA_DRIVER_AUTH_LEAP; - - drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; - drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; - drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - - /* - * As all cfg80211 drivers must support cases where the AP interface is - * removed without the knowledge of wpa_supplicant/hostapd, e.g., in - * case that the user space daemon has crashed, they must be able to - * cleanup all stations and key entries in the AP tear down flow. Thus, - * this flag can/should always be set for cfg80211 drivers. - */ - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT; - - if (!info.device_ap_sme) { - drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; - - /* - * No AP SME is currently assumed to also indicate no AP MLME - * in the driver/firmware. - */ - drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; - } - - drv->device_ap_sme = info.device_ap_sme; - drv->poll_command_supported = info.poll_command_supported; - drv->data_tx_status = info.data_tx_status; - if (info.set_qos_map_supported) - drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; - drv->have_low_prio_scan = info.have_low_prio_scan; - - /* - * If poll command and tx status are supported, mac80211 is new enough - * to have everything we need to not need monitor interfaces. - */ - drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; - - if (drv->device_ap_sme && drv->use_monitor) { - /* - * Non-mac80211 drivers may not support monitor interface. - * Make sure we do not get stuck with incorrect capability here - * by explicitly testing this. - */ - if (!info.monitor_supported) { - wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " - "with device_ap_sme since no monitor mode " - "support detected"); - drv->use_monitor = 0; - } - } - - /* - * If we aren't going to use monitor interfaces, but the - * driver doesn't support data TX status, we won't get TX - * status for EAPOL frames. - */ - if (!drv->use_monitor && !info.data_tx_status) - drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; - - return 0; -} - - -#ifdef ANDROID -static int android_genl_ctrl_resolve(struct nl_handle *handle, - const char *name) -{ - /* - * Android ICS has very minimal genl_ctrl_resolve() implementation, so - * need to work around that. - */ - struct nl_cache *cache = NULL; - struct genl_family *nl80211 = NULL; - int id = -1; - - if (genl_ctrl_alloc_cache(handle, &cache) < 0) { - wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " - "netlink cache"); - goto fail; - } - - nl80211 = genl_ctrl_search_by_name(cache, name); - if (nl80211 == NULL) - goto fail; - - id = genl_family_get_id(nl80211); - -fail: - if (nl80211) - genl_family_put(nl80211); - if (cache) - nl_cache_free(cache); - - return id; -} -#define genl_ctrl_resolve android_genl_ctrl_resolve -#endif /* ANDROID */ - - static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) { int ret; @@ -4262,23 +1461,6 @@ err: } -static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv) -{ - drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!drv->nl_cb) { - wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct"); - return -1; - } - - nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, - no_seq_check, NULL); - nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, - process_drv_event, drv); - - return 0; -} - - static void wpa_driver_nl80211_rfkill_blocked(void *ctx) { wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked"); @@ -4385,7 +1567,8 @@ static void nl80211_destroy_bss(struct i802_bss *bss) static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, void *global_priv, int hostapd, - const u8 *set_addr) + const u8 *set_addr, + const char *driver_params) { struct wpa_driver_nl80211_data *drv; struct rfkill_config *rcfg; @@ -4418,11 +1601,6 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, drv->eapol_tx_sock = -1; drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - if (wpa_driver_nl80211_init_nl(drv)) { - os_free(drv); - return NULL; - } - if (nl80211_init_bss(bss)) goto failed; @@ -4442,7 +1620,7 @@ static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname, if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0) drv->start_iface_up = 1; - if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1)) + if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params)) goto failed; drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0); @@ -4491,7 +1669,8 @@ failed: static void * wpa_driver_nl80211_init(void *ctx, const char *ifname, void *global_priv) { - return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL); + return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL, + NULL); } @@ -4501,54 +1680,42 @@ static int nl80211_register_frame(struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; + int ret; char buf[30]; - msg = nlmsg_alloc(); - if (!msg) - return -1; - buf[0] = '\0'; wpa_snprintf_hex(buf, sizeof(buf), match, match_len); wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s", type, fc2str(type), nl_handle, buf); - nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - - NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type); - NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_ACTION)) || + nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) || + nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) { + nlmsg_free(msg); + return -1; + } ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register frame command " "failed (type=%u): ret=%d (%s)", type, ret, strerror(-ret)); wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match", match, match_len); - goto nla_put_failure; } - ret = 0; -nla_put_failure: - nlmsg_free(msg); return ret; } static int nl80211_alloc_mgmt_handle(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = bss->drv; - if (bss->nl_mgmt) { wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting " "already on! (nl_mgmt=%p)", bss->nl_mgmt); return -1; } - bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt"); + bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt"); if (bss->nl_mgmt == NULL) return -1; @@ -4667,37 +1834,75 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) ret = -1; #endif /* CONFIG_HS20 */ + /* WMM-AC ADDTS Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0) + ret = -1; + + /* WMM-AC DELTS */ + if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0) + ret = -1; + + /* Radio Measurement - Neighbor Report Response */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0) + ret = -1; + + /* Radio Measurement - Link Measurement Request */ + if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) && + (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0)) + ret = -1; + nl80211_mgmt_handle_register_eloop(bss); return ret; } -static int nl80211_register_spurious_class3(struct i802_bss *bss) +static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int ret = -1; + int ret = 0; - msg = nlmsg_alloc(); - if (!msg) + if (nl80211_alloc_mgmt_handle(bss)) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); + wpa_printf(MSG_DEBUG, + "nl80211: Subscribe to mgmt frames with mesh handle %p", + bss->nl_mgmt); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); + /* Auth frames for mesh SAE */ + if (nl80211_register_frame(bss, bss->nl_mgmt, + (WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4), + NULL, 0) < 0) + ret = -1; - ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); - msg = NULL; + /* Mesh peering open */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0) + ret = -1; + /* Mesh peering confirm */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0) + ret = -1; + /* Mesh peering close */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0) + ret = -1; + + nl80211_mgmt_handle_register_eloop(bss); + + return ret; +} + + +static int nl80211_register_spurious_class3(struct i802_bss *bss) +{ + struct nl_msg *msg; + int ret; + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME); + ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL); if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 " "failed: ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; } - ret = 0; -nla_put_failure: - nlmsg_free(msg); return ret; } @@ -4792,56 +1997,31 @@ static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx) static void nl80211_del_p2pdev(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; - msg = nlmsg_alloc(); - if (!msg) - return; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); - NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE); + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s", bss->ifname, (long long unsigned int) bss->wdev_id, strerror(-ret)); - -nla_put_failure: - nlmsg_free(msg); } static int nl80211_set_p2pdev(struct i802_bss *bss, int start) { - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - if (start) - nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE); - else - nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE); - - NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id); + int ret; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; + msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE : + NL80211_CMD_STOP_P2P_DEVICE); + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s", start ? "Start" : "Stop", bss->ifname, (long long unsigned int) bss->wdev_id, strerror(-ret)); - -nla_put_failure: - nlmsg_free(msg); return ret; } @@ -4863,7 +2043,8 @@ static int i802_set_iface_flags(struct i802_bss *bss, int up) static int wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, - const u8 *set_addr, int first) + const u8 *set_addr, int first, + const char *driver_params) { struct i802_bss *bss = drv->first_bss; int send_rfkill_event = 0; @@ -4884,6 +2065,9 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (wpa_driver_nl80211_capa(drv)) return -1; + if (driver_params && nl80211_set_param(bss, driver_params) < 0) + return -1; + wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s", bss->ifname, drv->phyname); @@ -4951,19 +2135,10 @@ static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -4978,6 +2153,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; + wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d", + bss->ifname, drv->disabled_11b_rates); + bss->in_deinit = 1; if (drv->data_tx_status) eloop_unregister_read_sock(drv->eapol_tx_sock); @@ -4996,6 +2174,11 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_handle_destroy(drv->rtnl_sk); } if (bss->added_bridge) { + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname, + 0) < 0) + wpa_printf(MSG_INFO, + "nl80211: Could not set bridge %s down", + bss->brname); if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "bridge %s: %s", @@ -5029,7 +2212,11 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) (void) i802_set_iface_flags(bss, 0); if (drv->addr_changed) { - linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); + if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, + 0) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Could not set interface down to restore permanent MAC address"); + } if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, drv->perm_addr) < 0) { wpa_printf(MSG_DEBUG, @@ -5046,7 +2233,6 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_mgmt_unsubscribe(bss, "deinit"); nl80211_del_p2pdev(bss); } - nl_cb_put(drv->nl_cb); nl80211_destroy_bss(drv->first_bss); @@ -5064,720 +2250,6 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) } -/** - * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion - * @eloop_ctx: Driver private data - * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() - * - * This function can be used as registered timeout when starting a scan to - * generate a scan completed event if the driver does not report this. - */ -static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_nl80211_data *drv = eloop_ctx; - if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { - wpa_driver_nl80211_set_mode(drv->first_bss, - drv->ap_scan_as_station); - drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; - } - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); -} - - -static struct nl_msg * -nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd, - struct wpa_driver_scan_params *params, u64 *wdev_id) -{ - struct nl_msg *msg; - size_t i; - u32 scan_flags = 0; - - msg = nlmsg_alloc(); - if (!msg) - return NULL; - - nl80211_cmd(drv, msg, 0, cmd); - - if (!wdev_id) - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - else - NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id); - - if (params->num_ssids) { - struct nlattr *ssids; - - ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); - if (ssids == NULL) - goto fail; - for (i = 0; i < params->num_ssids; i++) { - wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - if (nla_put(msg, i + 1, params->ssids[i].ssid_len, - params->ssids[i].ssid) < 0) - goto fail; - } - nla_nest_end(msg, ssids); - } - - if (params->extra_ies) { - wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", - params->extra_ies, params->extra_ies_len); - if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, - params->extra_ies) < 0) - goto fail; - } - - if (params->freqs) { - struct nlattr *freqs; - freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); - if (freqs == NULL) - goto fail; - for (i = 0; params->freqs[i]; i++) { - wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " - "MHz", params->freqs[i]); - if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0) - goto fail; - } - nla_nest_end(msg, freqs); - } - - os_free(drv->filter_ssids); - drv->filter_ssids = params->filter_ssids; - params->filter_ssids = NULL; - drv->num_filter_ssids = params->num_filter_ssids; - - if (params->only_new_results) { - wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); - scan_flags |= NL80211_SCAN_FLAG_FLUSH; - } - - if (params->low_priority && drv->have_low_prio_scan) { - wpa_printf(MSG_DEBUG, - "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); - scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; - } - - if (scan_flags) - NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags); - - return msg; - -fail: -nla_put_failure: - nlmsg_free(msg); - return NULL; -} - - -/** - * wpa_driver_nl80211_scan - Request the driver to initiate scan - * @bss: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * Returns: 0 on success, -1 on failure - */ -static int wpa_driver_nl80211_scan(struct i802_bss *bss, - struct wpa_driver_scan_params *params) -{ - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1, timeout; - struct nl_msg *msg = NULL; - - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); - drv->scan_for_auth = 0; - - msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params, - bss->wdev_id_set ? &bss->wdev_id : NULL); - if (!msg) - return -1; - - if (params->p2p_probe) { - struct nlattr *rates; - - wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); - - rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); - if (rates == NULL) - goto nla_put_failure; - - /* - * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates - * by masking out everything else apart from the OFDM rates 6, - * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz - * rates are left enabled. - */ - NLA_PUT(msg, NL80211_BAND_2GHZ, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - nla_nest_end(msg, rates); - - NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); - } - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " - "(%s)", ret, strerror(-ret)); - if (drv->hostapd && is_ap_interface(drv->nlmode)) { - enum nl80211_iftype old_mode = drv->nlmode; - - /* - * mac80211 does not allow scan requests in AP mode, so - * try to do this in station mode. - */ - if (wpa_driver_nl80211_set_mode( - bss, NL80211_IFTYPE_STATION)) - goto nla_put_failure; - - if (wpa_driver_nl80211_scan(bss, params)) { - wpa_driver_nl80211_set_mode(bss, drv->nlmode); - goto nla_put_failure; - } - - /* Restore AP mode when processing scan results */ - drv->ap_scan_as_station = old_mode; - ret = 0; - } else - goto nla_put_failure; - } - - drv->scan_state = SCAN_REQUESTED; - /* Not all drivers generate "scan completed" wireless event, so try to - * read results after a timeout. */ - timeout = 10; - if (drv->scan_complete_events) { - /* - * The driver seems to deliver events to notify when scan is - * complete, so use longer timeout to avoid race conditions - * with scanning and following association request. - */ - timeout = 30; - } - wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " - "seconds", ret, timeout); - eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); - eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, - drv, drv->ctx); - -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -/** - * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * @params: Scan parameters - * @interval: Interval between scan cycles in milliseconds - * Returns: 0 on success, -1 on failure or if not supported - */ -static int wpa_driver_nl80211_sched_scan(void *priv, - struct wpa_driver_scan_params *params, - u32 interval) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = -1; - struct nl_msg *msg; - size_t i; - - wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); - -#ifdef ANDROID - if (!drv->capa.sched_scan_supported) - return android_pno_start(bss, params); -#endif /* ANDROID */ - - msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params, - bss->wdev_id_set ? &bss->wdev_id : NULL); - if (!msg) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval); - - if ((drv->num_filter_ssids && - (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || - params->filter_rssi) { - struct nlattr *match_sets; - match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); - if (match_sets == NULL) - goto nla_put_failure; - - for (i = 0; i < drv->num_filter_ssids; i++) { - struct nlattr *match_set_ssid; - wpa_hexdump_ascii(MSG_MSGDUMP, - "nl80211: Sched scan filter SSID", - drv->filter_ssids[i].ssid, - drv->filter_ssids[i].ssid_len); - - match_set_ssid = nla_nest_start(msg, i + 1); - if (match_set_ssid == NULL) - goto nla_put_failure; - NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, - drv->filter_ssids[i].ssid_len, - drv->filter_ssids[i].ssid); - if (params->filter_rssi) - NLA_PUT_U32(msg, - NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, - params->filter_rssi); - - nla_nest_end(msg, match_set_ssid); - } - - /* - * Due to backward compatibility code, newer kernels treat this - * matchset (with only an RSSI filter) as the default for all - * other matchsets, unless it's the only one, in which case the - * matchset will actually allow all SSIDs above the RSSI. - */ - if (params->filter_rssi) { - struct nlattr *match_set_rssi; - match_set_rssi = nla_nest_start(msg, 0); - if (match_set_rssi == NULL) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, - params->filter_rssi); - wpa_printf(MSG_MSGDUMP, - "nl80211: Sched scan RSSI filter %d dBm", - params->filter_rssi); - nla_nest_end(msg, match_set_rssi); - } - - nla_nest_end(msg, match_sets); - } - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - - /* TODO: if we get an error here, we should fall back to normal scan */ - - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " - "ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; - } - - wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " - "scan interval %d msec", ret, interval); - -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -/** - * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan - * @priv: Pointer to private driver data from wpa_driver_nl80211_init() - * Returns: 0 on success, -1 on failure or if not supported - */ -static int wpa_driver_nl80211_stop_sched_scan(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret = 0; - struct nl_msg *msg; - -#ifdef ANDROID - if (!drv->capa.sched_scan_supported) - return android_pno_stop(bss); -#endif /* ANDROID */ - - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: " - "ret=%d (%s)", ret, strerror(-ret)); - goto nla_put_failure; - } - - wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret); - -nla_put_failure: - nlmsg_free(msg); - return ret; -} - - -static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) -{ - const u8 *end, *pos; - - if (ies == NULL) - return NULL; - - pos = ies; - end = ies + ies_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, - const u8 *ie, size_t ie_len) -{ - const u8 *ssid; - size_t i; - - if (drv->filter_ssids == NULL) - return 0; - - ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); - if (ssid == NULL) - return 1; - - for (i = 0; i < drv->num_filter_ssids; i++) { - if (ssid[1] == drv->filter_ssids[i].ssid_len && - os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == - 0) - return 0; - } - - return 1; -} - - -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 }, - [NL80211_BSS_STATUS] = { .type = NLA_U32 }, - [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, - [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, - }; - struct nl80211_bss_info_arg *_arg = arg; - struct wpa_scan_results *res = _arg->res; - struct wpa_scan_res **tmp; - struct wpa_scan_res *r; - const u8 *ie, *beacon_ie; - size_t ie_len, beacon_ie_len; - u8 *pos; - size_t i; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb[NL80211_ATTR_BSS]) - return NL_SKIP; - if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], - bss_policy)) - return NL_SKIP; - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->assoc_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", - _arg->assoc_freq); - } - if (status == NL80211_BSS_STATUS_IBSS_JOINED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->ibss_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", - _arg->ibss_freq); - } - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_BSSID]) { - os_memcpy(_arg->assoc_bssid, - nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: Associated with " - MACSTR, MAC2STR(_arg->assoc_bssid)); - } - } - if (!res) - return NL_SKIP; - if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { - ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); - ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); - } else { - ie = NULL; - ie_len = 0; - } - if (bss[NL80211_BSS_BEACON_IES]) { - beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); - beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); - } else { - beacon_ie = NULL; - beacon_ie_len = 0; - } - - if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, - ie ? ie_len : beacon_ie_len)) - return NL_SKIP; - - r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); - if (r == NULL) - return NL_SKIP; - if (bss[NL80211_BSS_BSSID]) - os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), - ETH_ALEN); - if (bss[NL80211_BSS_FREQUENCY]) - r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - if (bss[NL80211_BSS_BEACON_INTERVAL]) - r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); - if (bss[NL80211_BSS_CAPABILITY]) - r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); - r->flags |= WPA_SCAN_NOISE_INVALID; - if (bss[NL80211_BSS_SIGNAL_MBM]) { - r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); - r->level /= 100; /* mBm to dBm */ - r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; - } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { - r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); - r->flags |= WPA_SCAN_QUAL_INVALID; - } else - r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; - if (bss[NL80211_BSS_TSF]) - r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); - if (bss[NL80211_BSS_SEEN_MS_AGO]) - r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); - r->ie_len = ie_len; - pos = (u8 *) (r + 1); - if (ie) { - os_memcpy(pos, ie, ie_len); - pos += ie_len; - } - r->beacon_ie_len = beacon_ie_len; - if (beacon_ie) - os_memcpy(pos, beacon_ie, beacon_ie_len); - - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - switch (status) { - case NL80211_BSS_STATUS_AUTHENTICATED: - r->flags |= WPA_SCAN_AUTHENTICATED; - break; - case NL80211_BSS_STATUS_ASSOCIATED: - r->flags |= WPA_SCAN_ASSOCIATED; - break; - default: - break; - } - } - - /* - * cfg80211 maintains separate BSS table entries for APs if the same - * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does - * not use frequency as a separate key in the BSS table, so filter out - * duplicated entries. Prefer associated BSS entry in such a case in - * order to get the correct frequency into the BSS table. Similarly, - * prefer newer entries over older. - */ - for (i = 0; i < res->num; i++) { - const u8 *s1, *s2; - if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) - continue; - - s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); - if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || - os_memcmp(s1, s2, 2 + s1[1]) != 0) - continue; - - /* Same BSSID,SSID was already included in scan results */ - wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " - "for " MACSTR, MAC2STR(r->bssid)); - - if (((r->flags & WPA_SCAN_ASSOCIATED) && - !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || - r->age < res->res[i]->age) { - os_free(res->res[i]); - res->res[i] = r; - } else - os_free(r); - return NL_SKIP; - } - - tmp = os_realloc_array(res->res, res->num + 1, - sizeof(struct wpa_scan_res *)); - if (tmp == NULL) { - os_free(r); - return NL_SKIP; - } - tmp[res->num++] = r; - res->res = tmp; - - return NL_SKIP; -} - - -static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, - const u8 *addr) -{ - if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { - wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " - "mismatch (" MACSTR ")", MAC2STR(addr)); - wpa_driver_nl80211_mlme(drv, addr, - NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID, 1); - } -} - - -static void wpa_driver_nl80211_check_bss_status( - struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) -{ - size_t i; - - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - if (r->flags & WPA_SCAN_AUTHENTICATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicates BSS status with " MACSTR - " as authenticated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && - os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID" - " in local state (auth=" MACSTR - " assoc=" MACSTR ")", - MAC2STR(drv->auth_bssid), - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - } - } - - if (r->flags & WPA_SCAN_ASSOCIATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicate BSS status with " MACSTR - " as associated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - !drv->associated) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(not associated) does not match " - "with BSS state"); - clear_state_mismatch(drv, r->bssid); - } else if (is_sta_interface(drv->nlmode) && - os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(associated with " MACSTR ") does " - "not match with BSS state", - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - clear_state_mismatch(drv, drv->bssid); - } - } - } -} - - -static struct wpa_scan_results * -nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) -{ - struct nl_msg *msg; - struct wpa_scan_results *res; - int ret; - struct nl80211_bss_info_arg arg; - - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN); - if (nl80211_set_iface_id(msg, drv->first_bss) < 0) - goto nla_put_failure; - - arg.drv = drv; - arg.res = res; - ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); - msg = NULL; - if (ret == 0) { - wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " - "BSSes)", (unsigned long) res->num); - nl80211_get_noise_for_scan_results(drv, res); - return res; - } - wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " - "(%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); - wpa_scan_results_free(res); - return NULL; -} - - -/** - * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results - * @priv: Pointer to private wext data from wpa_driver_nl80211_init() - * Returns: Scan results on success, -1 on failure - */ -static struct wpa_scan_results * -wpa_driver_nl80211_get_scan_results(void *priv) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct wpa_scan_results *res; - - res = nl80211_get_scan_results(drv); - if (res) - wpa_driver_nl80211_check_bss_status(drv, res); - return res; -} - - -static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) -{ - struct wpa_scan_results *res; - size_t i; - - res = nl80211_get_scan_results(drv); - if (res == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); - return; - } - - wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", - (int) i, (int) res->num, MAC2STR(r->bssid), - r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", - r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); - } - - wpa_scan_results_free(res); -} - - static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) { switch (alg) { @@ -5869,6 +2341,35 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], } +static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, + const u8 *key, size_t key_len) +{ + struct nl_msg *msg; + int ret; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) + return 0; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) || + nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); + return -1; + } + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Key management set key failed: ret=%d (%s)", + ret, strerror(-ret)); + } + + return ret; +} + + static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, @@ -5897,33 +2398,44 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_TDLS */ - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + if (alg == WPA_ALG_PMK && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key", + __func__); + ret = issue_key_mgmt_set_key(drv, key, key_len); + return ret; + } if (alg == WPA_ALG_NONE) { - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY); + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); + if (!msg) + return -ENOBUFS; } else { - nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY); - NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY); + if (!msg || + nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) || + nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len))) + goto fail; wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len); - NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len)); } if (seq && seq_len) { - NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq); + if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq)) + goto fail; wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len); } if (addr && !is_broadcast_ether_addr(addr)) { wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto fail; if (alg != WPA_ALG_WEP && key_idx && !set_tx) { wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK"); - NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, - NL80211_KEYTYPE_GROUP); + if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, + NL80211_KEYTYPE_GROUP)) + goto fail; } } else if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; @@ -5931,15 +2443,15 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, wpa_printf(MSG_DEBUG, " broadcast key"); types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) + goto fail; nla_nest_end(msg, types); } - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx)) + goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL); if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE) ret = 0; if (ret) @@ -5956,32 +2468,28 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, !is_broadcast_ether_addr(addr)) return ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY); - NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - if (alg == WPA_ALG_IGTK) - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT); - else - NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) || + nla_put_flag(msg, alg == WPA_ALG_IGTK ? + NL80211_ATTR_KEY_DEFAULT_MGMT : + NL80211_ATTR_KEY_DEFAULT)) + goto fail; if (addr && is_broadcast_ether_addr(addr)) { struct nlattr *types; types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST)) + goto fail; nla_nest_end(msg, types); } else if (addr) { struct nlattr *types; types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES); - if (!types) - goto nla_put_failure; - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST); + if (!types || + nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST)) + goto fail; nla_nest_end(msg, types); } @@ -5993,7 +2501,8 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, "err=%d %s)", ret, strerror(-ret)); return ret; -nla_put_failure: +fail: + nl80211_nlmsg_clear(msg); nlmsg_free(msg); return -ENOBUFS; } @@ -6008,26 +2517,25 @@ static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg, if (!key_attr) return -1; - if (defkey && alg == WPA_ALG_IGTK) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT); - else if (defkey) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); - - NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx); - - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - wpa_alg_to_cipher_suite(alg, key_len)); - - if (seq && seq_len) - NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq); + if (defkey && alg == WPA_ALG_IGTK) { + if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT)) + return -1; + } else if (defkey) { + if (nla_put_flag(msg, NL80211_KEY_DEFAULT)) + return -1; + } - NLA_PUT(msg, NL80211_KEY_DATA, key_len, key); + if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) || + nla_put_u32(msg, NL80211_KEY_CIPHER, + wpa_alg_to_cipher_suite(alg, key_len)) || + (seq && seq_len && + nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) || + nla_put(msg, NL80211_KEY_DATA, key_len, key)) + return -1; nla_nest_end(msg, key_attr); return 0; - nla_put_failure: - return -1; } @@ -6052,77 +2560,60 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, if (!privacy) return 0; - NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + if (nla_put_flag(msg, NL80211_ATTR_PRIVACY)) + return -ENOBUFS; nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS); if (!nl_keys) - goto nla_put_failure; + return -ENOBUFS; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) continue; nl_key = nla_nest_start(msg, i); - if (!nl_key) - goto nla_put_failure; - - NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i], - params->wep_key[i]); - if (params->wep_key_len[i] == 5) - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP40); - else - NLA_PUT_U32(msg, NL80211_KEY_CIPHER, - WLAN_CIPHER_SUITE_WEP104); - - NLA_PUT_U8(msg, NL80211_KEY_IDX, i); - - if (i == params->wep_tx_keyidx) - NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT); + if (!nl_key || + nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i], + params->wep_key[i]) || + nla_put_u32(msg, NL80211_KEY_CIPHER, + params->wep_key_len[i] == 5 ? + WLAN_CIPHER_SUITE_WEP40 : + WLAN_CIPHER_SUITE_WEP104) || + nla_put_u8(msg, NL80211_KEY_IDX, i) || + (i == params->wep_tx_keyidx && + nla_put_flag(msg, NL80211_KEY_DEFAULT))) + return -ENOBUFS; nla_nest_end(msg, nl_key); } nla_nest_end(msg, nl_keys); return 0; - -nla_put_failure: - return -ENOBUFS; } -static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, - const u8 *addr, int cmd, u16 reason_code, - int local_state_change) +int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change) { - int ret = -1; + int ret; struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_drv_msg(drv, 0, cmd)) || + nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) || + (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || + (local_state_change && + nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) { + nlmsg_free(msg); return -1; - - nl80211_cmd(drv, msg, 0, cmd); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code); - if (addr) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - if (local_state_change) - NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed: reason=%u ret=%d (%s)", reason_code, ret, strerror(-ret)); - goto nla_put_failure; } - ret = 0; - -nla_put_failure: - nlmsg_free(msg); return ret; } @@ -6155,7 +2646,7 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, if (drv->nlmode == NL80211_IFTYPE_ADHOC) { nl80211_mark_disconnected(drv); - return nl80211_leave_ibss(drv); + return nl80211_leave_ibss(drv, 1); } if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) return wpa_driver_nl80211_disconnect(drv, reason_code); @@ -6219,6 +2710,25 @@ static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv, } +static void nl80211_unmask_11b_rates(struct i802_bss *bss) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + + if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates) + return; + + /* + * Looks like we failed to unmask 11b rates previously. This could + * happen, e.g., if the interface was down at the point in time when a + * P2P group was terminated. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them", + bss->ifname); + nl80211_disable_11b_rates(drv, drv->ifindex, 0); +} + + static int wpa_driver_nl80211_authenticate( struct i802_bss *bss, struct wpa_driver_auth_params *params) { @@ -6230,6 +2740,8 @@ static int wpa_driver_nl80211_authenticate( int count = 0; int is_retry; + nl80211_unmask_11b_rates(bss); + is_retry = drv->retry_auth; drv->retry_auth = 0; drv->ignore_deauth_event = 0; @@ -6248,14 +2760,12 @@ static int wpa_driver_nl80211_authenticate( return -1; retry: - msg = nlmsg_alloc(); - if (!msg) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE); + if (!msg) + goto fail; for (i = 0; i < 4; i++) { if (!params->wep_key[i]) @@ -6268,36 +2778,38 @@ retry: if (params->wep_tx_keyidx != i) continue; if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0, - params->wep_key[i], params->wep_key_len[i])) { - nlmsg_free(msg); - return -1; - } + params->wep_key[i], params->wep_key_len[i])) + goto fail; } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + goto fail; } if (params->freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq)) + goto fail; } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid)) + goto fail; } wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len); - if (params->ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie); + if (params->ie && + nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie)) + goto fail; if (params->sae_data) { wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, params->sae_data_len); - NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, - params->sae_data); + if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, + params->sae_data)) + goto fail; } if (params->auth_alg & WPA_AUTH_ALG_OPEN) type = NL80211_AUTHTYPE_OPEN_SYSTEM; @@ -6310,12 +2822,14 @@ retry: else if (params->auth_alg & WPA_AUTH_ALG_SAE) type = NL80211_AUTHTYPE_SAE; else - goto nla_put_failure; + goto fail; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; if (params->local_state_change) { wpa_printf(MSG_DEBUG, " * Local state change only"); - NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE); + if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -6383,21 +2897,18 @@ retry: wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT, &event); } - - goto nla_put_failure; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Authentication request send successfully"); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Authentication request send " - "successfully"); -nla_put_failure: +fail: nlmsg_free(msg); return ret; } -static int wpa_driver_nl80211_authenticate_retry( - struct wpa_driver_nl80211_data *drv) +int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv) { struct wpa_driver_auth_params params; struct i802_bss *bss = drv->first_bss; @@ -6435,726 +2946,6 @@ static int wpa_driver_nl80211_authenticate_retry( } -struct phy_info_arg { - u16 *num_modes; - struct hostapd_hw_modes *modes; - int last_mode, last_chan_idx; -}; - -static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, - struct nlattr *ampdu_factor, - struct nlattr *ampdu_density, - struct nlattr *mcs_set) -{ - if (capa) - mode->ht_capab = nla_get_u16(capa); - - if (ampdu_factor) - mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; - - if (ampdu_density) - mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; - - if (mcs_set && nla_len(mcs_set) >= 16) { - u8 *mcs; - mcs = nla_data(mcs_set); - os_memcpy(mode->mcs_set, mcs, 16); - } -} - - -static void phy_info_vht_capa(struct hostapd_hw_modes *mode, - struct nlattr *capa, - struct nlattr *mcs_set) -{ - if (capa) - mode->vht_capab = nla_get_u32(capa); - - if (mcs_set && nla_len(mcs_set) >= 8) { - u8 *mcs; - mcs = nla_data(mcs_set); - os_memcpy(mode->vht_mcs_set, mcs, 8); - } -} - - -static void phy_info_freq(struct hostapd_hw_modes *mode, - struct hostapd_channel_data *chan, - struct nlattr *tb_freq[]) -{ - u8 channel; - chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); - chan->flag = 0; - chan->dfs_cac_ms = 0; - if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) - chan->chan = channel; - - if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) - chan->flag |= HOSTAPD_CHAN_DISABLED; - if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) - chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS; - if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) - chan->flag |= HOSTAPD_CHAN_RADAR; - - if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { - enum nl80211_dfs_state state = - nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); - - switch (state) { - case NL80211_DFS_USABLE: - chan->flag |= HOSTAPD_CHAN_DFS_USABLE; - break; - case NL80211_DFS_AVAILABLE: - chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; - break; - case NL80211_DFS_UNAVAILABLE: - chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; - break; - } - } - - if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) { - chan->dfs_cac_ms = nla_get_u32( - tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]); - } -} - - -static int phy_info_freqs(struct phy_info_arg *phy_info, - struct hostapd_hw_modes *mode, struct nlattr *tb) -{ - static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, - [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, - [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, - [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, - }; - int new_channels = 0; - struct hostapd_channel_data *channel; - struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; - struct nlattr *nl_freq; - int rem_freq, idx; - - if (tb == NULL) - return NL_OK; - - nla_for_each_nested(nl_freq, tb, rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_freq), nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - new_channels++; - } - - channel = os_realloc_array(mode->channels, - mode->num_channels + new_channels, - sizeof(struct hostapd_channel_data)); - if (!channel) - return NL_SKIP; - - mode->channels = channel; - mode->num_channels += new_channels; - - idx = phy_info->last_chan_idx; - - nla_for_each_nested(nl_freq, tb, rem_freq) { - nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_freq), nla_len(nl_freq), freq_policy); - if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) - continue; - phy_info_freq(mode, &mode->channels[idx], tb_freq); - idx++; - } - phy_info->last_chan_idx = idx; - - return NL_OK; -} - - -static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) -{ - static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { - [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, - [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = - { .type = NLA_FLAG }, - }; - struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; - struct nlattr *nl_rate; - int rem_rate, idx; - - if (tb == NULL) - return NL_OK; - - nla_for_each_nested(nl_rate, tb, rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, - nla_data(nl_rate), nla_len(nl_rate), - rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->num_rates++; - } - - mode->rates = os_calloc(mode->num_rates, sizeof(int)); - if (!mode->rates) - return NL_SKIP; - - idx = 0; - - nla_for_each_nested(nl_rate, tb, rem_rate) { - nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, - nla_data(nl_rate), nla_len(nl_rate), - rate_policy); - if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) - continue; - mode->rates[idx] = nla_get_u32( - tb_rate[NL80211_BITRATE_ATTR_RATE]); - idx++; - } - - return NL_OK; -} - - -static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) -{ - struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; - struct hostapd_hw_modes *mode; - int ret; - - if (phy_info->last_mode != nl_band->nla_type) { - mode = os_realloc_array(phy_info->modes, - *phy_info->num_modes + 1, - sizeof(*mode)); - if (!mode) - return NL_SKIP; - phy_info->modes = mode; - - mode = &phy_info->modes[*(phy_info->num_modes)]; - os_memset(mode, 0, sizeof(*mode)); - mode->mode = NUM_HOSTAPD_MODES; - mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | - HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; - - /* - * Unsupported VHT MCS stream is defined as value 3, so the VHT - * MCS RX/TX map must be initialized with 0xffff to mark all 8 - * possible streams as unsupported. This will be overridden if - * driver advertises VHT support. - */ - mode->vht_mcs_set[0] = 0xff; - mode->vht_mcs_set[1] = 0xff; - mode->vht_mcs_set[4] = 0xff; - mode->vht_mcs_set[5] = 0xff; - - *(phy_info->num_modes) += 1; - phy_info->last_mode = nl_band->nla_type; - phy_info->last_chan_idx = 0; - } else - mode = &phy_info->modes[*(phy_info->num_modes) - 1]; - - nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), - nla_len(nl_band), NULL); - - phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], - tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], - tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], - tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); - phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], - tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); - ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); - if (ret != NL_OK) - return ret; - ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); - if (ret != NL_OK) - return ret; - - return NL_OK; -} - - -static int phy_info_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct phy_info_arg *phy_info = arg; - struct nlattr *nl_band; - int rem_band; - - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) - return NL_SKIP; - - nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) - { - int res = phy_info_band(phy_info, nl_band); - if (res != NL_OK) - return res; - } - - return NL_SKIP; -} - - -static struct hostapd_hw_modes * -wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, - u16 *num_modes) -{ - u16 m; - struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; - int i, mode11g_idx = -1; - - /* heuristic to set up modes */ - for (m = 0; m < *num_modes; m++) { - if (!modes[m].num_channels) - continue; - if (modes[m].channels[0].freq < 4000) { - modes[m].mode = HOSTAPD_MODE_IEEE80211B; - for (i = 0; i < modes[m].num_rates; i++) { - if (modes[m].rates[i] > 200) { - modes[m].mode = HOSTAPD_MODE_IEEE80211G; - break; - } - } - } else if (modes[m].channels[0].freq > 50000) - modes[m].mode = HOSTAPD_MODE_IEEE80211AD; - else - modes[m].mode = HOSTAPD_MODE_IEEE80211A; - } - - /* If only 802.11g mode is included, use it to construct matching - * 802.11b mode data. */ - - for (m = 0; m < *num_modes; m++) { - if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) - return modes; /* 802.11b already included */ - if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) - mode11g_idx = m; - } - - if (mode11g_idx < 0) - return modes; /* 2.4 GHz band not supported at all */ - - nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); - if (nmodes == NULL) - return modes; /* Could not add 802.11b mode */ - - mode = &nmodes[*num_modes]; - os_memset(mode, 0, sizeof(*mode)); - (*num_modes)++; - modes = nmodes; - - mode->mode = HOSTAPD_MODE_IEEE80211B; - - mode11g = &modes[mode11g_idx]; - mode->num_channels = mode11g->num_channels; - mode->channels = os_malloc(mode11g->num_channels * - sizeof(struct hostapd_channel_data)); - if (mode->channels == NULL) { - (*num_modes)--; - return modes; /* Could not add 802.11b mode */ - } - os_memcpy(mode->channels, mode11g->channels, - mode11g->num_channels * sizeof(struct hostapd_channel_data)); - - mode->num_rates = 0; - mode->rates = os_malloc(4 * sizeof(int)); - if (mode->rates == NULL) { - os_free(mode->channels); - (*num_modes)--; - return modes; /* Could not add 802.11b mode */ - } - - for (i = 0; i < mode11g->num_rates; i++) { - if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && - mode11g->rates[i] != 55 && mode11g->rates[i] != 110) - continue; - mode->rates[mode->num_rates] = mode11g->rates[i]; - mode->num_rates++; - if (mode->num_rates == 4) - break; - } - - if (mode->num_rates == 0) { - os_free(mode->channels); - os_free(mode->rates); - (*num_modes)--; - return modes; /* No 802.11b rates */ - } - - wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " - "information"); - - return modes; -} - - -static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, - int end) -{ - int c; - - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (chan->freq - 10 >= start && chan->freq + 10 <= end) - chan->flag |= HOSTAPD_CHAN_HT40; - } -} - - -static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, - int end) -{ - int c; - - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (!(chan->flag & HOSTAPD_CHAN_HT40)) - continue; - if (chan->freq - 30 >= start && chan->freq - 10 <= end) - chan->flag |= HOSTAPD_CHAN_HT40MINUS; - if (chan->freq + 10 >= start && chan->freq + 30 <= end) - chan->flag |= HOSTAPD_CHAN_HT40PLUS; - } -} - - -static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, - struct phy_info_arg *results) -{ - u16 m; - - for (m = 0; m < *results->num_modes; m++) { - int c; - struct hostapd_hw_modes *mode = &results->modes[m]; - - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if ((u32) chan->freq - 10 >= start && - (u32) chan->freq + 10 <= end) - chan->max_tx_power = max_eirp; - } - } -} - - -static void nl80211_reg_rule_ht40(u32 start, u32 end, - struct phy_info_arg *results) -{ - u16 m; - - for (m = 0; m < *results->num_modes; m++) { - if (!(results->modes[m].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - continue; - nl80211_set_ht40_mode(&results->modes[m], start, end); - } -} - - -static void nl80211_reg_rule_sec(struct nlattr *tb[], - struct phy_info_arg *results) -{ - u32 start, end, max_bw; - u16 m; - - if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) - return; - - start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; - end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; - max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; - - if (max_bw < 20) - return; - - for (m = 0; m < *results->num_modes; m++) { - if (!(results->modes[m].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - continue; - nl80211_set_ht40_mode_sec(&results->modes[m], start, end); - } -} - - -static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, - int end) -{ - int c; - - for (c = 0; c < mode->num_channels; c++) { - struct hostapd_channel_data *chan = &mode->channels[c]; - if (chan->freq - 10 >= start && chan->freq + 70 <= end) - chan->flag |= HOSTAPD_CHAN_VHT_10_70; - - if (chan->freq - 30 >= start && chan->freq + 50 <= end) - chan->flag |= HOSTAPD_CHAN_VHT_30_50; - - if (chan->freq - 50 >= start && chan->freq + 30 <= end) - chan->flag |= HOSTAPD_CHAN_VHT_50_30; - - if (chan->freq - 70 >= start && chan->freq + 10 <= end) - chan->flag |= HOSTAPD_CHAN_VHT_70_10; - } -} - - -static void nl80211_reg_rule_vht(struct nlattr *tb[], - struct phy_info_arg *results) -{ - u32 start, end, max_bw; - u16 m; - - if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || - tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) - return; - - start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; - end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; - max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; - - if (max_bw < 80) - return; - - for (m = 0; m < *results->num_modes; m++) { - if (!(results->modes[m].ht_capab & - HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) - continue; - /* TODO: use a real VHT support indication */ - if (!results->modes[m].vht_capab) - continue; - - nl80211_set_vht_mode(&results->modes[m], start, end); - } -} - - -static const char * dfs_domain_name(enum nl80211_dfs_regions region) -{ - switch (region) { - case NL80211_DFS_UNSET: - return "DFS-UNSET"; - case NL80211_DFS_FCC: - return "DFS-FCC"; - case NL80211_DFS_ETSI: - return "DFS-ETSI"; - case NL80211_DFS_JP: - return "DFS-JP"; - default: - return "DFS-invalid"; - } -} - - -static int nl80211_get_reg(struct nl_msg *msg, void *arg) -{ - struct phy_info_arg *results = arg; - struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - struct nlattr *nl_rule; - struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; - int rem_rule; - static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { - [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, - [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, - [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, - }; - - nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || - !tb_msg[NL80211_ATTR_REG_RULES]) { - wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " - "available"); - return NL_SKIP; - } - - if (tb_msg[NL80211_ATTR_DFS_REGION]) { - enum nl80211_dfs_regions dfs_domain; - dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); - wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", - (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), - dfs_domain_name(dfs_domain)); - } else { - wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", - (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); - } - - nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) - { - u32 start, end, max_eirp = 0, max_bw = 0, flags = 0; - nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_rule), nla_len(nl_rule), reg_policy); - if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || - tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) - continue; - start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; - end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; - if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) - max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; - if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) - max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; - if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS]) - flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]); - - wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s", - start, end, max_bw, max_eirp, - flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "", - flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "", - flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "", - flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" : - "", - flags & NL80211_RRF_DFS ? " (DFS)" : "", - flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "", - flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "", - flags & NL80211_RRF_NO_IR ? " (no IR)" : ""); - if (max_bw >= 40) - nl80211_reg_rule_ht40(start, end, results); - if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) - nl80211_reg_rule_max_eirp(start, end, max_eirp, - results); - } - - nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) - { - nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_rule), nla_len(nl_rule), reg_policy); - nl80211_reg_rule_sec(tb_rule, results); - } - - nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) - { - nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, - nla_data(nl_rule), nla_len(nl_rule), reg_policy); - nl80211_reg_rule_vht(tb_rule, results); - } - - return NL_SKIP; -} - - -static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, - struct phy_info_arg *results) -{ - struct nl_msg *msg; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); - return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); -} - - -static struct hostapd_hw_modes * -wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) -{ - u32 feat; - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - struct phy_info_arg result = { - .num_modes = num_modes, - .modes = NULL, - .last_mode = -1, - }; - - *num_modes = 0; - *flags = 0; - - msg = nlmsg_alloc(); - if (!msg) - return NULL; - - feat = get_nl80211_protocol_features(drv); - if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY); - else - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY); - - NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP); - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - - if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { - nl80211_set_regulatory_flags(drv, &result); - return wpa_driver_nl80211_postprocess_modes(result.modes, - num_modes); - } - msg = NULL; - nla_put_failure: - nlmsg_free(msg); - return NULL; -} - - -static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv, - const void *data, size_t len, - int encrypt, int noack) -{ - __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 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, - }; - int res; - u16 txflags = 0; - - if (encrypt) - rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; - - if (drv->monitor_sock < 0) { - wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " - "for %s", __func__); - return -1; - } - - if (noack) - txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; - WPA_PUT_LE16(&rtap_hdr[12], txflags); - - res = sendmsg(drv->monitor_sock, &msg, 0); - if (res < 0) { - wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); - return -1; - } - return 0; -} - - static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, const void *data, size_t len, int encrypt, int noack, @@ -7178,10 +2969,9 @@ static int wpa_driver_nl80211_send_frame(struct i802_bss *bss, } if (drv->use_monitor) { - wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr", + wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor", freq, bss->freq); - return wpa_driver_nl80211_send_mntr(drv, data, len, - encrypt, noack); + return nl80211_send_monitor(drv, data, len, encrypt, noack); } wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd"); @@ -7287,22 +3077,18 @@ static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS); - - if (cts >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts); - if (preamble >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble); - if (slot >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot); - if (ht_opmode >= 0) - NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode); - if (ap_isolate >= 0) - NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) || + (cts >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) || + (preamble >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) || + (slot >= 0 && + nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) || + (ht_opmode >= 0 && + nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) || + (ap_isolate >= 0 && + nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate))) + goto fail; if (basic_rates) { u8 rates[NL80211_MAX_SUPP_RATES]; @@ -7313,13 +3099,13 @@ static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble, i++) rates[rates_len++] = basic_rates[i] / 5; - NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates); + if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, + rates)) + goto fail; } - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: +fail: nlmsg_free(msg); return -ENOBUFS; } @@ -7333,7 +3119,7 @@ static int wpa_driver_nl80211_set_acl(void *priv, struct nl_msg *msg; struct nlattr *acl; unsigned int i; - int ret = 0; + int ret; if (!(drv->capa.max_acl_mac_addrs)) return -ENOTSUP; @@ -7341,40 +3127,33 @@ static int wpa_driver_nl80211_set_acl(void *priv, if (params->num_mac_acl > drv->capa.max_acl_mac_addrs) return -ENOTSUP; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)", params->acl_policy ? "Accept" : "Deny", params->num_mac_acl); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? - NL80211_ACL_POLICY_DENY_UNLESS_LISTED : - NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED); - - acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS); - if (acl == NULL) - goto nla_put_failure; + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) || + nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ? + NL80211_ACL_POLICY_DENY_UNLESS_LISTED : + NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) || + (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) { + nlmsg_free(msg); + return -ENOMEM; + } - for (i = 0; i < params->num_mac_acl; i++) - NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr); + for (i = 0; i < params->num_mac_acl; i++) { + if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) { + nlmsg_free(msg); + return -ENOMEM; + } + } nla_nest_end(msg, acl); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)", ret, strerror(-ret)); } -nla_put_failure: - nlmsg_free(msg); - return ret; } @@ -7388,75 +3167,81 @@ static int wpa_driver_nl80211_set_ap(void *priv, u8 cmd = NL80211_CMD_NEW_BEACON; int ret; int beacon_set; - int ifindex = if_nametoindex(bss->ifname); int num_suites; + int smps_mode; u32 suites[10], suite; u32 ver; beacon_set = bss->beacon_set; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)", beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; - nl80211_cmd(drv, msg, 0, cmd); wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", params->head, params->head_len); - NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head); wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail", params->tail, params->tail_len); - NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail); - wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); + wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex); wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); - NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period); wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); + if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || + nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, + params->head) || + nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, + params->tail) || + nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, + params->beacon_int) || + nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) + goto fail; if (params->proberesp && params->proberesp_len) { wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)", params->proberesp, params->proberesp_len); - NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, - params->proberesp); + if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len, + params->proberesp)) + goto fail; } switch (params->hide_ssid) { case NO_SSID_HIDING: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use"); - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_NOT_IN_USE); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_NOT_IN_USE)) + goto fail; break; case HIDDEN_SSID_ZERO_LEN: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len"); - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_ZERO_LEN); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_LEN)) + goto fail; break; case HIDDEN_SSID_ZERO_CONTENTS: wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents"); - NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID, - NL80211_HIDDEN_SSID_ZERO_CONTENTS); + if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID, + NL80211_HIDDEN_SSID_ZERO_CONTENTS)) + goto fail; break; } wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy); - if (params->privacy) - NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY); + if (params->privacy && + nla_put_flag(msg, NL80211_ATTR_PRIVACY)) + goto fail; wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs); if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) == (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) { /* Leave out the attribute */ - } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, - NL80211_AUTHTYPE_SHARED_KEY); - else - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, - NL80211_AUTHTYPE_OPEN_SYSTEM); + } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) { + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_SHARED_KEY)) + goto fail; + } else { + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, + NL80211_AUTHTYPE_OPEN_SYSTEM)) + goto fail; + } wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version); ver = 0; @@ -7464,8 +3249,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, ver |= NL80211_WPA_VERSION_1; if (params->wpa_version & WPA_PROTO_RSN) ver |= NL80211_WPA_VERSION_2; - if (ver) - NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + if (ver && + nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) + goto fail; wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x", params->key_mgmt_suites); @@ -7474,56 +3260,82 @@ static int wpa_driver_nl80211_set_ap(void *priv, suites[num_suites++] = WLAN_AKM_SUITE_8021X; if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) suites[num_suites++] = WLAN_AKM_SUITE_PSK; - if (num_suites) { - NLA_PUT(msg, NL80211_ATTR_AKM_SUITES, - num_suites * sizeof(u32), suites); - } + if (num_suites && + nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32), + suites)) + goto fail; - if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X && - params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT); + if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA && + params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) && + nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)) + goto fail; wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x", params->pairwise_ciphers); num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers, suites, ARRAY_SIZE(suites)); - if (num_suites) { - NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, - num_suites * sizeof(u32), suites); - } + if (num_suites && + nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + num_suites * sizeof(u32), suites)) + goto fail; wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x", params->group_cipher); suite = wpa_cipher_to_cipher_suite(params->group_cipher); - if (suite) - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite); + if (suite && + nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite)) + goto fail; + + switch (params->smps_mode) { + case HT_CAP_INFO_SMPS_DYNAMIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic"); + smps_mode = NL80211_SMPS_DYNAMIC; + break; + case HT_CAP_INFO_SMPS_STATIC: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static"); + smps_mode = NL80211_SMPS_STATIC; + break; + default: + /* invalid - fallback to smps off */ + case HT_CAP_INFO_SMPS_DISABLED: + wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off"); + smps_mode = NL80211_SMPS_OFF; + break; + } + if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + goto fail; if (params->beacon_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies", params->beacon_ies); - NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies), - wpabuf_head(params->beacon_ies)); + if (nla_put(msg, NL80211_ATTR_IE, + wpabuf_len(params->beacon_ies), + wpabuf_head(params->beacon_ies))) + goto fail; } if (params->proberesp_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies", params->proberesp_ies); - NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, - wpabuf_len(params->proberesp_ies), - wpabuf_head(params->proberesp_ies)); + if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP, + wpabuf_len(params->proberesp_ies), + wpabuf_head(params->proberesp_ies))) + goto fail; } if (params->assocresp_ies) { wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies", params->assocresp_ies); - NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP, - wpabuf_len(params->assocresp_ies), - wpabuf_head(params->assocresp_ies)); + if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP, + wpabuf_len(params->assocresp_ies), + wpabuf_head(params->assocresp_ies))) + goto fail; } if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) { wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d", params->ap_max_inactivity); - NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, - params->ap_max_inactivity); + if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT, + params->ap_max_inactivity)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -7561,65 +3373,67 @@ static int wpa_driver_nl80211_set_ap(void *priv, } } return ret; - nla_put_failure: +fail: nlmsg_free(msg); return -ENOBUFS; } static int nl80211_put_freq_params(struct nl_msg *msg, - struct hostapd_freq_params *freq) + const struct hostapd_freq_params *freq) { - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq)) + return -ENOBUFS; + if (freq->vht_enabled) { + enum nl80211_chan_width cw; + switch (freq->bandwidth) { case 20: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_20); + cw = NL80211_CHAN_WIDTH_20; break; case 40: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_40); + cw = NL80211_CHAN_WIDTH_40; break; case 80: if (freq->center_freq2) - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_80P80); + cw = NL80211_CHAN_WIDTH_80P80; else - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_80); + cw = NL80211_CHAN_WIDTH_80; break; case 160: - NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH, - NL80211_CHAN_WIDTH_160); + cw = NL80211_CHAN_WIDTH_160; break; default: return -EINVAL; } - NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1); - if (freq->center_freq2) - NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2, - freq->center_freq2); + + if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) || + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, + freq->center_freq1) || + (freq->center_freq2 && + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, + freq->center_freq2))) + return -ENOBUFS; } else if (freq->ht_enabled) { + enum nl80211_channel_type ct; + switch (freq->sec_channel_offset) { case -1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40MINUS); + ct = NL80211_CHAN_HT40MINUS; break; case 1: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT40PLUS); + ct = NL80211_CHAN_HT40PLUS; break; default: - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - NL80211_CHAN_HT20); + ct = NL80211_CHAN_HT20; break; } + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct)) + return -ENOBUFS; } return 0; - -nla_put_failure: - return -ENOBUFS; } @@ -7634,27 +3448,21 @@ static int nl80211_set_channel(struct i802_bss *bss, "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)", freq->freq, freq->ht_enabled, freq->vht_enabled, freq->bandwidth, freq->center_freq1, freq->center_freq2); - msg = nlmsg_alloc(); - if (!msg) - return -1; - nl80211_cmd(drv, msg, 0, set_chan ? NL80211_CMD_SET_CHANNEL : - NL80211_CMD_SET_WIPHY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (nl80211_put_freq_params(msg, freq) < 0) - goto nla_put_failure; + msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL : + NL80211_CMD_SET_WIPHY); + if (!msg || nl80211_put_freq_params(msg, freq) < 0) { + nlmsg_free(msg); + return -1; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret == 0) { bss->freq = freq->freq; return 0; } wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): " "%d (%s)", freq->freq, ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return -1; } @@ -7673,11 +3481,40 @@ static u32 sta_flags_nl80211(int flags) f |= BIT(NL80211_STA_FLAG_MFP); if (flags & WPA_STA_TDLS_PEER) f |= BIT(NL80211_STA_FLAG_TDLS_PEER); + if (flags & WPA_STA_AUTHENTICATED) + f |= BIT(NL80211_STA_FLAG_AUTHENTICATED); return f; } +#ifdef CONFIG_MESH +static u32 sta_plink_state_nl80211(enum mesh_plink_state state) +{ + switch (state) { + case PLINK_LISTEN: + return NL80211_PLINK_LISTEN; + case PLINK_OPEN_SENT: + return NL80211_PLINK_OPN_SNT; + case PLINK_OPEN_RCVD: + return NL80211_PLINK_OPN_RCVD; + case PLINK_CNF_RCVD: + return NL80211_PLINK_CNF_RCVD; + case PLINK_ESTAB: + return NL80211_PLINK_ESTAB; + case PLINK_HOLDING: + return NL80211_PLINK_HOLDING; + case PLINK_BLOCKED: + return NL80211_PLINK_BLOCKED; + default: + wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d", + state); + } + return -1; +} +#endif /* CONFIG_MESH */ + + static int wpa_driver_nl80211_sta_add(void *priv, struct hostapd_sta_add_params *params) { @@ -7691,25 +3528,57 @@ static int wpa_driver_nl80211_sta_add(void *priv, !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR, params->set ? "Set" : "Add", MAC2STR(params->addr)); - nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION : - NL80211_CMD_NEW_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len, - params->supp_rates); - wpa_hexdump(MSG_DEBUG, " * supported rates", params->supp_rates, - params->supp_rates_len); + msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION : + NL80211_CMD_NEW_STATION); + if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr)) + goto fail; + + if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) { + wpa_hexdump(MSG_DEBUG, " * supported rates", + params->supp_rates, params->supp_rates_len); + wpa_printf(MSG_DEBUG, " * capability=0x%x", + params->capability); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES, + params->supp_rates_len, params->supp_rates) || + nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY, + params->capability)) + goto fail; + + if (params->ht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * ht_capabilities", + (u8 *) params->ht_capabilities, + sizeof(*params->ht_capabilities)); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, + sizeof(*params->ht_capabilities), + params->ht_capabilities)) + goto fail; + } + + if (params->vht_capabilities) { + wpa_hexdump(MSG_DEBUG, " * vht_capabilities", + (u8 *) params->vht_capabilities, + sizeof(*params->vht_capabilities)); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, + sizeof(*params->vht_capabilities), + params->vht_capabilities)) + goto fail; + } + + if (params->ext_capab) { + wpa_hexdump(MSG_DEBUG, " * ext_capab", + params->ext_capab, params->ext_capab_len); + if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY, + params->ext_capab_len, params->ext_capab)) + goto fail; + } + } if (!params->set) { if (params->aid) { wpa_printf(MSG_DEBUG, " * aid=%u", params->aid); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid)) + goto fail; } else { /* * cfg80211 validates that AID is non-zero, so we have @@ -7717,85 +3586,71 @@ static int wpa_driver_nl80211_sta_add(void *priv, * a dummy STA entry is used for now. */ wpa_printf(MSG_DEBUG, " * aid=1 (TDLS workaround)"); - NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1)) + goto fail; } wpa_printf(MSG_DEBUG, " * listen_interval=%u", params->listen_interval); - NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, - params->listen_interval); + if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, + params->listen_interval)) + goto fail; } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) { wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid); - NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid); - } - if (params->ht_capabilities) { - wpa_hexdump(MSG_DEBUG, " * ht_capabilities", - (u8 *) params->ht_capabilities, - sizeof(*params->ht_capabilities)); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, - sizeof(*params->ht_capabilities), - params->ht_capabilities); - } - - if (params->vht_capabilities) { - wpa_hexdump(MSG_DEBUG, " * vht_capabilities", - (u8 *) params->vht_capabilities, - sizeof(*params->vht_capabilities)); - NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, - sizeof(*params->vht_capabilities), - params->vht_capabilities); + if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid)) + goto fail; } if (params->vht_opmode_enabled) { wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode); - NLA_PUT_U8(msg, NL80211_ATTR_OPMODE_NOTIF, - params->vht_opmode); - } - - wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); - NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability); - - if (params->ext_capab) { - wpa_hexdump(MSG_DEBUG, " * ext_capab", - params->ext_capab, params->ext_capab_len); - NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY, - params->ext_capab_len, params->ext_capab); + if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF, + params->vht_opmode)) + goto fail; } if (params->supp_channels) { wpa_hexdump(MSG_DEBUG, " * supported channels", params->supp_channels, params->supp_channels_len); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, - params->supp_channels_len, params->supp_channels); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS, + params->supp_channels_len, params->supp_channels)) + goto fail; } if (params->supp_oper_classes) { wpa_hexdump(MSG_DEBUG, " * supported operating classes", params->supp_oper_classes, params->supp_oper_classes_len); - NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, - params->supp_oper_classes_len, - params->supp_oper_classes); + if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + params->supp_oper_classes_len, + params->supp_oper_classes)) + goto fail; } os_memset(&upd, 0, sizeof(upd)); - upd.mask = sta_flags_nl80211(params->flags); - upd.set = upd.mask; + upd.set = sta_flags_nl80211(params->flags); + upd.mask = upd.set | sta_flags_nl80211(params->flags_mask); wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x", upd.set, upd.mask); - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) + goto fail; + +#ifdef CONFIG_MESH + if (params->plink_state && + nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE, + sta_plink_state_nl80211(params->plink_state))) + goto fail; +#endif /* CONFIG_MESH */ if (params->flags & WPA_STA_WMM) { struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME); - if (!wme) - goto nla_put_failure; - wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo); - NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES, - params->qosinfo & WMM_QOSINFO_STA_AC_MASK); - NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP, - (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & - WMM_QOSINFO_STA_SP_MASK); + if (!wme || + nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES, + params->qosinfo & WMM_QOSINFO_STA_AC_MASK) || + nla_put_u8(msg, NL80211_STA_WME_MAX_SP, + (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) & + WMM_QOSINFO_STA_SP_MASK)) + goto fail; nla_nest_end(msg, wme); } @@ -7807,7 +3662,7 @@ static int wpa_driver_nl80211_sta_add(void *priv, strerror(-ret)); if (ret == -EEXIST) ret = 0; - nla_put_failure: +fail: nlmsg_free(msg); return ret; } @@ -7850,21 +3705,26 @@ static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr) } -static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr) +static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr, + int deauth, u16 reason_code) { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + (deauth == 0 && + nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE, + WLAN_FC_STYPE_DISASSOC)) || + (deauth == 1 && + nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE, + WLAN_FC_STYPE_DEAUTH)) || + (reason_code && + nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR @@ -7877,14 +3737,10 @@ static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr) if (ret == -ENOENT) return 0; return ret; - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } -static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, - int ifidx) +void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx) { struct nl_msg *msg; struct wpa_driver_nl80211_data *drv2; @@ -7896,18 +3752,9 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, struct wpa_driver_nl80211_data, list) del_ifidx(drv2, ifidx); - msg = nlmsg_alloc(); - if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); - + msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE); if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return; - msg = NULL; - nla_put_failure: - nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx); } @@ -7955,40 +3802,37 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)", iftype, nl80211_iftype_str(iftype)); - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE); - if (nl80211_set_iface_id(msg, drv->first_bss) < 0) - goto nla_put_failure; - NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE); + if (!msg || + nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) || + nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype)) + goto fail; if (iftype == NL80211_IFTYPE_MONITOR) { struct nlattr *flags; flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS); - if (!flags) - goto nla_put_failure; - - NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES); + if (!flags || + nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES)) + goto fail; nla_nest_end(msg, flags); } else if (wds) { - NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds); + if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds)) + goto fail; } /* * Tell cfg80211 that the interface belongs to the socket that created * it, and the interface should be deleted when the socket is closed. */ - NLA_PUT_FLAG(msg, NL80211_ATTR_IFACE_SOCKET_OWNER); + if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) + goto fail; ret = send_and_recv_msgs(drv, msg, handler, arg); msg = NULL; if (ret) { - nla_put_failure: + fail: nlmsg_free(msg); wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)", ifname, ret, strerror(-ret)); @@ -8027,11 +3871,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv, } -static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, - const char *ifname, enum nl80211_iftype iftype, - const u8 *addr, int wds, - int (*handler)(struct nl_msg *, void *), - void *arg, int use_existing) +int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype, + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg, int use_existing) { int ret; @@ -8065,423 +3909,14 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, wds, handler, arg); } - if (ret >= 0 && is_p2p_net_interface(iftype)) + if (ret >= 0 && is_p2p_net_interface(iftype)) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s created for P2P - disable 11b rates", + ifname); nl80211_disable_11b_rates(drv, ret, 1); - - return ret; -} - - -static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = buf; - event.tx_status.data_len = len; - event.tx_status.ack = ok; - wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); -} - - -static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len) -{ - struct ieee80211_hdr *hdr = (void *)buf; - u16 fc; - union wpa_event_data event; - - if (len < sizeof(*hdr)) - return; - - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); - event.rx_from_unknown.addr = hdr->addr2; - event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == - (WLAN_FC_FROMDS | WLAN_FC_TODS); - wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); -} - - -static void handle_frame(struct wpa_driver_nl80211_data *drv, - u8 *buf, size_t len, int datarate, int ssi_signal) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - - hdr = (struct ieee80211_hdr *) buf; - fc = le_to_host16(hdr->frame_control); - - switch (WLAN_FC_GET_TYPE(fc)) { - case WLAN_FC_TYPE_MGMT: - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = buf; - event.rx_mgmt.frame_len = len; - event.rx_mgmt.datarate = datarate; - event.rx_mgmt.ssi_signal = ssi_signal; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); - break; - case WLAN_FC_TYPE_CTRL: - /* can only get here with PS-Poll frames */ - wpa_printf(MSG_DEBUG, "CTRL"); - from_unknown_sta(drv, buf, len); - break; - case WLAN_FC_TYPE_DATA: - from_unknown_sta(drv, buf, len); - break; - } -} - - -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 datarate = 0, ssi_signal = 0; - int injected = 0, failed = 0, rxflags = 0; - - len = recv(sock, buf, sizeof(buf), 0); - if (len < 0) { - wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", - strerror(errno)); - return; - } - - if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) { - wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); - return; - } - - while (1) { - ret = ieee80211_radiotap_iterator_next(&iter); - if (ret == -ENOENT) - break; - if (ret) { - wpa_printf(MSG_INFO, "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((*(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 */ - break; - case IEEE80211_RADIOTAP_RATE: - datarate = *iter.this_arg * 5; - break; - case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: - ssi_signal = (s8) *iter.this_arg; - break; - } - } - - if (rxflags && injected) - return; - - if (!injected) - handle_frame(drv, buf + iter._max_length, - len - iter._max_length, datarate, ssi_signal); - else - handle_tx_callback(drv->ctx, buf + iter._max_length, - len - iter._max_length, !failed); -} - - -/* - * 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. - * - * For now allow all To DS data frames through. - */ - /* load the IEEE 802.11 frame control field */ - BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), - /* mask off frame type, version and DS status */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), - /* accept frame if version 0, type 2 and To DS, fall through otherwise - */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), - -#if 0 - /* - * drop non-data 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), -#endif - /* load the upper byte of the frame control field */ - BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), - /* mask off toDS/fromDS */ - BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), - /* accept WDS frames */ - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), - - /* - * 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 = ARRAY_SIZE(msock_filter_insns), - .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))) { - wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", - strerror(errno)); - return -1; - } - - return 0; -} - - -static void nl80211_remove_monitor_interface( - struct wpa_driver_nl80211_data *drv) -{ - if (drv->monitor_refcount > 0) - drv->monitor_refcount--; - wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", - drv->monitor_refcount); - if (drv->monitor_refcount > 0) - return; - - if (drv->monitor_ifidx >= 0) { - nl80211_remove_iface(drv, drv->monitor_ifidx); - drv->monitor_ifidx = -1; - } - if (drv->monitor_sock >= 0) { - eloop_unregister_read_sock(drv->monitor_sock); - close(drv->monitor_sock); - drv->monitor_sock = -1; - } -} - - -static int -nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) -{ - char buf[IFNAMSIZ]; - struct sockaddr_ll ll; - int optval; - socklen_t optlen; - - if (drv->monitor_ifidx >= 0) { - drv->monitor_refcount++; - wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", - drv->monitor_refcount); - return 0; - } - - if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { - /* - * P2P interface name is of the format p2p-%s-%d. For monitor - * interface name corresponding to P2P GO, replace "p2p-" with - * "mon-" to retain the same interface name length and to - * indicate that it is a monitor interface. - */ - snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); - } else { - /* Non-P2P interface with AP functionality. */ - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); - } - - buf[IFNAMSIZ - 1] = '\0'; - - drv->monitor_ifidx = - nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, - 0, NULL, NULL, 0); - - if (drv->monitor_ifidx == -EOPNOTSUPP) { - /* - * This is backward compatibility for a few versions of - * the kernel only that didn't advertise the right - * attributes for the only driver that then supported - * AP mode w/o monitor -- ath6kl. - */ - wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " - "monitor interface type - try to run without it"); - drv->device_ap_sme = 1; - } - - if (drv->monitor_ifidx < 0) - return -1; - - if (linux_set_iface_flags(drv->global->ioctl_sock, 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) { - wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", - strerror(errno)); - 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) { - wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", - strerror(errno)); - goto error; - } - - optlen = sizeof(optval); - optval = 20; - if (setsockopt - (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { - wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", - strerror(errno)); - goto error; - } - - if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, - drv, NULL)) { - wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); - goto error; } - drv->monitor_refcount++; - return 0; - error: - nl80211_remove_monitor_interface(drv); - return -1; + return ret; } @@ -8643,7 +4078,6 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, int flags_or, int flags_and) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *flags; struct nl80211_sta_flag_update upd; @@ -8653,47 +4087,38 @@ static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr, bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and, !!(total_flags & WPA_STA_AUTHORIZED)); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) + goto fail; /* * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This * can be removed eventually. */ flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS); - if (!flags) - goto nla_put_failure; - if (total_flags & WPA_STA_AUTHORIZED) - NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED); - - if (total_flags & WPA_STA_WMM) - NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME); - - if (total_flags & WPA_STA_SHORT_PREAMBLE) - NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE); - - if (total_flags & WPA_STA_MFP) - NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP); - - if (total_flags & WPA_STA_TDLS_PEER) - NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER); + if (!flags || + ((total_flags & WPA_STA_AUTHORIZED) && + nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) || + ((total_flags & WPA_STA_WMM) && + nla_put_flag(msg, NL80211_STA_FLAG_WME)) || + ((total_flags & WPA_STA_SHORT_PREAMBLE) && + nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) || + ((total_flags & WPA_STA_MFP) && + nla_put_flag(msg, NL80211_STA_FLAG_MFP)) || + ((total_flags & WPA_STA_TDLS_PEER) && + nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER))) + goto fail; nla_nest_end(msg, flags); os_memset(&upd, 0, sizeof(upd)); upd.mask = sta_flags_nl80211(flags_or | ~flags_and); upd.set = sta_flags_nl80211(flags_or); - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) + goto fail; - return send_and_recv_msgs(drv, msg, NULL, NULL); - nla_put_failure: + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); +fail: nlmsg_free(msg); return -ENOBUFS; } @@ -8728,36 +4153,29 @@ static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv, } -static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv) +static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv, + int reset_mode) { struct nl_msg *msg; - int ret = -1; - - msg = nlmsg_alloc(); - if (!msg) - return -1; + int ret; - nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d " "(%s)", ret, strerror(-ret)); - goto nla_put_failure; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Leave IBSS request sent successfully"); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully"); - -nla_put_failure: - if (wpa_driver_nl80211_set_mode(drv->first_bss, + if (reset_mode && + wpa_driver_nl80211_set_mode(drv->first_bss, NL80211_IFTYPE_STATION)) { wpa_printf(MSG_INFO, "nl80211: Failed to set interface into " "station mode"); } - nlmsg_free(msg); return ret; } @@ -8778,20 +4196,14 @@ static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv, } retry: - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) || + params->ssid == NULL || params->ssid_len > sizeof(drv->ssid)) + goto fail; wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) + goto fail; os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; @@ -8804,22 +4216,24 @@ retry: wpa_printf(MSG_DEBUG, " * center_freq2=%d", params->freq.center_freq2); wpa_printf(MSG_DEBUG, " * bandwidth=%d", params->freq.bandwidth); if (nl80211_put_freq_params(msg, ¶ms->freq) < 0) - goto nla_put_failure; + goto fail; if (params->beacon_int > 0) { wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int); - NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, - params->beacon_int); + if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, + params->beacon_int)) + goto fail; } ret = nl80211_set_conn_keys(params, msg); if (ret) - goto nla_put_failure; + goto fail; if (params->bssid && params->fixed_bssid) { wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR, MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + goto fail; } if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || @@ -8827,15 +4241,17 @@ retry: params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { wpa_printf(MSG_DEBUG, " * control port"); - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) + goto fail; } if (params->wpa_ie) { wpa_hexdump(MSG_DEBUG, " * Extra IEs for Beacon/Probe Response frames", params->wpa_ie, params->wpa_ie_len); - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); + if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -8847,17 +4263,16 @@ retry: if (ret == -EALREADY && count == 1) { wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after " "forced leave"); - nl80211_leave_ibss(drv); + nl80211_leave_ibss(drv, 0); nlmsg_free(msg); goto retry; } - - goto nla_put_failure; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Join IBSS request sent successfully"); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully"); -nla_put_failure: +fail: nlmsg_free(msg); return ret; } @@ -8867,56 +4282,61 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) { - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) + return -1; } if (params->bssid_hint) { wpa_printf(MSG_DEBUG, " * bssid_hint=" MACSTR, MAC2STR(params->bssid_hint)); - NLA_PUT(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN, - params->bssid_hint); + if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN, + params->bssid_hint)) + return -1; } if (params->freq.freq) { wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq.freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + params->freq.freq)) + return -1; drv->assoc_freq = params->freq.freq; } else drv->assoc_freq = 0; if (params->freq_hint) { wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ_HINT, - params->freq_hint); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT, + params->freq_hint)) + return -1; } if (params->bg_scan_period >= 0) { wpa_printf(MSG_DEBUG, " * bg scan period=%d", params->bg_scan_period); - NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD, - params->bg_scan_period); + if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD, + params->bg_scan_period)) + return -1; } if (params->ssid) { wpa_hexdump_ascii(MSG_DEBUG, " * SSID", params->ssid, params->ssid_len); - NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len, - params->ssid); + if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid)) + return -1; if (params->ssid_len > sizeof(drv->ssid)) - goto nla_put_failure; + return -1; os_memcpy(drv->ssid, params->ssid, params->ssid_len); drv->ssid_len = params->ssid_len; } wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len); - if (params->wpa_ie) - NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len, - params->wpa_ie); + if (params->wpa_ie && + nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie)) + return -1; if (params->wpa_proto) { enum nl80211_wpa_versions ver = 0; @@ -8927,13 +4347,16 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, ver |= NL80211_WPA_VERSION_2; wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver); - NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver); + if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver)) + return -1; } if (params->pairwise_suite != WPA_CIPHER_NONE) { u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite); wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher); + if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + cipher)) + return -1; } if (params->group_suite == WPA_CIPHER_GTK_NOT_USED && @@ -8946,7 +4369,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, } else if (params->group_suite != WPA_CIPHER_NONE) { u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite); wpa_printf(MSG_DEBUG, " * group=0x%x", cipher); - NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher); + if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher)) + return -1; } if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || @@ -8956,7 +4380,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_CCKM || params->key_mgmt_suite == WPA_KEY_MGMT_OSEN || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || - params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) { + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { int mgmt = WLAN_AKM_SUITE_PSK; switch (params->key_mgmt_suite) { @@ -8981,47 +4406,67 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, case WPA_KEY_MGMT_OSEN: mgmt = WLAN_AKM_SUITE_OSEN; break; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + mgmt = WLAN_AKM_SUITE_8021X_SUITE_B; + break; case WPA_KEY_MGMT_PSK: default: mgmt = WLAN_AKM_SUITE_PSK; break; } wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); - NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt); + if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt)) + return -1; } - NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT); + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) + return -1; + + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + return -1; - if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED) - NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED); + if (params->rrm_used) { + u32 drv_rrm_flags = drv->capa.rrm_flags; + if (!(drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || + !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) || + nla_put_flag(msg, NL80211_ATTR_USE_RRM)) + return -1; + } - if (params->disable_ht) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT)) + return -1; if (params->htcaps && params->htcaps_mask) { int sz = sizeof(struct ieee80211_ht_capabilities); wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps); wpa_hexdump(MSG_DEBUG, " * htcaps_mask", params->htcaps_mask, sz); - NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, - params->htcaps_mask); + if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz, + params->htcaps) || + nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz, + params->htcaps_mask)) + return -1; } #ifdef CONFIG_VHT_OVERRIDES if (params->disable_vht) { wpa_printf(MSG_DEBUG, " * VHT disabled"); - NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT); + if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT)) + return -1; } if (params->vhtcaps && params->vhtcaps_mask) { int sz = sizeof(struct ieee80211_vht_capabilities); wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz); - NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps); wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask", params->vhtcaps_mask, sz); - NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, - params->vhtcaps_mask); + if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz, + params->vhtcaps) || + nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz, + params->vhtcaps_mask)) + return -1; } #endif /* CONFIG_VHT_OVERRIDES */ @@ -9029,8 +4474,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, " * P2P group"); return 0; -nla_put_failure: - return -1; } @@ -9043,16 +4486,24 @@ static int wpa_driver_nl80211_try_connect( int ret; int algs; - msg = nlmsg_alloc(); - if (!msg) - return -1; + if (params->req_key_mgmt_offload && params->psk && + (params->key_mgmt_suite == WPA_KEY_MGMT_PSK || + params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { + wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK"); + ret = issue_key_mgmt_set_key(drv, params->psk, 32); + if (ret) + return ret; + } wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); + if (!msg) + return -1; ret = nl80211_connect_common(drv, params, msg); if (ret) - goto nla_put_failure; + goto fail; algs = 0; if (params->auth_alg & WPA_AUTH_ALG_OPEN) @@ -9076,27 +4527,28 @@ static int wpa_driver_nl80211_try_connect( else if (params->auth_alg & WPA_AUTH_ALG_FT) type = NL80211_AUTHTYPE_FT; else - goto nla_put_failure; + goto fail; wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type); + if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; skip_auth_type: ret = nl80211_set_conn_keys(params, msg); if (ret) - goto nla_put_failure; + goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " "(%s)", ret, strerror(-ret)); - goto nla_put_failure; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Connect request send successfully"); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully"); -nla_put_failure: +fail: nlmsg_free(msg); return ret; @@ -9139,9 +4591,11 @@ static int wpa_driver_nl80211_associate( { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; + int ret = -1; struct nl_msg *msg; + nl80211_unmask_11b_rates(bss); + if (params->mode == IEEE80211_MODE_AP) return wpa_driver_nl80211_ap(drv, params); @@ -9159,23 +4613,22 @@ static int wpa_driver_nl80211_associate( nl80211_mark_disconnected(drv); - msg = nlmsg_alloc(); - if (!msg) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE); + if (!msg) + return -1; ret = nl80211_connect_common(drv, params, msg); if (ret) - goto nla_put_failure; + goto fail; if (params->prev_bssid) { wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR, MAC2STR(params->prev_bssid)); - NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, - params->prev_bssid); + if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + params->prev_bssid)) + goto fail; } ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -9185,13 +4638,12 @@ static int wpa_driver_nl80211_associate( "nl80211: MLME command failed (assoc): ret=%d (%s)", ret, strerror(-ret)); nl80211_dump_scan(drv); - goto nla_put_failure; + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Association request send successfully"); } - ret = 0; - wpa_printf(MSG_DEBUG, "nl80211: Association request send " - "successfully"); -nla_put_failure: +fail: nlmsg_free(msg); return ret; } @@ -9206,20 +4658,15 @@ static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)", ifindex, mode, nl80211_iftype_str(mode)); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE); - if (nl80211_set_iface_id(msg, drv->first_bss) < 0) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode); + msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE); + if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode)) + goto fail; ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (!ret) return 0; -nla_put_failure: +fail: nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:" " %d (%s)", ifindex, mode, ret, strerror(-ret)); @@ -9281,7 +4728,7 @@ static int wpa_driver_nl80211_set_mode_impl( * on a frequency that the mode is disallowed in. */ if (desired_freq_params) { - res = i802_set_freq(bss, desired_freq_params); + res = nl80211_set_channel(bss, desired_freq_params, 0); if (res) { wpa_printf(MSG_DEBUG, "nl80211: Failed to set frequency on interface"); @@ -9322,10 +4769,17 @@ done: return ret; } - if (is_p2p_net_interface(nlmode)) + if (is_p2p_net_interface(nlmode)) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode change to P2P - disable 11b rates", + bss->ifname); nl80211_disable_11b_rates(drv, drv->ifindex, 1); - else if (drv->disabled_11b_rates) + } else if (drv->disabled_11b_rates) { + wpa_printf(MSG_DEBUG, + "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates", + bss->ifname); nl80211_disable_11b_rates(drv, drv->ifindex, 0); + } if (is_ap_interface(nlmode)) { nl80211_mgmt_unsubscribe(bss, "start AP"); @@ -9339,7 +4793,12 @@ done: nl80211_mgmt_unsubscribe(bss, "mode change"); } + if (is_mesh_interface(nlmode) && + nl80211_mgmt_subscribe_mesh(bss)) + return -1; + if (!bss->in_deinit && !is_ap_interface(nlmode) && + !is_mesh_interface(nlmode) && nl80211_mgmt_subscribe_non_ap(bss) < 0) wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action " "frame processing - ignore for now"); @@ -9348,37 +4807,8 @@ done: } -static int dfs_info_handler(struct nl_msg *msg, void *arg) -{ - struct nlattr *tb[NL80211_ATTR_MAX + 1]; - struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); - int *dfs_capability_ptr = arg; - - nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), - genlmsg_attrlen(gnlh, 0), NULL); - - if (tb[NL80211_ATTR_VENDOR_DATA]) { - struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; - struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; - - nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, - nla_data(nl_vend), nla_len(nl_vend), NULL); - - if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) { - u32 val; - val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]); - wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u", - val); - *dfs_capability_ptr = val; - } - } - - return NL_SKIP; -} - - -static int wpa_driver_nl80211_set_mode(struct i802_bss *bss, - enum nl80211_iftype nlmode) +int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode) { return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL); } @@ -9397,9 +4827,6 @@ static int wpa_driver_nl80211_get_capa(void *priv, { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct nl_msg *msg; - int dfs_capability = 0; - int ret = 0; if (!drv->has_capability) return -1; @@ -9410,37 +4837,7 @@ static int wpa_driver_nl80211_get_capa(void *priv, capa->extended_capa_len = drv->extended_capa_len; } - if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - !drv->allow_p2p_device) { - wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)"); - capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; - } - - if (drv->dfs_vendor_cmd_avail == 1) { - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA); - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, - QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY); - - ret = send_and_recv_msgs(drv, msg, dfs_info_handler, - &dfs_capability); - if (!ret) { - if (dfs_capability) - capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; - } - } - - return ret; - - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return 0; } @@ -9464,7 +4861,7 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nl80211_sta_flag_update upd; - int ret = -ENOBUFS; + int ret; if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) { wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated"); @@ -9474,28 +4871,21 @@ static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized) wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for " MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid)); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid); - os_memset(&upd, 0, sizeof(upd)); upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED); if (authorized) upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED); - NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd); + + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) || + nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (!ret) return 0; - nla_put_failure: - nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)", ret, strerror(-ret)); return ret; @@ -9546,23 +4936,18 @@ static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY); - - 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)); + msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0, + NL80211_CMD_GET_KEY); + if (!msg || + (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) || + nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) { + nlmsg_free(msg); + return -ENOBUFS; + } memset(seq, 0, 6); return send_and_recv_msgs(drv, msg, get_key_handler, seq); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -9571,28 +4956,23 @@ static int i802_set_rts(void *priv, int rts) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -ENOBUFS; + int ret; u32 val; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - if (rts >= 2347) val = (u32) -1; else val = rts; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (!ret) return 0; -nla_put_failure: - nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: " "%d (%s)", rts, ret, strerror(-ret)); return ret; @@ -9604,28 +4984,23 @@ static int i802_set_frag(void *priv, int frag) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -ENOBUFS; + int ret; u32 val; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - if (frag >= 2346) val = (u32) -1; else val = frag; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (!ret) return 0; -nla_put_failure: - nlmsg_free(msg); wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold " "%d: %d (%s)", frag, ret, strerror(-ret)); return ret; @@ -9635,33 +5010,22 @@ nla_put_failure: static int i802_flush(void *priv) { struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; int res; - msg = nlmsg_alloc(); - if (!msg) - return -1; - wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)", bss->ifname); - nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION); /* * XXX: FIX! this needs to flush all VLANs too */ - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - - res = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION); + res = send_and_recv_msgs(bss->drv, msg, NULL, NULL); if (res) { wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d " "(%s)", res, strerror(-res)); } return res; - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -9724,23 +5088,17 @@ static int i802_read_sta_data(struct i802_bss *bss, struct hostap_sta_driver_data *data, const u8 *addr) { - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; os_memset(data, 0, sizeof(*data)); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return -ENOBUFS; + } - return send_and_recv_msgs(drv, msg, get_sta_handler, data); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data); } @@ -9752,43 +5110,45 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, struct nl_msg *msg; struct nlattr *txq, *params; - msg = nlmsg_alloc(); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY); if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS); if (!txq) - goto nla_put_failure; + goto fail; /* We are only sending parameters for a single TXQ at a time */ params = nla_nest_start(msg, 1); if (!params) - goto nla_put_failure; + goto fail; switch (queue) { case 0: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO); + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO)) + goto fail; break; case 1: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI); + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI)) + goto fail; break; case 2: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE); + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE)) + goto fail; break; case 3: - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK); + if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK)) + goto fail; break; } /* Burst time is configured in units of 0.1 msec and TXOP parameter in * 32 usec, so need to convert the value here. */ - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32); - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min); - NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max); - NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs); + if (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)) + goto fail; nla_nest_end(msg, params); @@ -9797,7 +5157,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) return 0; msg = NULL; - nla_put_failure: +fail: nlmsg_free(msg); return -1; } @@ -9808,34 +5168,26 @@ static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr, { struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -ENOBUFS; - - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; + int ret; wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR ", ifname=%s[%d], vlan_id=%d)", bss->ifname, if_nametoindex(bss->ifname), MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, - if_nametoindex(bss->ifname)); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN, - if_nametoindex(ifname)); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret < 0) { wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr=" MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)", MAC2STR(addr), ifname, vlan_id, ret, strerror(-ret)); } - nla_put_failure: - nlmsg_free(msg); return ret; } @@ -9869,8 +5221,11 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (is_mesh_interface(drv->nlmode)) + return -1; + if (drv->device_ap_sme) - return wpa_driver_nl80211_sta_remove(bss, addr); + return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason); memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -9893,8 +5248,11 @@ static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + if (is_mesh_interface(drv->nlmode)) + return -1; + if (drv->device_ap_sme) - return wpa_driver_nl80211_sta_remove(bss, addr); + return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason); memset(&mgmt, 0, sizeof(mgmt)); mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, @@ -9922,7 +5280,7 @@ static void dump_ifidx(struct wpa_driver_nl80211_data *drv) if (!drv->if_indices[i]) continue; res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) break; pos += res; } @@ -10071,12 +5429,12 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, struct i802_bss *bss, const char *brname, const char *ifname) { - int ifindex; + int br_ifindex; char in_br[IFNAMSIZ]; os_strlcpy(bss->brname, brname, IFNAMSIZ); - ifindex = if_nametoindex(brname); - if (ifindex == 0) { + br_ifindex = if_nametoindex(brname); + if (br_ifindex == 0) { /* * Bridge was configured, but the bridge device does * not exist. Try to add it now. @@ -10088,8 +5446,10 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, return -1; } bss->added_bridge = 1; - add_ifidx(drv, if_nametoindex(brname)); + br_ifindex = if_nametoindex(brname); + add_ifidx(drv, br_ifindex); } + bss->br_ifindex = br_ifindex; if (linux_br_get(in_br, ifname) == 0) { if (os_strcmp(in_br, brname) == 0) @@ -10133,7 +5493,7 @@ static void *i802_init(struct hostapd_data *hapd, bss = wpa_driver_nl80211_drv_init(hapd, params->ifname, params->global_priv, 1, - params->bssid); + params->bssid, params->driver_params); if (bss == NULL) return NULL; @@ -10143,10 +5503,12 @@ static void *i802_init(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s", params->ifname, brname); br_ifindex = if_nametoindex(brname); + os_strlcpy(bss->brname, brname, IFNAMSIZ); } else { brname[0] = '\0'; br_ifindex = 0; } + bss->br_ifindex = br_ifindex; for (i = 0; i < params->num_bridge; i++) { if (params->bridge[i]) { @@ -10157,16 +5519,21 @@ static void *i802_init(struct hostapd_data *hapd, br_added = 1; } } - if (!br_added && br_ifindex && - (params->num_bridge == 0 || !params->bridge[0])) - add_ifidx(drv, br_ifindex); /* start listening for EAPOL on the default AP interface */ add_ifidx(drv, drv->ifindex); - if (params->num_bridge && params->bridge[0] && - i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0) - goto failed; + if (params->num_bridge && params->bridge[0]) { + if (i802_check_bridge(drv, bss, params->bridge[0], + params->ifname) < 0) + goto failed; + if (os_strcmp(params->bridge[0], brname) != 0) + br_added = 1; + } + + if (!br_added && br_ifindex && + (params->num_bridge == 0 || !params->bridge[0])) + add_ifidx(drv, br_ifindex); #ifdef CONFIG_LIBNL3_ROUTE if (bss->added_if_into_bridge) { @@ -10236,12 +5603,14 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type( return NL80211_IFTYPE_P2P_GO; case WPA_IF_P2P_DEVICE: return NL80211_IFTYPE_P2P_DEVICE; + case WPA_IF_MESH: + return NL80211_IFTYPE_MESH_POINT; } return -1; } -#ifdef CONFIG_P2P +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) { @@ -10255,8 +5624,7 @@ static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr) } -static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, - u8 *new_addr) +static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr) { unsigned int idx; @@ -10273,13 +5641,13 @@ static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv, if (idx == 64) return -1; - wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address " + wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address " MACSTR, MAC2STR(new_addr)); return 0; } -#endif /* CONFIG_P2P */ +#endif /* CONFIG_P2P || CONFIG_MESH */ struct wdev_info { @@ -10366,10 +5734,10 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } } -#ifdef CONFIG_P2P +#if defined(CONFIG_P2P) || defined(CONFIG_MESH) if (!addr && (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP || - type == WPA_IF_P2P_GO)) { + type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) { /* Enforce unique P2P Interface Address */ u8 new_addr[ETH_ALEN]; @@ -10381,8 +5749,9 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } if (nl80211_addr_in_use(drv->global, new_addr)) { wpa_printf(MSG_DEBUG, "nl80211: Allocate new address " - "for P2P group interface"); - if (nl80211_p2p_interface_addr(drv, new_addr) < 0) { + "for %s interface", type == WPA_IF_MESH ? + "mesh" : "P2P group"); + if (nl80211_vif_addr(drv, new_addr) < 0) { if (added) nl80211_remove_iface(drv, ifidx); return -1; @@ -10396,7 +5765,7 @@ static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type, } os_memcpy(if_addr, new_addr, ETH_ALEN); } -#endif /* CONFIG_P2P */ +#endif /* CONFIG_P2P || CONFIG_MESH */ if (type == WPA_IF_AP_BSS) { struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss)); @@ -10560,31 +5929,21 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, u64 cookie; int ret = -1; - msg = nlmsg_alloc(); - if (!msg) - return -1; - wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d " "no_ack=%d offchanok=%d", freq, wait, no_cck, no_ack, offchanok); wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len); - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME); - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - if (freq) - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - if (wait) - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait); - if (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || - drv->test_use_roc_tx)) - NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); - if (no_cck) - NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE); - if (no_ack) - NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK); - - NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) || + (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || + (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) || + (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || + drv->test_use_roc_tx) && + nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) || + (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) || + (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) || + nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf)) + goto fail; cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); @@ -10593,16 +5952,16 @@ static int nl80211_send_frame_cmd(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d " "(%s) (freq=%u wait=%u)", ret, strerror(-ret), freq, wait); - goto nla_put_failure; - } - wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " - "cookie 0x%llx", no_ack ? " (no ACK)" : "", - (long long unsigned int) cookie); + } else { + wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; " + "cookie 0x%llx", no_ack ? " (no ACK)" : "", + (long long unsigned int) cookie); - if (cookie_out) - *cookie_out = no_ack ? (u64) -1 : cookie; + if (cookie_out) + *cookie_out = no_ack ? (u64) -1 : cookie; + } -nla_put_failure: +fail: nlmsg_free(msg); return ret; } @@ -10661,26 +6020,18 @@ static void wpa_driver_nl80211_send_action_cancel_wait(void *priv) struct nl_msg *msg; int ret; - msg = nlmsg_alloc(); - if (!msg) - return; - wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx", (long long unsigned int) drv->send_action_cookie); - nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) || + nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) { + nlmsg_free(msg); + return; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d " "(%s)", ret, strerror(-ret)); - - nla_put_failure: - nlmsg_free(msg); } @@ -10693,21 +6044,15 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, int ret; u64 cookie; - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || + nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) { + nlmsg_free(msg); return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); - NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); + } cookie = 0; ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie); - msg = NULL; if (ret == 0) { wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie " "0x%llx for freq=%u MHz duration=%u", @@ -10719,8 +6064,6 @@ static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq, wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel " "(freq=%d duration=%u): %d (%s)", freq, duration, ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return -1; } @@ -10742,25 +6085,18 @@ static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv) "0x%llx", (long long unsigned int) drv->remain_on_chan_cookie); - msg = nlmsg_alloc(); - if (!msg) + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); + if (!msg || + nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) { + nlmsg_free(msg); return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL); - - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie); + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: " "%d (%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return -1; } @@ -10825,16 +6161,19 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, struct nlattr *bands, *band; int ret; - msg = nlmsg_alloc(); + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)", + ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" : + "no NL80211_TXRATE_LEGACY constraint"); + + msg = nl80211_ifindex_msg(drv, ifindex, 0, + NL80211_CMD_SET_TX_BITRATE_MASK); if (!msg) return -1; - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex); - bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); if (!bands) - goto nla_put_failure; + goto fail; /* * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything @@ -10842,18 +6181,15 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, * rates. All 5 GHz rates are left enabled. */ band = nla_nest_start(msg, NL80211_BAND_2GHZ); - if (!band) - goto nla_put_failure; - if (disabled) { - NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8, - "\x0c\x12\x18\x24\x30\x48\x60\x6c"); - } + if (!band || + (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c"))) + goto fail; nla_nest_end(msg, band); nla_nest_end(msg, bands); ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d " "(%s)", ret, strerror(-ret)); @@ -10862,7 +6198,7 @@ static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv, return ret; -nla_put_failure: +fail: nlmsg_free(msg); return -1; } @@ -10875,6 +6211,7 @@ static int wpa_driver_nl80211_deinit_ap(void *priv) if (!is_ap_interface(drv->nlmode)) return -1; wpa_driver_nl80211_del_beacon(drv); + bss->beacon_set = 0; /* * If the P2P GO interface was dynamically added, then it is @@ -10926,86 +6263,26 @@ static void wpa_driver_nl80211_resume(void *priv) } -static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len) -{ - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; - u8 *data, *pos; - size_t data_len; - const u8 *own_addr = bss->addr; - - if (action != 1) { - wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action " - "action %d", action); - return -1; - } - - /* - * Action frame payload: - * Category[1] = 6 (Fast BSS Transition) - * Action[1] = 1 (Fast BSS Transition Request) - * STA Address - * Target AP Address - * FT IEs - */ - - data_len = 2 + 2 * ETH_ALEN + ies_len; - data = os_malloc(data_len); - if (data == NULL) - return -1; - pos = data; - *pos++ = 0x06; /* FT Action category */ - *pos++ = action; - os_memcpy(pos, own_addr, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, target_ap, ETH_ALEN); - pos += ETH_ALEN; - os_memcpy(pos, ies, ies_len); - - ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0, - drv->bssid, own_addr, drv->bssid, - data, data_len, 0); - os_free(data); - - return ret; -} - - static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis) { struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *cqm; - int ret = -1; wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d " "hysteresis=%d", threshold, hysteresis); - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) || + !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) || + nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) || + nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) { + nlmsg_free(msg); return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - cqm = nla_nest_start(msg, NL80211_ATTR_CQM); - if (cqm == NULL) - goto nla_put_failure; - - NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold); - NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis); + } nla_nest_end(msg, cqm); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; - -nla_put_failure: - nlmsg_free(msg); - return ret; + return send_and_recv_msgs(drv, msg, NULL, NULL); } @@ -11042,18 +6319,8 @@ static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE); return send_and_recv_msgs(drv, msg, get_channel_width, sig); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -11142,12 +6409,6 @@ static int nl80211_set_param(void *priv, const char *param) drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; } - - if (os_strstr(param, "p2p_device=1")) { - struct i802_bss *bss = priv; - struct wpa_driver_nl80211_data *drv = bss->drv; - drv->allow_p2p_device = 1; - } #endif /* CONFIG_P2P */ if (os_strstr(param, "use_monitor=1")) { @@ -11258,22 +6519,14 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, { struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(bss->drv, msg, 0, cmd); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - if (pmkid) - NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid); - if (bssid) - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); + if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || + (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) || + (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) { + nlmsg_free(msg); + return -ENOBUFS; + } return send_and_recv_msgs(bss->drv, msg, NULL, NULL); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -11444,7 +6697,7 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int err = -ENOBUFS; + int err; union wpa_event_data data; struct survey_results *survey_results; @@ -11453,13 +6706,9 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) dl_list_init(&survey_results->survey_list); - msg = nlmsg_alloc(); + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); if (!msg) - goto nla_put_failure; - - nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + return -ENOBUFS; if (freq) data.survey_results.freq_filter = freq; @@ -11470,16 +6719,12 @@ static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq) survey_results); } while (err > 0); - if (err) { + if (err) wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data"); - goto out_clean; - } - - wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); + else + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); -out_clean: clean_survey_results(survey_results); -nla_put_failure: return err; } @@ -11492,29 +6737,20 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck, struct nlattr *replay_nested; struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) || + !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) || + nla_put(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek) || + nla_put(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck) || + nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, + replay_ctr)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); return; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA); - if (!replay_nested) - goto nla_put_failure; - - NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek); - NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck); - NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, - replay_ctr); + } nla_nest_end(msg, replay_nested); - send_and_recv_msgs(drv, msg, NULL, NULL); - return; - nla_put_failure: - nlmsg_free(msg); + send_and_recv_msgs(drv, msg, NULL, (void *) -1); } @@ -11568,19 +6804,13 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, return; } - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); return; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + } send_and_recv_msgs(drv, msg, NULL, NULL); - return; - nla_put_failure: - nlmsg_free(msg); } @@ -11588,18 +6818,13 @@ static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { struct nl_msg *msg; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, - enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED); + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) || + nla_put_u32(msg, NL80211_ATTR_PS_STATE, + enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) { + nlmsg_free(msg); + return -ENOBUFS; + } return send_and_recv_msgs(bss->drv, msg, NULL, NULL); -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -11646,24 +6871,17 @@ static int nl80211_start_radar_detection(void *priv, return -1; } - msg = nlmsg_alloc(); - if (!msg) + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) || + nl80211_put_freq_params(msg, freq) < 0) { + nlmsg_free(msg); return -1; - - nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - if (nl80211_put_freq_params(msg, freq) < 0) - goto nla_put_failure; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); - msg = NULL; if (ret == 0) return 0; wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: " "%d (%s)", ret, strerror(-ret)); -nla_put_failure: - nlmsg_free(msg); return -1; } @@ -11684,16 +6902,12 @@ static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, if (!dst) return -EINVAL; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token); - NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) || + nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) || + nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) || + nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code)) + goto fail; if (peer_capab) { /* * The internal enum tdls_peer_capability definition is @@ -11701,15 +6915,18 @@ static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code, * nl80211_tdls_peer_capability, so no conversion is needed * here. */ - NLA_PUT_U32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, peer_capab); + if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, + peer_capab)) + goto fail; } - if (initiator) - NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_INITIATOR); - NLA_PUT(msg, NL80211_ATTR_IE, len, buf); + if ((initiator && + nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) || + nla_put(msg, NL80211_ATTR_IE, len, buf)) + goto fail; return send_and_recv_msgs(drv, msg, NULL, NULL); -nla_put_failure: +fail: nlmsg_free(msg); return -ENOBUFS; } @@ -11749,156 +6966,73 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) return -EINVAL; } - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER); - NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) || + nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) { + nlmsg_free(msg); + return -ENOBUFS; + } return send_and_recv_msgs(drv, msg, NULL, NULL); - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } -#endif /* CONFIG TDLS */ - -#ifdef ANDROID - -typedef struct android_wifi_priv_cmd { - char *buf; - int used_len; - int total_len; -} android_wifi_priv_cmd; - -static int drv_errors = 0; - -static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) -{ - drv_errors++; - if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { - drv_errors = 0; - wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); - } -} - - -static int android_priv_cmd(struct i802_bss *bss, const char *cmd) +static int +nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct ifreq ifr; - android_wifi_priv_cmd priv_cmd; - char buf[MAX_DRV_CMD_SIZE]; - int ret; - - os_memset(&ifr, 0, sizeof(ifr)); - os_memset(&priv_cmd, 0, sizeof(priv_cmd)); - os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); - - os_memset(buf, 0, sizeof(buf)); - os_strlcpy(buf, cmd, sizeof(buf)); + struct nl_msg *msg; + int ret = -ENOBUFS; - priv_cmd.buf = buf; - priv_cmd.used_len = sizeof(buf); - priv_cmd.total_len = sizeof(buf); - ifr.ifr_data = &priv_cmd; + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) || + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; - ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); - if (ret < 0) { - wpa_printf(MSG_ERROR, "%s: failed to issue private commands", - __func__); - wpa_driver_send_hang_msg(drv); + wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR + " oper_class=%u freq=%u", + MAC2STR(addr), oper_class, params->freq); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) || + (ret = nl80211_put_freq_params(msg, params))) { + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch"); return ret; } - drv_errors = 0; - return 0; + return send_and_recv_msgs(drv, msg, NULL, NULL); } -static int android_pno_start(struct i802_bss *bss, - struct wpa_driver_scan_params *params) +static int +nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr) { + struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; - struct ifreq ifr; - android_wifi_priv_cmd priv_cmd; - int ret = 0, i = 0, bp; - char buf[WEXT_PNO_MAX_COMMAND_SIZE]; - - bp = WEXT_PNOSETUP_HEADER_SIZE; - os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); - buf[bp++] = WEXT_PNO_TLV_PREFIX; - buf[bp++] = WEXT_PNO_TLV_VERSION; - buf[bp++] = WEXT_PNO_TLV_SUBVERSION; - buf[bp++] = WEXT_PNO_TLV_RESERVED; - - while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { - /* Check that there is enough space needed for 1 more SSID, the - * other sections and null termination */ - if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + - WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) - break; - wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", - params->ssids[i].ssid, - params->ssids[i].ssid_len); - buf[bp++] = WEXT_PNO_SSID_SECTION; - buf[bp++] = params->ssids[i].ssid_len; - os_memcpy(&buf[bp], params->ssids[i].ssid, - params->ssids[i].ssid_len); - bp += params->ssids[i].ssid_len; - i++; - } - - buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", - WEXT_PNO_SCAN_INTERVAL); - bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; - - buf[bp++] = WEXT_PNO_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_REPEAT); - bp += WEXT_PNO_REPEAT_LENGTH; - - buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; - os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", - WEXT_PNO_MAX_REPEAT); - bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; - - memset(&ifr, 0, sizeof(ifr)); - memset(&priv_cmd, 0, sizeof(priv_cmd)); - os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); - - priv_cmd.buf = buf; - priv_cmd.used_len = bp; - priv_cmd.total_len = bp; - ifr.ifr_data = &priv_cmd; - - ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); - - if (ret < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", - ret); - wpa_driver_send_hang_msg(drv); - return ret; - } - - drv_errors = 0; + struct nl_msg *msg; - return android_priv_cmd(bss, "PNOFORCE 1"); -} + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) || + !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)) + return -EOPNOTSUPP; + wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR, + MAC2STR(addr)); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + wpa_printf(MSG_DEBUG, + "nl80211: Could not build TDLS cancel chan switch"); + return -ENOBUFS; + } -static int android_pno_stop(struct i802_bss *bss) -{ - return android_priv_cmd(bss, "PNOFORCE 0"); + return send_and_recv_msgs(drv, msg, NULL, NULL); } -#endif /* ANDROID */ +#endif /* CONFIG TDLS */ static int driver_nl80211_set_key(const char *ifname, void *priv, @@ -11964,7 +7098,7 @@ static int driver_nl80211_send_mlme(void *priv, const u8 *data, static int driver_nl80211_sta_remove(void *priv, const u8 *addr) { struct i802_bss *bss = priv; - return wpa_driver_nl80211_sta_remove(bss, addr); + return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0); } @@ -12014,15 +7148,13 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, struct wpa_driver_nl80211_data *drv = bss->drv; u16 mdid = WPA_GET_LE16(md); - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs"); - nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); - NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) || + nla_put(msg, NL80211_ATTR_IE, ies_len, ies) || + nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { @@ -12031,10 +7163,6 @@ static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md, } return ret; - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -12103,14 +7231,14 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) bss->added_bridge ? "added_bridge=1\n" : "", bss->in_deinit ? "in_deinit=1\n" : "", bss->if_dynamic ? "if_dynamic=1\n" : ""); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; if (bss->wdev_id_set) { res = os_snprintf(pos, end - pos, "wdev_id=%llu\n", (unsigned long long) bss->wdev_id); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -12132,7 +7260,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "monitor_refcount=%d\n" "last_mgmt_freq=%u\n" "eapol_tx_sock=%d\n" - "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + "%s%s%s%s%s%s%s%s%s%s%s%s%s", drv->phyname, MAC2STR(drv->perm_addr), drv->ifindex, @@ -12168,9 +7296,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->ignore_next_local_disconnect ? "ignore_next_local_disconnect=1\n" : "", drv->ignore_next_local_deauth ? - "ignore_next_local_deauth=1\n" : "", - drv->allow_p2p_device ? "allow_p2p_device=1\n" : ""); - if (res < 0 || res >= end - pos) + "ignore_next_local_deauth=1\n" : ""); + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -12179,7 +7306,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.key_mgmt=0x%x\n" "capa.enc=0x%x\n" "capa.auth=0x%x\n" - "capa.flags=0x%x\n" + "capa.flags=0x%llx\n" + "capa.rrm_flags=0x%x\n" "capa.max_scan_ssids=%d\n" "capa.max_sched_scan_ssids=%d\n" "capa.sched_scan_supported=%d\n" @@ -12188,11 +7316,14 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "capa.max_stations=%u\n" "capa.probe_resp_offloads=0x%x\n" "capa.max_acl_mac_addrs=%u\n" - "capa.num_multichan_concurrent=%u\n", + "capa.num_multichan_concurrent=%u\n" + "capa.mac_addr_rand_sched_scan_supported=%d\n" + "capa.mac_addr_rand_scan_supported=%d\n", drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth, - drv->capa.flags, + (unsigned long long) drv->capa.flags, + drv->capa.rrm_flags, drv->capa.max_scan_ssids, drv->capa.max_sched_scan_ssids, drv->capa.sched_scan_supported, @@ -12201,8 +7332,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) drv->capa.max_stations, drv->capa.probe_resp_offloads, drv->capa.max_acl_mac_addrs, - drv->capa.num_multichan_concurrent); - if (res < 0 || res >= end - pos) + drv->capa.num_multichan_concurrent, + drv->capa.mac_addr_rand_sched_scan_supported, + drv->capa.mac_addr_rand_scan_supported); + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -12213,35 +7346,27 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings) { - if (settings->head) - NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, - settings->head_len, settings->head); - - if (settings->tail) - NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, - settings->tail_len, settings->tail); - - if (settings->beacon_ies) - NLA_PUT(msg, NL80211_ATTR_IE, - settings->beacon_ies_len, settings->beacon_ies); - - if (settings->proberesp_ies) - NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP, - settings->proberesp_ies_len, settings->proberesp_ies); - - if (settings->assocresp_ies) - NLA_PUT(msg, - NL80211_ATTR_IE_ASSOC_RESP, - settings->assocresp_ies_len, settings->assocresp_ies); - - if (settings->probe_resp) - NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, - settings->probe_resp_len, settings->probe_resp); + if ((settings->head && + nla_put(msg, NL80211_ATTR_BEACON_HEAD, + settings->head_len, settings->head)) || + (settings->tail && + nla_put(msg, NL80211_ATTR_BEACON_TAIL, + settings->tail_len, settings->tail)) || + (settings->beacon_ies && + nla_put(msg, NL80211_ATTR_IE, + settings->beacon_ies_len, settings->beacon_ies)) || + (settings->proberesp_ies && + nla_put(msg, NL80211_ATTR_IE_PROBE_RESP, + settings->proberesp_ies_len, settings->proberesp_ies)) || + (settings->assocresp_ies && + nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP, + settings->assocresp_ies_len, settings->assocresp_ies)) || + (settings->probe_resp && + nla_put(msg, NL80211_ATTR_PROBE_RESP, + settings->probe_resp_len, settings->probe_resp))) + return -ENOBUFS; return 0; - -nla_put_failure: - return -ENOBUFS; } @@ -12283,20 +7408,14 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) settings->cs_count))) return -EINVAL; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count); - ret = nl80211_put_freq_params(msg, &settings->freq_params); - if (ret) + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) || + nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, + settings->cs_count) || + (ret = nl80211_put_freq_params(msg, &settings->freq_params)) || + (settings->block_tx && + nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX))) goto error; - if (settings->block_tx) - NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX); - /* beacon_after params */ ret = set_beacon_data(msg, &settings->beacon_after); if (ret) @@ -12305,18 +7424,18 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) /* beacon_csa params */ beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES); if (!beacon_csa) - goto nla_put_failure; + goto fail; ret = set_beacon_data(msg, &settings->beacon_csa); if (ret) goto error; - NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, - settings->counter_offset_beacon); - - if (settings->beacon_csa.probe_resp) - NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, - settings->counter_offset_presp); + if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON, + settings->counter_offset_beacon) || + (settings->beacon_csa.probe_resp && + nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP, + settings->counter_offset_presp))) + goto fail; nla_nest_end(msg, beacon_csa); ret = send_and_recv_msgs(drv, msg, NULL, NULL); @@ -12326,7 +7445,7 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) } return ret; -nla_put_failure: +fail: ret = -ENOBUFS; error: nlmsg_free(msg); @@ -12335,6 +7454,66 @@ error: } +static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr, + u8 user_priority, u16 admitted_time) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, + "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d", + tsid, admitted_time, user_priority); + + if (!is_sta_interface(drv->nlmode)) + return -ENOTSUP; + + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS); + if (!msg || + nla_put_u8(msg, NL80211_ATTR_TSID, tsid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) || + nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)", + ret, strerror(-ret)); + return ret; +} + + +static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid); + + if (!is_sta_interface(drv->nlmode)) + return -ENOTSUP; + + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) || + nla_put_u8(msg, NL80211_ATTR_TSID, tsid) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) + wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)", + ret, strerror(-ret)); + return ret; +} + + #ifdef CONFIG_TESTING_OPTIONS static int cmd_reply_handler(struct nl_msg *msg, void *arg) { @@ -12397,16 +7576,16 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, struct nl_msg *msg; int ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - #ifdef CONFIG_TESTING_OPTIONS if (vendor_id == 0xffffffff) { + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + nl80211_cmd(drv, msg, 0, subcmd); if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) < 0) - goto nla_put_failure; + goto fail; ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf); if (ret) wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d", @@ -12415,13 +7594,12 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, } #endif /* CONFIG_TESTING_OPTIONS */ - nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR); - if (nl80211_set_iface_id(msg, bss) < 0) - goto nla_put_failure; - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, vendor_id); - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd); - if (data) - NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, data_len, data); + if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) || + (data && + nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data))) + goto fail; ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf); if (ret) @@ -12429,7 +7607,7 @@ static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id, ret); return ret; -nla_put_failure: +fail: nlmsg_free(msg); return -ENOBUFS; } @@ -12443,26 +7621,20 @@ static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set, struct nl_msg *msg; int ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map", qos_map_set, qos_map_set_len); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) || + nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) { + nlmsg_free(msg); + return -ENOBUFS; + } ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed"); return ret; - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -12475,33 +7647,28 @@ static int nl80211_set_wowlan(void *priv, struct nlattr *wowlan_triggers; int ret; - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan"); - nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WOWLAN); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - - wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS); - if (!wowlan_triggers) - goto nla_put_failure; - - if (triggers->any) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY); - if (triggers->disconnect) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT); - if (triggers->magic_pkt) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT); - if (triggers->gtk_rekey_failure) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE); - if (triggers->eap_identity_req) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST); - if (triggers->four_way_handshake) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE); - if (triggers->rfkill_release) - NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) || + !(wowlan_triggers = nla_nest_start(msg, + NL80211_ATTR_WOWLAN_TRIGGERS)) || + (triggers->any && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) || + (triggers->disconnect && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) || + (triggers->magic_pkt && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) || + (triggers->gtk_rekey_failure && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || + (triggers->eap_identity_req && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || + (triggers->four_way_handshake && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || + (triggers->rfkill_release && + nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) { + nlmsg_free(msg); + return -ENOBUFS; + } nla_nest_end(msg, wowlan_triggers); @@ -12510,10 +7677,6 @@ static int nl80211_set_wowlan(void *priv, wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed"); return ret; - -nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; } @@ -12532,32 +7695,22 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return -1; } - msg = nlmsg_alloc(); - if (!msg) - return -ENOMEM; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA); - NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, - QCA_NL80211_VENDOR_SUBCMD_ROAMING); - - params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); - if (!params) - goto nla_put_failure; - NLA_PUT_U32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY, - allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS : - QCA_ROAMING_NOT_ALLOWED); - if (bssid) - NLA_PUT(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid); + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ROAMING) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY, + allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS : + QCA_ROAMING_NOT_ALLOWED) || + (bssid && + nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) { + nlmsg_free(msg); + return -1; + } nla_nest_end(msg, params); return send_and_recv_msgs(drv, msg, NULL, NULL); - - nla_put_failure: - nlmsg_free(msg); - return -1; } @@ -12601,6 +7754,502 @@ static int nl80211_set_mac_addr(void *priv, const u8 *addr) } +#ifdef CONFIG_MESH + +static int wpa_driver_nl80211_init_mesh(void *priv) +{ + if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to set interface into mesh mode"); + return -1; + } + return 0; +} + + +static int +wpa_driver_nl80211_join_mesh(void *priv, + struct wpa_driver_mesh_join_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *container; + int ret = 0; + + wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH); + if (!msg) + goto fail; + if (params->freq) { + wpa_printf(MSG_DEBUG, " * freq=%d", params->freq); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq)) + goto fail; + } + + if (params->ht_mode) { + unsigned int ht_value; + char *ht_mode = ""; + + switch (params->ht_mode) { + default: + case CHAN_NO_HT: + ht_value = NL80211_CHAN_NO_HT; + ht_mode = "NOHT"; + break; + case CHAN_HT20: + ht_value = NL80211_CHAN_HT20; + ht_mode = "HT20"; + break; + case CHAN_HT40PLUS: + ht_value = NL80211_CHAN_HT40PLUS; + ht_mode = "HT40+"; + break; + case CHAN_HT40MINUS: + ht_value = NL80211_CHAN_HT40MINUS; + ht_mode = "HT40-"; + break; + } + wpa_printf(MSG_DEBUG, " * ht_mode=%s", ht_mode); + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ht_value)) + goto fail; + } + + if (params->basic_rates) { + u8 rates[NL80211_MAX_SUPP_RATES]; + u8 rates_len = 0; + int i; + + for (i = 0; i < NL80211_MAX_SUPP_RATES; i++) { + if (params->basic_rates[i] < 0) + break; + rates[rates_len++] = params->basic_rates[i] / 5; + } + + if (nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, + rates)) + goto fail; + } + + if (params->meshid) { + wpa_hexdump_ascii(MSG_DEBUG, " * SSID", + params->meshid, params->meshid_len); + if (nla_put(msg, NL80211_ATTR_MESH_ID, params->meshid_len, + params->meshid)) + goto fail; + } + + if (params->beacon_int > 0) { + wpa_printf(MSG_DEBUG, " * beacon_int=%d", params->beacon_int); + if (nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL, + params->beacon_int)) + goto fail; + } + + wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags); + + container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP); + if (!container) + goto fail; + + if (params->ies) { + wpa_hexdump(MSG_DEBUG, " * IEs", params->ies, params->ie_len); + if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len, + params->ies)) + goto fail; + } + /* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */ + if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) { + if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) || + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH)) + goto fail; + } + if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) && + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE)) + goto fail; + if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) && + nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM)) + goto fail; + nla_nest_end(msg, container); + + container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); + if (!container) + goto fail; + + if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && + nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0)) + goto fail; + if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) && + nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, + params->max_peer_links)) + goto fail; + nla_nest_end(msg, container); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + ret = 0; + bss->freq = params->freq; + wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully"); + +fail: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_leave_mesh(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: mesh leave request send successfully"); + } + + if (wpa_driver_nl80211_set_mode(drv->first_bss, + NL80211_IFTYPE_STATION)) { + wpa_printf(MSG_INFO, + "nl80211: Failed to set interface into station mode"); + } + return ret; +} + +#endif /* CONFIG_MESH */ + + +static int wpa_driver_br_add_ip_neigh(void *priv, u8 version, + const u8 *ipaddr, int prefixlen, + const u8 *addr) +{ +#ifdef CONFIG_LIBNL3_ROUTE + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr = NULL; + struct nl_addr *nl_lladdr = NULL; + int family, addrsize; + int res; + + if (!ipaddr || prefixlen == 0 || !addr) + return -EINVAL; + + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set before adding an ip neigh to it"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + if (version == 4) { + family = AF_INET; + addrsize = 4; + } else if (version == 6) { + family = AF_INET6; + addrsize = 16; + } else { + return -EINVAL; + } + + rn = rtnl_neigh_alloc(); + if (rn == NULL) + return -ENOMEM; + + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + nl_addr_set_prefixlen(nl_ipaddr, prefixlen); + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } + + /* set the corresponding lladdr for neigh */ + nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN); + if (nl_lladdr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed"); + res = -ENOMEM; + goto errout; + } + rtnl_neigh_set_lladdr(rn, nl_lladdr); + + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + rtnl_neigh_set_state(rn, NUD_PERMANENT); + + res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Adding bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_lladdr) + nl_addr_put(nl_lladdr); + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ +} + + +static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version, + const u8 *ipaddr) +{ +#ifdef CONFIG_LIBNL3_ROUTE + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct rtnl_neigh *rn; + struct nl_addr *nl_ipaddr; + int family, addrsize; + int res; + + if (!ipaddr) + return -EINVAL; + + if (version == 4) { + family = AF_INET; + addrsize = 4; + } else if (version == 6) { + family = AF_INET6; + addrsize = 16; + } else { + return -EINVAL; + } + + if (bss->br_ifindex == 0) { + wpa_printf(MSG_DEBUG, + "nl80211: bridge must be set to delete an ip neigh"); + return -1; + } + + if (!drv->rtnl_sk) { + wpa_printf(MSG_DEBUG, + "nl80211: nl_sock for NETLINK_ROUTE is not initialized"); + return -1; + } + + rn = rtnl_neigh_alloc(); + if (rn == NULL) + return -ENOMEM; + + /* set the destination ip address for neigh */ + nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize); + if (nl_ipaddr == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed"); + res = -ENOMEM; + goto errout; + } + res = rtnl_neigh_set_dst(rn, nl_ipaddr); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: neigh set destination addr failed"); + goto errout; + } + + rtnl_neigh_set_ifindex(rn, bss->br_ifindex); + + res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0); + if (res) { + wpa_printf(MSG_DEBUG, + "nl80211: Deleting bridge ip neigh failed: %s", + strerror(errno)); + } +errout: + if (nl_ipaddr) + nl_addr_put(nl_ipaddr); + if (rn) + rtnl_neigh_put(rn); + return res; +#else /* CONFIG_LIBNL3_ROUTE */ + return -1; +#endif /* CONFIG_LIBNL3_ROUTE */ +} + + +static int linux_write_system_file(const char *path, unsigned int val) +{ + char buf[50]; + int fd, len; + + len = os_snprintf(buf, sizeof(buf), "%u\n", val); + if (os_snprintf_error(sizeof(buf), len)) + return -1; + + fd = open(path, O_WRONLY); + if (fd < 0) + return -1; + + if (write(fd, buf, len) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to write Linux system file: %s with the value of %d", + path, val); + close(fd); + return -1; + } + close(fd); + + return 0; +} + + +static const char * drv_br_port_attr_str(enum drv_br_port_attr attr) +{ + switch (attr) { + case DRV_BR_PORT_ATTR_PROXYARP: + return "proxyarp"; + case DRV_BR_PORT_ATTR_HAIRPIN_MODE: + return "hairpin_mode"; + } + + return NULL; +} + + +static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr, + unsigned int val) +{ + struct i802_bss *bss = priv; + char path[128]; + const char *attr_txt; + + attr_txt = drv_br_port_attr_str(attr); + if (attr_txt == NULL) + return -EINVAL; + + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s", + bss->ifname, attr_txt); + + if (linux_write_system_file(path, val)) + return -1; + + return 0; +} + + +static const char * drv_br_net_param_str(enum drv_br_net_param param) +{ + switch (param) { + case DRV_BR_NET_PARAM_GARP_ACCEPT: + return "arp_accept"; + } + + return NULL; +} + + +static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param, + unsigned int val) +{ + struct i802_bss *bss = priv; + char path[128]; + const char *param_txt; + int ip_version = 4; + + param_txt = drv_br_net_param_str(param); + if (param_txt == NULL) + return -EINVAL; + + switch (param) { + case DRV_BR_NET_PARAM_GARP_ACCEPT: + ip_version = 4; + break; + default: + return -EINVAL; + } + + os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s", + ip_version, bss->brname, param_txt); + + if (linux_write_system_file(path, val)) + return -1; + + return 0; +} + + +static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode) +{ + switch (hw_mode) { + case HOSTAPD_MODE_IEEE80211B: + return QCA_ACS_MODE_IEEE80211B; + case HOSTAPD_MODE_IEEE80211G: + return QCA_ACS_MODE_IEEE80211G; + case HOSTAPD_MODE_IEEE80211A: + return QCA_ACS_MODE_IEEE80211A; + case HOSTAPD_MODE_IEEE80211AD: + return QCA_ACS_MODE_IEEE80211AD; + default: + return -1; + } +} + + +static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *data; + int ret; + int mode; + + mode = hw_mode_to_qca_acs(params->hw_mode); + if (mode < 0) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_DO_ACS) || + !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) || + (params->ht_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) || + (params->ht40_enabled && + nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) { + nlmsg_free(msg); + return -ENOBUFS; + } + nla_nest_end(msg, data); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to invoke driver ACS function: %s", + strerror(errno)); + } + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -12628,7 +8277,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .if_add = wpa_driver_nl80211_if_add, .if_remove = driver_nl80211_if_remove, .send_mlme = driver_nl80211_send_mlme, - .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .get_hw_feature_data = nl80211_get_hw_feature_data, .sta_add = wpa_driver_nl80211_sta_add, .sta_remove = driver_nl80211_sta_remove, .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol, @@ -12657,7 +8306,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .deinit_ap = wpa_driver_nl80211_deinit_ap, .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, - .send_ft_action = nl80211_send_ft_action, .signal_monitor = nl80211_signal_monitor, .signal_poll = nl80211_signal_poll, .send_frame = nl80211_send_frame, @@ -12675,6 +8323,8 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { #ifdef CONFIG_TDLS .send_tdls_mgmt = nl80211_send_tdls_mgmt, .tdls_oper = nl80211_tdls_oper, + .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch, + .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch, #endif /* CONFIG_TDLS */ .update_ft_ies = wpa_driver_nl80211_update_ft_ies, .get_mac_addr = wpa_driver_nl80211_get_macaddr, @@ -12694,4 +8344,16 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .set_wowlan = nl80211_set_wowlan, .roaming = nl80211_roaming, .set_mac_addr = nl80211_set_mac_addr, +#ifdef CONFIG_MESH + .init_mesh = wpa_driver_nl80211_init_mesh, + .join_mesh = wpa_driver_nl80211_join_mesh, + .leave_mesh = wpa_driver_nl80211_leave_mesh, +#endif /* CONFIG_MESH */ + .br_add_ip_neigh = wpa_driver_br_add_ip_neigh, + .br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh, + .br_port_set_attr = wpa_driver_br_port_set_attr, + .br_set_net_param = wpa_driver_br_set_net_param, + .add_tx_ts = nl80211_add_ts, + .del_tx_ts = nl80211_del_ts, + .do_acs = wpa_driver_do_acs, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h new file mode 100644 index 00000000..6892b318 --- /dev/null +++ b/src/drivers/driver_nl80211.h @@ -0,0 +1,271 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - definitions + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_NL80211_H +#define DRIVER_NL80211_H + +#include "nl80211_copy.h" +#include "utils/list.h" +#include "driver.h" + +#ifdef CONFIG_LIBNL20 +/* libnl 2.0 compatibility code */ +#define nl_handle nl_sock +#define nl80211_handle_alloc nl_socket_alloc_cb +#define nl80211_handle_destroy nl_socket_free +#endif /* CONFIG_LIBNL20 */ + +struct nl80211_global { + struct dl_list interfaces; + int if_add_ifindex; + u64 if_add_wdevid; + int if_add_wdevid_set; + struct netlink_data *netlink; + struct nl_cb *nl_cb; + struct nl_handle *nl; + int nl80211_id; + int ioctl_sock; /* socket for ioctl() use */ + + struct nl_handle *nl_event; +}; + +struct nl80211_wiphy_data { + struct dl_list list; + struct dl_list bsss; + struct dl_list drvs; + + struct nl_handle *nl_beacons; + struct nl_cb *nl_cb; + + int wiphy_idx; +}; + +struct i802_bss { + struct wpa_driver_nl80211_data *drv; + struct i802_bss *next; + int ifindex; + int br_ifindex; + u64 wdev_id; + char ifname[IFNAMSIZ + 1]; + char brname[IFNAMSIZ]; + unsigned int beacon_set:1; + unsigned int added_if_into_bridge:1; + unsigned int added_bridge:1; + unsigned int in_deinit:1; + unsigned int wdev_id_set:1; + unsigned int added_if:1; + unsigned int static_ap:1; + + u8 addr[ETH_ALEN]; + + int freq; + int bandwidth; + int if_dynamic; + + void *ctx; + struct nl_handle *nl_preq, *nl_mgmt; + struct nl_cb *nl_cb; + + struct nl80211_wiphy_data *wiphy_data; + struct dl_list wiphy_list; +}; + +struct wpa_driver_nl80211_data { + struct nl80211_global *global; + struct dl_list list; + struct dl_list wiphy_list; + char phyname[32]; + u8 perm_addr[ETH_ALEN]; + void *ctx; + int ifindex; + int if_removed; + int if_disabled; + int ignore_if_down_event; + struct rfkill_data *rfkill; + struct wpa_driver_capa capa; + u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + int has_capability; + + int operstate; + + int scan_complete_events; + enum scan_states { + NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED, + SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED, + SCHED_SCAN_RESULTS + } scan_state; + + u8 auth_bssid[ETH_ALEN]; + u8 auth_attempt_bssid[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 prev_bssid[ETH_ALEN]; + int associated; + u8 ssid[32]; + size_t ssid_len; + enum nl80211_iftype nlmode; + enum nl80211_iftype ap_scan_as_station; + unsigned int assoc_freq; + + int monitor_sock; + int monitor_ifidx; + int monitor_refcount; + + unsigned int disabled_11b_rates:1; + unsigned int pending_remain_on_chan:1; + unsigned int in_interface_list:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int scan_for_auth:1; + unsigned int retry_auth:1; + unsigned int use_monitor:1; + unsigned int ignore_next_local_disconnect:1; + unsigned int ignore_next_local_deauth:1; + unsigned int hostapd:1; + unsigned int start_mode_ap:1; + unsigned int start_iface_up:1; + unsigned int test_use_roc_tx:1; + unsigned int ignore_deauth_event:1; + unsigned int roaming_vendor_cmd_avail:1; + unsigned int dfs_vendor_cmd_avail:1; + unsigned int have_low_prio_scan:1; + unsigned int force_connect_cmd:1; + unsigned int addr_changed:1; + unsigned int get_features_vendor_cmd_avail:1; + + u64 remain_on_chan_cookie; + u64 send_action_cookie; + + unsigned int last_mgmt_freq; + + struct wpa_driver_scan_filter *filter_ssids; + size_t num_filter_ssids; + + struct i802_bss *first_bss; + + int eapol_tx_sock; + + int eapol_sock; /* socket for EAPOL frames */ + + struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */ + + int default_if_indices[16]; + int *if_indices; + int num_if_indices; + + /* From failed authentication command */ + int auth_freq; + u8 auth_bssid_[ETH_ALEN]; + u8 auth_ssid[32]; + size_t auth_ssid_len; + int auth_alg; + u8 *auth_ie; + size_t auth_ie_len; + u8 auth_wep_key[4][16]; + size_t auth_wep_key_len[4]; + int auth_wep_tx_keyidx; + int auth_local_state_change; + int auth_p2p; +}; + +struct nl_msg; + +void * nl80211_cmd(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, int flags, uint8_t cmd); +struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd); +struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags, + uint8_t cmd); +struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd); +int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data); +int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype, + const u8 *addr, int wds, + int (*handler)(struct nl_msg *, void *), + void *arg, int use_existing); +void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx); +unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv); +enum chan_width convert2width(int width); +void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv); +struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex); +int is_ap_interface(enum nl80211_iftype nlmode); +int is_sta_interface(enum nl80211_iftype nlmode); +int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv); +int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig); +int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, + struct wpa_signal_info *sig_change); +int nl80211_get_wiphy_index(struct i802_bss *bss); +int wpa_driver_nl80211_set_mode(struct i802_bss *bss, + enum nl80211_iftype nlmode); +int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, u16 reason_code, + int local_state_change); + +int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); +void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); +int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack); + +int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv); +struct hostapd_hw_modes * +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); + +int process_global_event(struct nl_msg *msg, void *arg); +int process_bss_event(struct nl_msg *msg, void *arg); + +#ifdef ANDROID +int android_nl_socket_set_nonblocking(struct nl_handle *handle); +int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name); +int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int android_pno_stop(struct i802_bss *bss); +extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, + size_t buf_len); + +#ifdef ANDROID_P2P +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration); +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len); +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow); +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp); +#endif /* ANDROID_P2P */ +#endif /* ANDROID */ + + +/* driver_nl80211_scan.c */ + +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; + unsigned int assoc_freq; + unsigned int ibss_freq; + u8 assoc_bssid[ETH_ALEN]; +}; + +int bss_info_handler(struct nl_msg *msg, void *arg); +void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); +int wpa_driver_nl80211_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params); +int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval); +int wpa_driver_nl80211_stop_sched_scan(void *priv); +struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); + +#endif /* DRIVER_NL80211_H */ diff --git a/src/drivers/driver_nl80211_android.c b/src/drivers/driver_nl80211_android.c new file mode 100644 index 00000000..3cc9a658 --- /dev/null +++ b/src/drivers/driver_nl80211_android.c @@ -0,0 +1,220 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Android specific + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <fcntl.h> + +#include "utils/common.h" +#include "driver_nl80211.h" +#include "android_drv.h" + + +typedef struct android_wifi_priv_cmd { + char *buf; + int used_len; + int total_len; +} android_wifi_priv_cmd; + +static int drv_errors = 0; + +static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) +{ + drv_errors++; + if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { + drv_errors = 0; + wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); + } +} + + +static int android_priv_cmd(struct i802_bss *bss, const char *cmd) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + char buf[MAX_DRV_CMD_SIZE]; + int ret; + + os_memset(&ifr, 0, sizeof(ifr)); + os_memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + os_memset(buf, 0, sizeof(buf)); + os_strlcpy(buf, cmd, sizeof(buf)); + + priv_cmd.buf = buf; + priv_cmd.used_len = sizeof(buf); + priv_cmd.total_len = sizeof(buf); + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: failed to issue private commands", + __func__); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + return 0; +} + + +int android_pno_start(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct ifreq ifr; + android_wifi_priv_cmd priv_cmd; + int ret = 0, i = 0, bp; + char buf[WEXT_PNO_MAX_COMMAND_SIZE]; + + bp = WEXT_PNOSETUP_HEADER_SIZE; + os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); + buf[bp++] = WEXT_PNO_TLV_PREFIX; + buf[bp++] = WEXT_PNO_TLV_VERSION; + buf[bp++] = WEXT_PNO_TLV_SUBVERSION; + buf[bp++] = WEXT_PNO_TLV_RESERVED; + + while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) { + /* Check that there is enough space needed for 1 more SSID, the + * other sections and null termination */ + if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf)) + break; + wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + buf[bp++] = WEXT_PNO_SSID_SECTION; + buf[bp++] = params->ssids[i].ssid_len; + os_memcpy(&buf[bp], params->ssids[i].ssid, + params->ssids[i].ssid_len); + bp += params->ssids[i].ssid_len; + i++; + } + + buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", + WEXT_PNO_SCAN_INTERVAL); + bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; + + buf[bp++] = WEXT_PNO_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_REPEAT); + bp += WEXT_PNO_REPEAT_LENGTH; + + buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; + os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", + WEXT_PNO_MAX_REPEAT); + bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; + + memset(&ifr, 0, sizeof(ifr)); + memset(&priv_cmd, 0, sizeof(priv_cmd)); + os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); + + priv_cmd.buf = buf; + priv_cmd.used_len = bp; + priv_cmd.total_len = bp; + ifr.ifr_data = &priv_cmd; + + ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", + ret); + wpa_driver_send_hang_msg(drv); + return ret; + } + + drv_errors = 0; + + return android_priv_cmd(bss, "PNOFORCE 1"); +} + + +int android_pno_stop(struct i802_bss *bss) +{ + return android_priv_cmd(bss, "PNOFORCE 0"); +} + + +#ifdef ANDROID_P2P +#ifdef ANDROID_P2P_STUB + +int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) +{ + return 0; +} + + +int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) +{ + return 0; +} + + +int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) +{ + return -1; +} + + +int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp, + const struct wpabuf *assocresp) +{ + return 0; +} + +#endif /* ANDROID_P2P_STUB */ +#endif /* ANDROID_P2P */ + + +int android_nl_socket_set_nonblocking(struct nl_handle *handle) +{ + return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); +} + + +int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name) +{ + /* + * Android ICS has very minimal genl_ctrl_resolve() implementation, so + * need to work around that. + */ + struct nl_cache *cache = NULL; + struct genl_family *nl80211 = NULL; + int id = -1; + + if (genl_ctrl_alloc_cache(handle, &cache) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto fail; + } + + nl80211 = genl_ctrl_search_by_name(cache, name); + if (nl80211 == NULL) + goto fail; + + id = genl_family_get_id(nl80211); + +fail: + if (nl80211) + genl_family_put(nl80211); + if (cache) + nl_cache_free(cache); + + return id; +} diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c new file mode 100644 index 00000000..6661a894 --- /dev/null +++ b/src/drivers/driver_nl80211_capa.c @@ -0,0 +1,1519 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Capabilities + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <netlink/genl/genl.h> + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" +#include "driver_nl80211.h" + + +static int protocol_feature_handler(struct nl_msg *msg, void *arg) +{ + u32 *feat = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]) + *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]); + + return NL_SKIP; +} + + +static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv) +{ + u32 feat = 0; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return 0; + + if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) { + nlmsg_free(msg); + return 0; + } + + if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0) + return feat; + + return 0; +} + + +struct wiphy_info_data { + struct wpa_driver_nl80211_data *drv; + struct wpa_driver_capa *capa; + + unsigned int num_multichan_concurrent; + + unsigned int error:1; + unsigned int device_ap_sme:1; + unsigned int poll_command_supported:1; + unsigned int data_tx_status:1; + unsigned int monitor_supported:1; + unsigned int auth_supported:1; + unsigned int connect_supported:1; + unsigned int p2p_go_supported:1; + unsigned int p2p_client_supported:1; + unsigned int p2p_concurrent:1; + unsigned int channel_switch_supported:1; + unsigned int set_qos_map_supported:1; + unsigned int have_low_prio_scan:1; + unsigned int wmm_ac_supported:1; + unsigned int mac_addr_rand_scan_supported:1; + unsigned int mac_addr_rand_sched_scan_supported:1; +}; + + +static unsigned int probe_resp_offload_support(int supp_protocols) +{ + unsigned int prot = 0; + + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P; + if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U) + prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING; + + return prot; +} + + +static void wiphy_info_supported_iftypes(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_mode; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_mode, tb, i) { + switch (nla_type(nl_mode)) { + case NL80211_IFTYPE_AP: + info->capa->flags |= WPA_DRIVER_FLAGS_AP; + break; + case NL80211_IFTYPE_MESH_POINT: + info->capa->flags |= WPA_DRIVER_FLAGS_MESH; + break; + case NL80211_IFTYPE_ADHOC: + info->capa->flags |= WPA_DRIVER_FLAGS_IBSS; + break; + case NL80211_IFTYPE_P2P_DEVICE: + info->capa->flags |= + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE; + break; + case NL80211_IFTYPE_P2P_GO: + info->p2p_go_supported = 1; + break; + case NL80211_IFTYPE_P2P_CLIENT: + info->p2p_client_supported = 1; + break; + case NL80211_IFTYPE_MONITOR: + info->monitor_supported = 1; + break; + } + } +} + + +static int wiphy_info_iface_comb_process(struct wiphy_info_data *info, + struct nlattr *nl_combi) +{ + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit, *nl_mode; + int err, rem_limit, rem_mode; + int combination_has_p2p = 0, combination_has_mgd = 0; + static struct nla_policy + iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, + }, + iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nl_combi, iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) + return 0; /* broken combination */ + + if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) + info->capa->flags |= WPA_DRIVER_FLAGS_RADAR; + + nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], + rem_limit) { + err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, + nl_limit, iface_limit_policy); + if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) + return 0; /* broken combination */ + + nla_for_each_nested(nl_mode, + tb_limit[NL80211_IFACE_LIMIT_TYPES], + rem_mode) { + int ift = nla_type(nl_mode); + if (ift == NL80211_IFTYPE_P2P_GO || + ift == NL80211_IFTYPE_P2P_CLIENT) + combination_has_p2p = 1; + if (ift == NL80211_IFTYPE_STATION) + combination_has_mgd = 1; + } + if (combination_has_p2p && combination_has_mgd) + break; + } + + if (combination_has_p2p && combination_has_mgd) { + unsigned int num_channels = + nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]); + + info->p2p_concurrent = 1; + if (info->num_multichan_concurrent < num_channels) + info->num_multichan_concurrent = num_channels; + } + + return 0; +} + + +static void wiphy_info_iface_comb(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_combi; + int rem_combi; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_combi, tb, rem_combi) { + if (wiphy_info_iface_comb_process(info, nl_combi) > 0) + break; + } +} + + +static void wiphy_info_supp_cmds(struct wiphy_info_data *info, + struct nlattr *tb) +{ + struct nlattr *nl_cmd; + int i; + + if (tb == NULL) + return; + + nla_for_each_nested(nl_cmd, tb, i) { + switch (nla_get_u32(nl_cmd)) { + case NL80211_CMD_AUTHENTICATE: + info->auth_supported = 1; + break; + case NL80211_CMD_CONNECT: + info->connect_supported = 1; + break; + case NL80211_CMD_START_SCHED_SCAN: + info->capa->sched_scan_supported = 1; + break; + case NL80211_CMD_PROBE_CLIENT: + info->poll_command_supported = 1; + break; + case NL80211_CMD_CHANNEL_SWITCH: + info->channel_switch_supported = 1; + break; + case NL80211_CMD_SET_QOS_MAP: + info->set_qos_map_supported = 1; + break; + } + } +} + + +static void wiphy_info_cipher_suites(struct wiphy_info_data *info, + struct nlattr *tb) +{ + int i, num; + u32 *ciphers; + + if (tb == NULL) + return; + + num = nla_len(tb) / sizeof(u32); + ciphers = nla_data(tb); + for (i = 0; i < num; i++) { + u32 c = ciphers[i]; + + wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d", + c >> 24, (c >> 16) & 0xff, + (c >> 8) & 0xff, c & 0xff); + switch (c) { + case WLAN_CIPHER_SUITE_CCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; + break; + case WLAN_CIPHER_SUITE_CCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; + break; + case WLAN_CIPHER_SUITE_WEP104: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; + break; + case WLAN_CIPHER_SUITE_WEP40: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; + break; + case WLAN_CIPHER_SUITE_NO_GROUP_ADDR: + info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; + break; + } + } +} + + +static void wiphy_info_max_roc(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + if (tb) + capa->max_remain_on_chan = nla_get_u32(tb); +} + + +static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls, + struct nlattr *ext_setup) +{ + if (tdls == NULL) + return; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS supported"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT; + + if (ext_setup) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP; + } +} + + +static void wiphy_info_feature_flags(struct wiphy_info_data *info, + struct nlattr *tb) +{ + u32 flags; + struct wpa_driver_capa *capa = info->capa; + + if (tb == NULL) + return; + + flags = nla_get_u32(tb); + + if (flags & NL80211_FEATURE_SK_TX_STATUS) + info->data_tx_status = 1; + + if (flags & NL80211_FEATURE_INACTIVITY_TIMER) + capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER; + + if (flags & NL80211_FEATURE_SAE) + capa->flags |= WPA_DRIVER_FLAGS_SAE; + + if (flags & NL80211_FEATURE_NEED_OBSS_SCAN) + capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN; + + if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE) + capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX; + + if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) { + wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch"); + capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH; + } + + if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN) + info->have_low_prio_scan = 1; + + if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR) + info->mac_addr_rand_scan_supported = 1; + + if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR) + info->mac_addr_rand_sched_scan_supported = 1; + + if (flags & NL80211_FEATURE_STATIC_SMPS) + capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC; + + if (flags & NL80211_FEATURE_DYNAMIC_SMPS) + capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC; + + if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) + info->wmm_ac_supported = 1; + + if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) + capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES; + + if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES) + capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES; + + if (flags & NL80211_FEATURE_QUIET) + capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET; + + if (flags & NL80211_FEATURE_TX_POWER_INSERTION) + capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION; +} + + +static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + u32 protocols; + + if (tb == NULL) + return; + + protocols = nla_get_u32(tb); + wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP " + "mode"); + capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD; + capa->probe_resp_offloads = probe_resp_offload_support(protocols); +} + + +static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa, + struct nlattr *tb) +{ + struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1]; + + if (tb == NULL) + return; + + if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG, + tb, NULL)) + return; + + if (triggers[NL80211_WOWLAN_TRIG_ANY]) + capa->wowlan_triggers.any = 1; + if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT]) + capa->wowlan_triggers.disconnect = 1; + if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT]) + capa->wowlan_triggers.magic_pkt = 1; + if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) + capa->wowlan_triggers.gtk_rekey_failure = 1; + if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) + capa->wowlan_triggers.eap_identity_req = 1; + if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) + capa->wowlan_triggers.four_way_handshake = 1; + if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) + capa->wowlan_triggers.rfkill_release = 1; +} + + +static int wiphy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct wiphy_info_data *info = arg; + struct wpa_driver_capa *capa = info->capa; + struct wpa_driver_nl80211_data *drv = info->drv; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_WIPHY_NAME]) + os_strlcpy(drv->phyname, + nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]), + sizeof(drv->phyname)); + if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) + capa->max_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]) + capa->max_sched_scan_ssids = + nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]); + + if (tb[NL80211_ATTR_MAX_MATCH_SETS]) + capa->max_match_sets = + nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]); + + if (tb[NL80211_ATTR_MAC_ACL_MAX]) + capa->max_acl_mac_addrs = + nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]); + + wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]); + wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]); + wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]); + wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]); + + if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based " + "off-channel TX"); + capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX; + } + + if (tb[NL80211_ATTR_ROAM_SUPPORT]) { + wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming"); + capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION; + } + + wiphy_info_max_roc(capa, + tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]); + + if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD]) + capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD; + + wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT], + tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]); + + if (tb[NL80211_ATTR_DEVICE_AP_SME]) + info->device_ap_sme = 1; + + wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]); + wiphy_info_probe_resp_offload(capa, + tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]); + + if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] && + drv->extended_capa == NULL) { + drv->extended_capa = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa) { + os_memcpy(drv->extended_capa, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + drv->extended_capa_len = + nla_len(tb[NL80211_ATTR_EXT_CAPA]); + } + drv->extended_capa_mask = + os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA])); + if (drv->extended_capa_mask) { + os_memcpy(drv->extended_capa_mask, + nla_data(tb[NL80211_ATTR_EXT_CAPA]), + nla_len(tb[NL80211_ATTR_EXT_CAPA])); + } else { + os_free(drv->extended_capa); + drv->extended_capa = NULL; + drv->extended_capa_len = 0; + } + } + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + switch (vinfo->subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_ROAMING: + drv->roaming_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: + drv->dfs_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: + drv->get_features_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD; + break; + } + + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + if (tb[NL80211_ATTR_VENDOR_EVENTS]) { + struct nlattr *nl; + int rem; + + nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) { + struct nl80211_vendor_cmd_info *vinfo; + if (nla_len(nl) != sizeof(*vinfo)) { + wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info"); + continue; + } + vinfo = nla_data(nl); + wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u", + vinfo->vendor_id, vinfo->subcmd); + } + } + + wiphy_info_wowlan_triggers(capa, + tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]); + + if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA]) + capa->max_stations = + nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]); + + return NL_SKIP; +} + + +static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv, + struct wiphy_info_data *info) +{ + u32 feat; + struct nl_msg *msg; + int flags = 0; + + os_memset(info, 0, sizeof(*info)); + info->capa = &drv->capa; + info->drv = drv; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + flags = NLM_F_DUMP; + msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY); + if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return -1; + } + + if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info)) + return -1; + + if (info->auth_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_SME; + else if (!info->connect_supported) { + wpa_printf(MSG_INFO, "nl80211: Driver does not support " + "authentication/association or connect commands"); + info->error = 1; + } + + if (info->p2p_go_supported && info->p2p_client_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; + if (info->p2p_concurrent) { + wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group " + "interface (driver advertised support)"); + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; + drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P; + } + if (info->num_multichan_concurrent > 1) { + wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel " + "concurrent (driver advertised support)"); + drv->capa.num_multichan_concurrent = + info->num_multichan_concurrent; + } + if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) + wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support"); + + /* default to 5000 since early versions of mac80211 don't set it */ + if (!drv->capa.max_remain_on_chan) + drv->capa.max_remain_on_chan = 5000; + + if (info->channel_switch_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA; + drv->capa.wmm_ac_supported = info->wmm_ac_supported; + + drv->capa.mac_addr_rand_sched_scan_supported = + info->mac_addr_rand_sched_scan_supported; + drv->capa.mac_addr_rand_scan_supported = + info->mac_addr_rand_scan_supported; + + return 0; +} + + +static int dfs_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + int *dfs_capability_ptr = arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) { + u32 val; + val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]); + wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u", + val); + *dfs_capability_ptr = val; + } + } + + return NL_SKIP; +} + + +static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int dfs_capability = 0; + int ret; + + if (!drv->dfs_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) { + nlmsg_free(msg); + return; + } + + ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability); + if (!ret && dfs_capability) + drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; +} + + +struct features_info { + u8 *flags; + size_t flags_len; +}; + + +static int features_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct features_info *info = arg; + struct nlattr *nl_vend, *attr; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + if (nl_vend) { + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS]; + if (attr) { + info->flags = nla_data(attr); + info->flags_len = nla_len(attr); + } + } + + return NL_SKIP; +} + + +static int check_feature(enum qca_wlan_vendor_features feature, + struct features_info *info) +{ + size_t idx = feature / 8; + + return (idx < info->flags_len) && + (info->flags[idx] & BIT(feature % 8)); +} + + +static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct features_info info; + int ret; + + if (!drv->get_features_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) { + nlmsg_free(msg); + return; + } + + os_memset(&info, 0, sizeof(info)); + ret = send_and_recv_msgs(drv, msg, features_info_handler, &info); + if (ret || !info.flags) + return; + + if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD; +} + + +int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) +{ + struct wiphy_info_data info; + if (wpa_driver_nl80211_get_info(drv, &info)) + return -1; + + if (info.error) + return -1; + + drv->has_capability = 1; + drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES; + drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE; + drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + /* + * As all cfg80211 drivers must support cases where the AP interface is + * removed without the knowledge of wpa_supplicant/hostapd, e.g., in + * case that the user space daemon has crashed, they must be able to + * cleanup all stations and key entries in the AP tear down flow. Thus, + * this flag can/should always be set for cfg80211 drivers. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT; + + if (!info.device_ap_sme) { + drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS; + + /* + * No AP SME is currently assumed to also indicate no AP MLME + * in the driver/firmware. + */ + drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME; + } + + drv->device_ap_sme = info.device_ap_sme; + drv->poll_command_supported = info.poll_command_supported; + drv->data_tx_status = info.data_tx_status; + if (info.set_qos_map_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING; + drv->have_low_prio_scan = info.have_low_prio_scan; + + /* + * If poll command and tx status are supported, mac80211 is new enough + * to have everything we need to not need monitor interfaces. + */ + drv->use_monitor = !info.poll_command_supported || !info.data_tx_status; + + if (drv->device_ap_sme && drv->use_monitor) { + /* + * Non-mac80211 drivers may not support monitor interface. + * Make sure we do not get stuck with incorrect capability here + * by explicitly testing this. + */ + if (!info.monitor_supported) { + wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor " + "with device_ap_sme since no monitor mode " + "support detected"); + drv->use_monitor = 0; + } + } + + /* + * If we aren't going to use monitor interfaces, but the + * driver doesn't support data TX status, we won't get TX + * status for EAPOL frames. + */ + if (!drv->use_monitor && !info.data_tx_status) + drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; + + qca_nl80211_check_dfs_capa(drv); + qca_nl80211_get_features(drv); + + return 0; +} + + +struct phy_info_arg { + u16 *num_modes; + struct hostapd_hw_modes *modes; + int last_mode, last_chan_idx; +}; + +static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, + struct nlattr *ampdu_factor, + struct nlattr *ampdu_density, + struct nlattr *mcs_set) +{ + if (capa) + mode->ht_capab = nla_get_u16(capa); + + if (ampdu_factor) + mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03; + + if (ampdu_density) + mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2; + + if (mcs_set && nla_len(mcs_set) >= 16) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->mcs_set, mcs, 16); + } +} + + +static void phy_info_vht_capa(struct hostapd_hw_modes *mode, + struct nlattr *capa, + struct nlattr *mcs_set) +{ + if (capa) + mode->vht_capab = nla_get_u32(capa); + + if (mcs_set && nla_len(mcs_set) >= 8) { + u8 *mcs; + mcs = nla_data(mcs_set); + os_memcpy(mode->vht_mcs_set, mcs, 8); + } +} + + +static void phy_info_freq(struct hostapd_hw_modes *mode, + struct hostapd_channel_data *chan, + struct nlattr *tb_freq[]) +{ + u8 channel; + chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); + chan->flag = 0; + chan->dfs_cac_ms = 0; + if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES) + chan->chan = channel; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) + chan->flag |= HOSTAPD_CHAN_DISABLED; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR]) + chan->flag |= HOSTAPD_CHAN_NO_IR; + if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) + chan->flag |= HOSTAPD_CHAN_RADAR; + if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY]) + chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY; + if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT]) + chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT; + + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) { + enum nl80211_dfs_state state = + nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]); + + switch (state) { + case NL80211_DFS_USABLE: + chan->flag |= HOSTAPD_CHAN_DFS_USABLE; + break; + case NL80211_DFS_AVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE; + break; + case NL80211_DFS_UNAVAILABLE: + chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE; + break; + } + } + + if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) { + chan->dfs_cac_ms = nla_get_u32( + tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]); + } +} + + +static int phy_info_freqs(struct phy_info_arg *phy_info, + struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, + [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, + [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 }, + }; + int new_channels = 0; + struct hostapd_channel_data *channel; + struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; + struct nlattr *nl_freq; + int rem_freq, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + new_channels++; + } + + channel = os_realloc_array(mode->channels, + mode->num_channels + new_channels, + sizeof(struct hostapd_channel_data)); + if (!channel) + return NL_SKIP; + + mode->channels = channel; + mode->num_channels += new_channels; + + idx = phy_info->last_chan_idx; + + nla_for_each_nested(nl_freq, tb, rem_freq) { + nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_freq), nla_len(nl_freq), freq_policy); + if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) + continue; + phy_info_freq(mode, &mode->channels[idx], tb_freq); + idx++; + } + phy_info->last_chan_idx = idx; + + return NL_OK; +} + + +static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb) +{ + static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { + [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, + [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = + { .type = NLA_FLAG }, + }; + struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; + struct nlattr *nl_rate; + int rem_rate, idx; + + if (tb == NULL) + return NL_OK; + + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->num_rates++; + } + + mode->rates = os_calloc(mode->num_rates, sizeof(int)); + if (!mode->rates) + return NL_SKIP; + + idx = 0; + + nla_for_each_nested(nl_rate, tb, rem_rate) { + nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, + nla_data(nl_rate), nla_len(nl_rate), + rate_policy); + if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) + continue; + mode->rates[idx] = nla_get_u32( + tb_rate[NL80211_BITRATE_ATTR_RATE]); + idx++; + } + + return NL_OK; +} + + +static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band) +{ + struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + struct hostapd_hw_modes *mode; + int ret; + + if (phy_info->last_mode != nl_band->nla_type) { + mode = os_realloc_array(phy_info->modes, + *phy_info->num_modes + 1, + sizeof(*mode)); + if (!mode) + return NL_SKIP; + phy_info->modes = mode; + + mode = &phy_info->modes[*(phy_info->num_modes)]; + os_memset(mode, 0, sizeof(*mode)); + mode->mode = NUM_HOSTAPD_MODES; + mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN | + HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN; + + /* + * Unsupported VHT MCS stream is defined as value 3, so the VHT + * MCS RX/TX map must be initialized with 0xffff to mark all 8 + * possible streams as unsupported. This will be overridden if + * driver advertises VHT support. + */ + mode->vht_mcs_set[0] = 0xff; + mode->vht_mcs_set[1] = 0xff; + mode->vht_mcs_set[4] = 0xff; + mode->vht_mcs_set[5] = 0xff; + + *(phy_info->num_modes) += 1; + phy_info->last_mode = nl_band->nla_type; + phy_info->last_chan_idx = 0; + } else + mode = &phy_info->modes[*(phy_info->num_modes) - 1]; + + nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), + nla_len(nl_band), NULL); + + phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR], + tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY], + tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); + phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA], + tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]); + ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]); + if (ret != NL_OK) + return ret; + ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]); + if (ret != NL_OK) + return ret; + + return NL_OK; +} + + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct phy_info_arg *phy_info = arg; + struct nlattr *nl_band; + int rem_band; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) + return NL_SKIP; + + nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) + { + int res = phy_info_band(phy_info, nl_band); + if (res != NL_OK) + return res; + } + + return NL_SKIP; +} + + +static struct hostapd_hw_modes * +wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, + u16 *num_modes) +{ + u16 m; + struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode; + int i, mode11g_idx = -1; + + /* heuristic to set up modes */ + for (m = 0; m < *num_modes; m++) { + if (!modes[m].num_channels) + continue; + if (modes[m].channels[0].freq < 4000) { + modes[m].mode = HOSTAPD_MODE_IEEE80211B; + for (i = 0; i < modes[m].num_rates; i++) { + if (modes[m].rates[i] > 200) { + modes[m].mode = HOSTAPD_MODE_IEEE80211G; + break; + } + } + } else if (modes[m].channels[0].freq > 50000) + modes[m].mode = HOSTAPD_MODE_IEEE80211AD; + else + modes[m].mode = HOSTAPD_MODE_IEEE80211A; + } + + /* If only 802.11g mode is included, use it to construct matching + * 802.11b mode data. */ + + for (m = 0; m < *num_modes; m++) { + if (modes[m].mode == HOSTAPD_MODE_IEEE80211B) + return modes; /* 802.11b already included */ + if (modes[m].mode == HOSTAPD_MODE_IEEE80211G) + mode11g_idx = m; + } + + if (mode11g_idx < 0) + return modes; /* 2.4 GHz band not supported at all */ + + nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes)); + if (nmodes == NULL) + return modes; /* Could not add 802.11b mode */ + + mode = &nmodes[*num_modes]; + os_memset(mode, 0, sizeof(*mode)); + (*num_modes)++; + modes = nmodes; + + mode->mode = HOSTAPD_MODE_IEEE80211B; + + mode11g = &modes[mode11g_idx]; + mode->num_channels = mode11g->num_channels; + mode->channels = os_malloc(mode11g->num_channels * + sizeof(struct hostapd_channel_data)); + if (mode->channels == NULL) { + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + os_memcpy(mode->channels, mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); + + mode->num_rates = 0; + mode->rates = os_malloc(4 * sizeof(int)); + if (mode->rates == NULL) { + os_free(mode->channels); + (*num_modes)--; + return modes; /* Could not add 802.11b mode */ + } + + for (i = 0; i < mode11g->num_rates; i++) { + if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 && + mode11g->rates[i] != 55 && mode11g->rates[i] != 110) + continue; + mode->rates[mode->num_rates] = mode11g->rates[i]; + mode->num_rates++; + if (mode->num_rates == 4) + break; + } + + if (mode->num_rates == 0) { + os_free(mode->channels); + os_free(mode->rates); + (*num_modes)--; + return modes; /* No 802.11b rates */ + } + + wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g " + "information"); + + return modes; +} + + +static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40; + } +} + + +static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (!(chan->flag & HOSTAPD_CHAN_HT40)) + continue; + if (chan->freq - 30 >= start && chan->freq - 10 <= end) + chan->flag |= HOSTAPD_CHAN_HT40MINUS; + if (chan->freq + 10 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_HT40PLUS; + } +} + + +static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + int c; + struct hostapd_hw_modes *mode = &results->modes[m]; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if ((u32) chan->freq - 10 >= start && + (u32) chan->freq + 10 <= end) + chan->max_tx_power = max_eirp; + } + } +} + + +static void nl80211_reg_rule_ht40(u32 start, u32 end, + struct phy_info_arg *results) +{ + u16 m; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode(&results->modes[m], start, end); + } +} + + +static void nl80211_reg_rule_sec(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 20) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + nl80211_set_ht40_mode_sec(&results->modes[m], start, end); + } +} + + +static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start, + int end) +{ + int c; + + for (c = 0; c < mode->num_channels; c++) { + struct hostapd_channel_data *chan = &mode->channels[c]; + if (chan->freq - 10 >= start && chan->freq + 70 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_10_70; + + if (chan->freq - 30 >= start && chan->freq + 50 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_30_50; + + if (chan->freq - 50 >= start && chan->freq + 30 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_50_30; + + if (chan->freq - 70 >= start && chan->freq + 10 <= end) + chan->flag |= HOSTAPD_CHAN_VHT_70_10; + } +} + + +static void nl80211_reg_rule_vht(struct nlattr *tb[], + struct phy_info_arg *results) +{ + u32 start, end, max_bw; + u16 m; + + if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_END] == NULL || + tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL) + return; + + start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + + if (max_bw < 80) + return; + + for (m = 0; m < *results->num_modes; m++) { + if (!(results->modes[m].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + continue; + /* TODO: use a real VHT support indication */ + if (!results->modes[m].vht_capab) + continue; + + nl80211_set_vht_mode(&results->modes[m], start, end); + } +} + + +static const char * dfs_domain_name(enum nl80211_dfs_regions region) +{ + switch (region) { + case NL80211_DFS_UNSET: + return "DFS-UNSET"; + case NL80211_DFS_FCC: + return "DFS-FCC"; + case NL80211_DFS_ETSI: + return "DFS-ETSI"; + case NL80211_DFS_JP: + return "DFS-JP"; + default: + return "DFS-invalid"; + } +} + + +static int nl80211_get_reg(struct nl_msg *msg, void *arg) +{ + struct phy_info_arg *results = arg; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *nl_rule; + struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1]; + int rem_rule; + static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { + [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, + [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, + [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, + }; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb_msg[NL80211_ATTR_REG_ALPHA2] || + !tb_msg[NL80211_ATTR_REG_RULES]) { + wpa_printf(MSG_DEBUG, "nl80211: No regulatory information " + "available"); + return NL_SKIP; + } + + if (tb_msg[NL80211_ATTR_DFS_REGION]) { + enum nl80211_dfs_regions dfs_domain; + dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), + dfs_domain_name(dfs_domain)); + } else { + wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s", + (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2])); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + u32 start, end, max_eirp = 0, max_bw = 0, flags = 0; + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL || + tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL) + continue; + start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000; + end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000; + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100; + if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) + max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000; + if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS]) + flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]); + + wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s", + start, end, max_bw, max_eirp, + flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "", + flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "", + flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "", + flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" : + "", + flags & NL80211_RRF_DFS ? " (DFS)" : "", + flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "", + flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "", + flags & NL80211_RRF_NO_IR ? " (no IR)" : ""); + if (max_bw >= 40) + nl80211_reg_rule_ht40(start, end, results); + if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) + nl80211_reg_rule_max_eirp(start, end, max_eirp, + results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_sec(tb_rule, results); + } + + nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule) + { + nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX, + nla_data(nl_rule), nla_len(nl_rule), reg_policy); + nl80211_reg_rule_vht(tb_rule, results); + } + + return NL_SKIP; +} + + +static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, + struct phy_info_arg *results) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); +} + + +struct hostapd_hw_modes * +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + u32 feat; + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int nl_flags = 0; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + .last_mode = -1, + }; + + *num_modes = 0; + *flags = 0; + + feat = get_nl80211_protocol_features(drv); + if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) + nl_flags = NLM_F_DUMP; + if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) || + nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) { + nlmsg_free(msg); + return NULL; + } + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) { + nl80211_set_regulatory_flags(drv, &result); + return wpa_driver_nl80211_postprocess_modes(result.modes, + num_modes); + } + + return NULL; +} diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c new file mode 100644 index 00000000..85769d80 --- /dev/null +++ b/src/drivers/driver_nl80211_event.c @@ -0,0 +1,1948 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Event processing + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <netlink/genl/genl.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/qca-vendor.h" +#include "common/qca-vendor-attr.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "driver_nl80211.h" + + +static const char * nl80211_command_to_string(enum nl80211_commands cmd) +{ +#define C2S(x) case x: return #x; + switch (cmd) { + C2S(NL80211_CMD_UNSPEC) + C2S(NL80211_CMD_GET_WIPHY) + C2S(NL80211_CMD_SET_WIPHY) + C2S(NL80211_CMD_NEW_WIPHY) + C2S(NL80211_CMD_DEL_WIPHY) + C2S(NL80211_CMD_GET_INTERFACE) + C2S(NL80211_CMD_SET_INTERFACE) + C2S(NL80211_CMD_NEW_INTERFACE) + C2S(NL80211_CMD_DEL_INTERFACE) + C2S(NL80211_CMD_GET_KEY) + C2S(NL80211_CMD_SET_KEY) + C2S(NL80211_CMD_NEW_KEY) + C2S(NL80211_CMD_DEL_KEY) + C2S(NL80211_CMD_GET_BEACON) + C2S(NL80211_CMD_SET_BEACON) + C2S(NL80211_CMD_START_AP) + C2S(NL80211_CMD_STOP_AP) + C2S(NL80211_CMD_GET_STATION) + C2S(NL80211_CMD_SET_STATION) + C2S(NL80211_CMD_NEW_STATION) + C2S(NL80211_CMD_DEL_STATION) + C2S(NL80211_CMD_GET_MPATH) + C2S(NL80211_CMD_SET_MPATH) + C2S(NL80211_CMD_NEW_MPATH) + C2S(NL80211_CMD_DEL_MPATH) + C2S(NL80211_CMD_SET_BSS) + C2S(NL80211_CMD_SET_REG) + C2S(NL80211_CMD_REQ_SET_REG) + C2S(NL80211_CMD_GET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MESH_CONFIG) + C2S(NL80211_CMD_SET_MGMT_EXTRA_IE) + C2S(NL80211_CMD_GET_REG) + C2S(NL80211_CMD_GET_SCAN) + C2S(NL80211_CMD_TRIGGER_SCAN) + C2S(NL80211_CMD_NEW_SCAN_RESULTS) + C2S(NL80211_CMD_SCAN_ABORTED) + C2S(NL80211_CMD_REG_CHANGE) + C2S(NL80211_CMD_AUTHENTICATE) + C2S(NL80211_CMD_ASSOCIATE) + C2S(NL80211_CMD_DEAUTHENTICATE) + C2S(NL80211_CMD_DISASSOCIATE) + C2S(NL80211_CMD_MICHAEL_MIC_FAILURE) + C2S(NL80211_CMD_REG_BEACON_HINT) + C2S(NL80211_CMD_JOIN_IBSS) + C2S(NL80211_CMD_LEAVE_IBSS) + C2S(NL80211_CMD_TESTMODE) + C2S(NL80211_CMD_CONNECT) + C2S(NL80211_CMD_ROAM) + C2S(NL80211_CMD_DISCONNECT) + C2S(NL80211_CMD_SET_WIPHY_NETNS) + C2S(NL80211_CMD_GET_SURVEY) + C2S(NL80211_CMD_NEW_SURVEY_RESULTS) + C2S(NL80211_CMD_SET_PMKSA) + C2S(NL80211_CMD_DEL_PMKSA) + C2S(NL80211_CMD_FLUSH_PMKSA) + C2S(NL80211_CMD_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL) + C2S(NL80211_CMD_SET_TX_BITRATE_MASK) + C2S(NL80211_CMD_REGISTER_FRAME) + C2S(NL80211_CMD_FRAME) + C2S(NL80211_CMD_FRAME_TX_STATUS) + C2S(NL80211_CMD_SET_POWER_SAVE) + C2S(NL80211_CMD_GET_POWER_SAVE) + C2S(NL80211_CMD_SET_CQM) + C2S(NL80211_CMD_NOTIFY_CQM) + C2S(NL80211_CMD_SET_CHANNEL) + C2S(NL80211_CMD_SET_WDS_PEER) + C2S(NL80211_CMD_FRAME_WAIT_CANCEL) + C2S(NL80211_CMD_JOIN_MESH) + C2S(NL80211_CMD_LEAVE_MESH) + C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE) + C2S(NL80211_CMD_UNPROT_DISASSOCIATE) + C2S(NL80211_CMD_NEW_PEER_CANDIDATE) + C2S(NL80211_CMD_GET_WOWLAN) + C2S(NL80211_CMD_SET_WOWLAN) + C2S(NL80211_CMD_START_SCHED_SCAN) + C2S(NL80211_CMD_STOP_SCHED_SCAN) + C2S(NL80211_CMD_SCHED_SCAN_RESULTS) + C2S(NL80211_CMD_SCHED_SCAN_STOPPED) + C2S(NL80211_CMD_SET_REKEY_OFFLOAD) + C2S(NL80211_CMD_PMKSA_CANDIDATE) + C2S(NL80211_CMD_TDLS_OPER) + C2S(NL80211_CMD_TDLS_MGMT) + C2S(NL80211_CMD_UNEXPECTED_FRAME) + C2S(NL80211_CMD_PROBE_CLIENT) + C2S(NL80211_CMD_REGISTER_BEACONS) + C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME) + C2S(NL80211_CMD_SET_NOACK_MAP) + C2S(NL80211_CMD_CH_SWITCH_NOTIFY) + C2S(NL80211_CMD_START_P2P_DEVICE) + C2S(NL80211_CMD_STOP_P2P_DEVICE) + C2S(NL80211_CMD_CONN_FAILED) + C2S(NL80211_CMD_SET_MCAST_RATE) + C2S(NL80211_CMD_SET_MAC_ACL) + C2S(NL80211_CMD_RADAR_DETECT) + C2S(NL80211_CMD_GET_PROTOCOL_FEATURES) + C2S(NL80211_CMD_UPDATE_FT_IES) + C2S(NL80211_CMD_FT_EVENT) + C2S(NL80211_CMD_CRIT_PROTOCOL_START) + C2S(NL80211_CMD_CRIT_PROTOCOL_STOP) + C2S(NL80211_CMD_GET_COALESCE) + C2S(NL80211_CMD_SET_COALESCE) + C2S(NL80211_CMD_CHANNEL_SWITCH) + C2S(NL80211_CMD_VENDOR) + C2S(NL80211_CMD_SET_QOS_MAP) + C2S(NL80211_CMD_ADD_TX_TS) + C2S(NL80211_CMD_DEL_TX_TS) + default: + return "NL80211_CMD_UNKNOWN"; + } +#undef C2S +} + + +static void mlme_event_auth(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore auth event when using driver SME"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Authenticate event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.auth)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } + + os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN); + os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); + event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); + event.auth.auth_transaction = + le_to_host16(mgmt->u.auth.auth_transaction); + event.auth.status_code = le_to_host16(mgmt->u.auth.status_code); + if (len > 24 + sizeof(mgmt->u.auth)) { + event.auth.ies = mgmt->u.auth.variable; + event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth); + } + + wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event); +} + + +static int nl80211_parse_wmm_params(struct nlattr *wmm_attr, + struct wmm_params *wmm_params) +{ + struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1]; + static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = { + [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, + }; + + if (!wmm_attr) { + wpa_printf(MSG_DEBUG, "nl80211: WMM data missing"); + return -1; + } + + if (nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr, + wme_policy)) { + wpa_printf(MSG_DEBUG, + "nl80211: Failed to parse nested attributes"); + return -1; + } + + if (!wmm_info[NL80211_STA_WME_UAPSD_QUEUES]) + return -1; + + wmm_params->uapsd_queues = + nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]); + wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO; + + return 0; +} + + +static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, + const u8 *frame, size_t len, struct nlattr *wmm) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 status; + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + drv->force_connect_cmd) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore assoc event when using driver SME"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Associate event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24 + sizeof(mgmt->u.assoc_resp)) { + wpa_printf(MSG_DEBUG, "nl80211: Too short association event " + "frame"); + return; + } + + status = le_to_host16(mgmt->u.assoc_resp.status_code); + if (status != WLAN_STATUS_SUCCESS) { + os_memset(&event, 0, sizeof(event)); + event.assoc_reject.bssid = mgmt->bssid; + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_reject.resp_ies = + (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_reject.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + event.assoc_reject.status_code = status; + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } + + drv->associated = 1; + os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN); + os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); + + os_memset(&event, 0, sizeof(event)); + if (len > 24 + sizeof(mgmt->u.assoc_resp)) { + event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; + event.assoc_info.resp_ies_len = + len - 24 - sizeof(mgmt->u.assoc_resp); + } + + event.assoc_info.freq = drv->assoc_freq; + + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *status, + struct nlattr *addr, struct nlattr *req_ie, + struct nlattr *resp_ie, + struct nlattr *authorized, + struct nlattr *key_replay_ctr, + struct nlattr *ptk_kck, + struct nlattr *ptk_kek) +{ + union wpa_event_data event; + u16 status_code; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two association events that would confuse + * the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) " + "when using userspace SME", cmd); + return; + } + + status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS; + + if (cmd == NL80211_CMD_CONNECT) { + wpa_printf(MSG_DEBUG, + "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)", + status_code, drv->ignore_next_local_disconnect); + } else if (cmd == NL80211_CMD_ROAM) { + wpa_printf(MSG_DEBUG, "nl80211: Roam event"); + } + + os_memset(&event, 0, sizeof(event)); + if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) { + if (addr) + event.assoc_reject.bssid = nla_data(addr); + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (!event.assoc_reject.bssid || + (os_memcmp(event.assoc_reject.bssid, + drv->auth_attempt_bssid, + ETH_ALEN) != 0)) { + /* + * Ignore the event that came without a BSSID or + * for the old connection since this is likely + * not relevant to the new Connect command. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore connection failure event triggered during reassociation"); + return; + } + } + if (resp_ie) { + event.assoc_reject.resp_ies = nla_data(resp_ie); + event.assoc_reject.resp_ies_len = nla_len(resp_ie); + } + event.assoc_reject.status_code = status_code; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); + return; + } + + drv->associated = 1; + if (addr) { + os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN); + os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN); + } + + if (req_ie) { + event.assoc_info.req_ies = nla_data(req_ie); + event.assoc_info.req_ies_len = nla_len(req_ie); + } + if (resp_ie) { + event.assoc_info.resp_ies = nla_data(resp_ie); + event.assoc_info.resp_ies_len = nla_len(resp_ie); + } + + event.assoc_info.freq = nl80211_get_assoc_freq(drv); + + if (authorized && nla_get_u8(authorized)) { + event.assoc_info.authorized = 1; + wpa_printf(MSG_DEBUG, "nl80211: connection authorized"); + } + if (key_replay_ctr) { + event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr); + event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr); + } + if (ptk_kck) { + event.assoc_info.ptk_kck = nla_data(ptk_kck); + event.assoc_info.ptk_kck_len = nla_len(ptk_kck); + } + if (ptk_kek) { + event.assoc_info.ptk_kek = nla_data(ptk_kek); + event.assoc_info.ptk_kek_len = nla_len(ptk_kek); + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); +} + + +static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv, + struct nlattr *reason, struct nlattr *addr, + struct nlattr *by_ap) +{ + union wpa_event_data data; + unsigned int locally_generated = by_ap == NULL; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + /* + * Avoid reporting two disassociation events that could + * confuse the core code. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event when using userspace SME"); + return; + } + + if (drv->ignore_next_local_disconnect) { + drv->ignore_next_local_disconnect = 0; + if (locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect " + "event triggered during reassociation"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local " + "disconnect but got another disconnect " + "event first"); + } + + wpa_printf(MSG_DEBUG, "nl80211: Disconnect event"); + nl80211_mark_disconnected(drv); + os_memset(&data, 0, sizeof(data)); + if (reason) + data.deauth_info.reason_code = nla_get_u16(reason); + data.deauth_info.locally_generated = by_ap == NULL; + wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data); +} + + +static int calculate_chan_offset(int width, int freq, int cf1, int cf2) +{ + int freq1 = 0; + + switch (convert2width(width)) { + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + return 0; + case CHAN_WIDTH_40: + freq1 = cf1 - 10; + break; + case CHAN_WIDTH_80: + freq1 = cf1 - 30; + break; + case CHAN_WIDTH_160: + freq1 = cf1 - 70; + break; + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_80P80: + /* FIXME: implement this */ + return 0; + } + + return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1; +} + + +static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, + struct nlattr *ifindex, struct nlattr *freq, + struct nlattr *type, struct nlattr *bw, + struct nlattr *cf1, struct nlattr *cf2) +{ + struct i802_bss *bss; + union wpa_event_data data; + int ht_enabled = 1; + int chan_offset = 0; + int ifidx; + + wpa_printf(MSG_DEBUG, "nl80211: Channel switch event"); + + if (!freq) + return; + + ifidx = nla_get_u32(ifindex); + bss = get_bss_ifindex(drv, ifidx); + if (bss == NULL) { + wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring", + ifidx); + return; + } + + if (type) { + enum nl80211_channel_type ch_type = nla_get_u32(type); + + wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type); + switch (ch_type) { + case NL80211_CHAN_NO_HT: + ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + chan_offset = -1; + break; + } + } else if (bw && cf1) { + /* This can happen for example with VHT80 ch switch */ + chan_offset = calculate_chan_offset(nla_get_u32(bw), + nla_get_u32(freq), + nla_get_u32(cf1), + cf2 ? nla_get_u32(cf2) : 0); + } else { + wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail"); + } + + os_memset(&data, 0, sizeof(data)); + data.ch_switch.freq = nla_get_u32(freq); + data.ch_switch.ht_enabled = ht_enabled; + data.ch_switch.ch_offset = chan_offset; + if (bw) + data.ch_switch.ch_width = convert2width(nla_get_u32(bw)); + if (cf1) + data.ch_switch.cf1 = nla_get_u32(cf1); + if (cf2) + data.ch_switch.cf2 = nla_get_u32(cf2); + + bss->freq = data.ch_switch.freq; + + wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); +} + + +static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv, + enum nl80211_commands cmd, struct nlattr *addr) +{ + union wpa_event_data event; + enum wpa_event_type ev; + + if (nla_len(addr) != ETH_ALEN) + return; + + wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR, + cmd, MAC2STR((u8 *) nla_data(addr))); + + if (cmd == NL80211_CMD_AUTHENTICATE) + ev = EVENT_AUTH_TIMED_OUT; + else if (cmd == NL80211_CMD_ASSOCIATE) + ev = EVENT_ASSOC_TIMED_OUT; + else + return; + + os_memset(&event, 0, sizeof(event)); + os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN); + wpa_supplicant_event(drv->ctx, ev, &event); +} + + +static void mlme_event_mgmt(struct i802_bss *bss, + struct nlattr *freq, struct nlattr *sig, + const u8 *frame, size_t len) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 fc, stype; + int ssi_signal = 0; + int rx_freq = 0; + + wpa_printf(MSG_MSGDUMP, "nl80211: Frame event"); + mgmt = (const struct ieee80211_mgmt *) frame; + if (len < 24) { + wpa_printf(MSG_DEBUG, "nl80211: Too short management frame"); + return; + } + + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + if (sig) + ssi_signal = (s32) nla_get_u32(sig); + + os_memset(&event, 0, sizeof(event)); + if (freq) { + event.rx_mgmt.freq = nla_get_u32(freq); + rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq; + } + wpa_printf(MSG_DEBUG, + "nl80211: RX frame sa=" MACSTR + " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u", + MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc, + le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc), + (unsigned int) len); + event.rx_mgmt.frame = frame; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.drv_priv = bss; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); +} + + +static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, + struct nlattr *cookie, const u8 *frame, + size_t len, struct nlattr *ack) +{ + union wpa_event_data event; + const struct ieee80211_hdr *hdr; + u16 fc; + + wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event"); + if (!is_ap_interface(drv->nlmode)) { + u64 cookie_val; + + if (!cookie) + return; + + cookie_val = nla_get_u64(cookie); + wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" + " cookie=0%llx%s (ack=%d)", + (long long unsigned int) cookie_val, + cookie_val == drv->send_action_cookie ? + " (match)" : " (unknown)", ack != NULL); + if (cookie_val != drv->send_action_cookie) + return; + } + + hdr = (const struct ieee80211_hdr *) frame; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = frame; + event.tx_status.data_len = len; + event.tx_status.ack = ack != NULL; + wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); +} + + +static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + const u8 *bssid = NULL; + u16 reason_code = 0; + + if (type == EVENT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Disassociate event"); + + mgmt = (const struct ieee80211_mgmt *) frame; + if (len >= 24) { + bssid = mgmt->bssid; + + if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) && + !drv->associated && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) { + /* + * Avoid issues with some roaming cases where + * disconnection event for the old AP may show up after + * we have started connection with the new AP. + */ + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); + return; + } + + if (drv->associated != 0 && + os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) { + /* + * We have presumably received this deauth as a + * response to a clear_state_mismatch() outgoing + * deauth. Don't let it take us offline! + */ + wpa_printf(MSG_DEBUG, "nl80211: Deauth received " + "from Unknown BSSID " MACSTR " -- ignoring", + MAC2STR(bssid)); + return; + } + } + + nl80211_mark_disconnected(drv); + os_memset(&event, 0, sizeof(event)); + + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_DISASSOC) { + event.disassoc_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + event.disassoc_info.addr = bssid; + event.disassoc_info.reason_code = reason_code; + if (frame + len > mgmt->u.disassoc.variable) { + event.disassoc_info.ie = mgmt->u.disassoc.variable; + event.disassoc_info.ie_len = frame + len - + mgmt->u.disassoc.variable; + } + } else { + if (drv->ignore_deauth_event) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth"); + drv->ignore_deauth_event = 0; + return; + } + event.deauth_info.locally_generated = + !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN); + if (drv->ignore_next_local_deauth) { + drv->ignore_next_local_deauth = 0; + if (event.deauth_info.locally_generated) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request"); + return; + } + wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first"); + } + event.deauth_info.addr = bssid; + event.deauth_info.reason_code = reason_code; + if (frame + len > mgmt->u.deauth.variable) { + event.deauth_info.ie = mgmt->u.deauth.variable; + event.deauth_info.ie_len = frame + len - + mgmt->u.deauth.variable; + } + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv, + enum wpa_event_type type, + const u8 *frame, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + union wpa_event_data event; + u16 reason_code = 0; + + if (type == EVENT_UNPROT_DEAUTH) + wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event"); + else + wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event"); + + if (len < 24) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + + os_memset(&event, 0, sizeof(event)); + /* Note: Same offset for Reason Code in both frame subtypes */ + if (len >= 24 + sizeof(mgmt->u.deauth)) + reason_code = le_to_host16(mgmt->u.deauth.reason_code); + + if (type == EVENT_UNPROT_DISASSOC) { + event.unprot_disassoc.sa = mgmt->sa; + event.unprot_disassoc.da = mgmt->da; + event.unprot_disassoc.reason_code = reason_code; + } else { + event.unprot_deauth.sa = mgmt->sa; + event.unprot_deauth.da = mgmt->da; + event.unprot_deauth.reason_code = reason_code; + } + + wpa_supplicant_event(drv->ctx, type, &event); +} + + +static void mlme_event(struct i802_bss *bss, + enum nl80211_commands cmd, struct nlattr *frame, + struct nlattr *addr, struct nlattr *timed_out, + struct nlattr *freq, struct nlattr *ack, + struct nlattr *cookie, struct nlattr *sig, + struct nlattr *wmm) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + const u8 *data; + size_t len; + + if (timed_out && addr) { + mlme_timeout_event(drv, cmd, addr); + return; + } + + if (frame == NULL) { + wpa_printf(MSG_DEBUG, + "nl80211: MLME event %d (%s) without frame data", + cmd, nl80211_command_to_string(cmd)); + return; + } + + data = nla_data(frame); + len = nla_len(frame); + if (len < 4 + 2 * ETH_ALEN) { + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" + MACSTR ") - too short", + cmd, nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr)); + return; + } + wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR + ") A1=" MACSTR " A2=" MACSTR, cmd, + nl80211_command_to_string(cmd), bss->ifname, + MAC2STR(bss->addr), MAC2STR(data + 4), + MAC2STR(data + 4 + ETH_ALEN)); + if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && + os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " + "for foreign address", bss->ifname); + return; + } + wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame", + nla_data(frame), nla_len(frame)); + + switch (cmd) { + case NL80211_CMD_AUTHENTICATE: + mlme_event_auth(drv, nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_ASSOCIATE: + mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm); + break; + case NL80211_CMD_DEAUTHENTICATE: + mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_DISASSOCIATE: + mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_FRAME: + mlme_event_mgmt(bss, freq, sig, nla_data(frame), + nla_len(frame)); + break; + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame), + nla_len(frame), ack); + break; + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH, + nla_data(frame), nla_len(frame)); + break; + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC, + nla_data(frame), nla_len(frame)); + break; + default: + break; + } +} + + +static void mlme_event_michael_mic_failure(struct i802_bss *bss, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure"); + os_memset(&data, 0, sizeof(data)); + if (tb[NL80211_ATTR_MAC]) { + wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address", + nla_data(tb[NL80211_ATTR_MAC]), + nla_len(tb[NL80211_ATTR_MAC])); + data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]); + } + if (tb[NL80211_ATTR_KEY_SEQ]) { + wpa_hexdump(MSG_DEBUG, "nl80211: TSC", + nla_data(tb[NL80211_ATTR_KEY_SEQ]), + nla_len(tb[NL80211_ATTR_KEY_SEQ])); + } + if (tb[NL80211_ATTR_KEY_TYPE]) { + enum nl80211_key_type key_type = + nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]); + wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type); + if (key_type == NL80211_KEYTYPE_PAIRWISE) + data.michael_mic_failure.unicast = 1; + } else + data.michael_mic_failure.unicast = 1; + + if (tb[NL80211_ATTR_KEY_IDX]) { + u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]); + wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id); + } + + wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + unsigned int freq; + + if (tb[NL80211_ATTR_MAC] == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined " + "event"); + return; + } + os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + drv->associated = 1; + wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined", + MAC2STR(drv->bssid)); + + freq = nl80211_get_assoc_freq(drv); + if (freq) { + wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz", + freq); + drv->first_bss->freq = freq; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv, + int cancel_event, struct nlattr *tb[]) +{ + unsigned int freq, chan_type, duration; + union wpa_event_data data; + u64 cookie; + + if (tb[NL80211_ATTR_WIPHY_FREQ]) + freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + else + freq = 0; + + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) + chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + else + chan_type = 0; + + if (tb[NL80211_ATTR_DURATION]) + duration = nla_get_u32(tb[NL80211_ATTR_DURATION]); + else + duration = 0; + + if (tb[NL80211_ATTR_COOKIE]) + cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]); + else + cookie = 0; + + wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d " + "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))", + cancel_event, freq, chan_type, duration, + (long long unsigned int) cookie, + cookie == drv->remain_on_chan_cookie ? "match" : "unknown"); + + if (cookie != drv->remain_on_chan_cookie) + return; /* not for us */ + + if (cancel_event) + drv->pending_remain_on_chan = 0; + + os_memset(&data, 0, sizeof(data)); + data.remain_on_channel.freq = freq; + data.remain_on_channel.duration = duration; + wpa_supplicant_event(drv->ctx, cancel_event ? + EVENT_CANCEL_REMAIN_ON_CHANNEL : + EVENT_REMAIN_ON_CHANNEL, &data); +} + + +static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + + os_memset(&data, 0, sizeof(data)); + + if (tb[NL80211_ATTR_IE]) { + data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]); + data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + + if (tb[NL80211_ATTR_IE_RIC]) { + data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]); + data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]); + } + + if (tb[NL80211_ATTR_MAC]) + os_memcpy(data.ft_ies.target_ap, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR, + MAC2STR(data.ft_ies.target_ap)); + + wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data); +} + + +static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, + struct nlattr *tb[]) +{ + union wpa_event_data event; + struct nlattr *nl; + int rem; + struct scan_info *info; +#define MAX_REPORT_FREQS 50 + int freqs[MAX_REPORT_FREQS]; + int num_freqs = 0; + + if (drv->scan_for_auth) { + drv->scan_for_auth = 0; + wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing " + "cfg80211 BSS entry"); + wpa_driver_nl80211_authenticate_retry(drv); + return; + } + + os_memset(&event, 0, sizeof(event)); + info = &event.scan_info; + info->aborted = aborted; + + if (tb[NL80211_ATTR_SCAN_SSIDS]) { + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) { + struct wpa_driver_scan_ssid *s = + &info->ssids[info->num_ssids]; + s->ssid = nla_data(nl); + s->ssid_len = nla_len(nl); + wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'", + wpa_ssid_txt(s->ssid, s->ssid_len)); + info->num_ssids++; + if (info->num_ssids == WPAS_MAX_SCAN_SSIDS) + break; + } + } + if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) { + char msg[200], *pos, *end; + int res; + + pos = msg; + end = pos + sizeof(msg); + *pos = '\0'; + + nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem) + { + freqs[num_freqs] = nla_get_u32(nl); + res = os_snprintf(pos, end - pos, " %d", + freqs[num_freqs]); + if (!os_snprintf_error(end - pos, res)) + pos += res; + num_freqs++; + if (num_freqs == MAX_REPORT_FREQS - 1) + break; + } + info->freqs = freqs; + info->num_freqs = num_freqs; + wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", + msg); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); +} + + +static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + }; + struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; + enum nl80211_cqm_rssi_threshold_event event; + union wpa_event_data ed; + struct wpa_signal_info sig; + int res; + + if (tb[NL80211_ATTR_CQM] == NULL || + nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], + cqm_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event"); + return; + } + + os_memset(&ed, 0, sizeof(ed)); + + if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) { + if (!tb[NL80211_ATTR_MAC]) + return; + os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), + ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + return; + event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); + + if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI high"); + ed.signal_change.above_threshold = 1; + } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " + "event: RSSI low"); + ed.signal_change.above_threshold = 0; + } else + return; + + res = nl80211_get_link_signal(drv, &sig); + if (res == 0) { + ed.signal_change.current_signal = sig.current_signal; + ed.signal_change.current_txrate = sig.current_txrate; + wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d", + sig.current_signal, sig.current_txrate); + } + + res = nl80211_get_link_noise(drv, &sig); + if (res == 0) { + ed.signal_change.current_noise = sig.current_noise; + wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm", + sig.current_noise); + } + + wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed); +} + + +static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + const u8 *addr; + union wpa_event_data data; + + if (drv->nlmode != NL80211_IFTYPE_MESH_POINT) + return; + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE]) + return; + + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR, + MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + data.mesh_peer.peer = addr; + data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]); + data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]); + wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data); +} + + +static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv, + struct i802_bss *bss, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + u8 *ies = NULL; + size_t ies_len = 0; + if (tb[NL80211_ATTR_IE]) { + ies = nla_data(tb[NL80211_ATTR_IE]); + ies_len = nla_len(tb[NL80211_ATTR_IE]); + } + wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len); + drv_event_assoc(bss->ctx, addr, ies, ies_len, 0); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN); + wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data); +} + + +static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u8 *addr; + union wpa_event_data data; + + if (tb[NL80211_ATTR_MAC] == NULL) + return; + addr = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR, + MAC2STR(addr)); + + if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) { + drv_event_disassoc(drv->ctx, addr); + return; + } + + if (drv->nlmode != NL80211_IFTYPE_ADHOC) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN); + wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data); +} + + +static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA]; + static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = { + [NL80211_REKEY_DATA_KEK] = { + .minlen = NL80211_KEK_LEN, + .maxlen = NL80211_KEK_LEN, + }, + [NL80211_REKEY_DATA_KCK] = { + .minlen = NL80211_KCK_LEN, + .maxlen = NL80211_KCK_LEN, + }, + [NL80211_REKEY_DATA_REPLAY_CTR] = { + .minlen = NL80211_REPLAY_CTR_LEN, + .maxlen = NL80211_REPLAY_CTR_LEN, + }, + }; + union wpa_event_data data; + + if (!tb[NL80211_ATTR_MAC]) + return; + if (!tb[NL80211_ATTR_REKEY_DATA]) + return; + if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA, + tb[NL80211_ATTR_REKEY_DATA], rekey_policy)) + return; + if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]) + return; + + os_memset(&data, 0, sizeof(data)); + data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]); + wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR, + MAC2STR(data.driver_gtk_rekey.bssid)); + data.driver_gtk_rekey.replay_ctr = + nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]); + wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter", + data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN); + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data); +} + + +static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE]; + static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = { + [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 }, + [NL80211_PMKSA_CANDIDATE_BSSID] = { + .minlen = ETH_ALEN, + .maxlen = ETH_ALEN, + }, + [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG }, + }; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event"); + + if (!tb[NL80211_ATTR_PMKSA_CANDIDATE]) + return; + if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE, + tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy)) + return; + if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] || + !cand[NL80211_PMKSA_CANDIDATE_BSSID]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, + nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN); + data.pmkid_candidate.index = + nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]); + data.pmkid_candidate.preauth = + cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Probe client event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.client_poll.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data); +} + + +static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) { + case NL80211_TDLS_SETUP: + wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_SETUP; + break; + case NL80211_TDLS_TEARDOWN: + wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer " + MACSTR, MAC2STR(data.tdls.peer)); + data.tdls.oper = TDLS_REQUEST_TEARDOWN; + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione " + "event"); + return; + } + if (tb[NL80211_ATTR_REASON_CODE]) { + data.tdls.reason_code = + nla_get_u16(tb[NL80211_ATTR_REASON_CODE]); + } + + wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data); +} + + +static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL); +} + + +static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + u32 reason; + + wpa_printf(MSG_DEBUG, "nl80211: Connect failed event"); + + if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON]) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.connect_failed_reason.addr, + nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + + reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]); + switch (reason) { + case NL80211_CONN_FAIL_MAX_CLIENTS: + wpa_printf(MSG_DEBUG, "nl80211: Max client reached"); + data.connect_failed_reason.code = MAX_CLIENT_REACHED; + break; + case NL80211_CONN_FAIL_BLOCKED_CLIENT: + wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR + " tried to connect", + MAC2STR(data.connect_failed_reason.addr)); + data.connect_failed_reason.code = BLOCKED_CLIENT; + break; + default: + wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason " + "%u", reason); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data); +} + + +static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data data; + enum nl80211_radar_event event_type; + + if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT]) + return; + + os_memset(&data, 0, sizeof(data)); + data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]); + event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]); + + /* Check HT params */ + if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + data.dfs_event.ht_enabled = 1; + data.dfs_event.chan_offset = 0; + + switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) { + case NL80211_CHAN_NO_HT: + data.dfs_event.ht_enabled = 0; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + data.dfs_event.chan_offset = 1; + break; + case NL80211_CHAN_HT40MINUS: + data.dfs_event.chan_offset = -1; + break; + } + } + + /* Get VHT params */ + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) + data.dfs_event.chan_width = + convert2width(nla_get_u32( + tb[NL80211_ATTR_CHANNEL_WIDTH])); + if (tb[NL80211_ATTR_CENTER_FREQ1]) + data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]); + if (tb[NL80211_ATTR_CENTER_FREQ2]) + data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]); + + wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz", + data.dfs_event.freq, data.dfs_event.ht_enabled, + data.dfs_event.chan_offset, data.dfs_event.chan_width, + data.dfs_event.cf1, data.dfs_event.cf2); + + switch (event_type) { + case NL80211_RADAR_DETECTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data); + break; + case NL80211_RADAR_CAC_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data); + break; + case NL80211_RADAR_CAC_ABORTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data); + break; + case NL80211_RADAR_NOP_FINISHED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " + "received", event_type); + break; + } +} + + +static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb, + int wds) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data event; + + if (!tb[NL80211_ATTR_MAC]) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = bss->addr; + event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]); + event.rx_from_unknown.wds = wds; + + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + u32 i, count; + union wpa_event_data event; + struct wpa_freq_range *range = NULL; + const struct qca_avoid_freq_list *freq_range; + + freq_range = (const struct qca_avoid_freq_list *) data; + if (len < sizeof(freq_range->count)) + return; + + count = freq_range->count; + if (len < sizeof(freq_range->count) + + count * sizeof(struct qca_avoid_freq_range)) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)", + (unsigned int) len); + return; + } + + if (count > 0) { + range = os_calloc(count, sizeof(struct wpa_freq_range)); + if (range == NULL) + return; + } + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < count; i++) { + unsigned int idx = event.freq_range.num; + range[idx].min = freq_range->range[i].start_freq; + range[idx].max = freq_range->range[i].end_freq; + wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u", + range[idx].min, range[idx].max); + if (range[idx].min > range[idx].max) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range"); + continue; + } + event.freq_range.num++; + } + event.freq_range.range = range; + + wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event); + + os_free(range); +} + + +static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1]; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, + "nl80211: ACS channel selection vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX, + (struct nlattr *) data, len, NULL)) + return; + + if (!tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] || + !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]) + return; + + os_memset(&event, 0, sizeof(event)); + event.acs_selected_channels.pri_channel = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]); + event.acs_selected_channels.sec_channel = + nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]); + + wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event); +} + + +static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, + const u8 *data, size_t len) +{ + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1]; + u8 *bssid; + + wpa_printf(MSG_DEBUG, + "nl80211: Key management roam+auth vendor event received"); + + if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX, + (struct nlattr *) data, len, NULL)) + return; + if (!tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] || + nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] || + !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED]) + return; + + bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]); + wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid)); + + mlme_event_connect(drv, NL80211_CMD_ROAM, NULL, + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]); +} + + +static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv, + u32 subcmd, u8 *data, size_t len) +{ + switch (subcmd) { + case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: + qca_nl80211_avoid_freq(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: + qca_nl80211_key_mgmt_auth(drv, data, len); + break; + case QCA_NL80211_VENDOR_SUBCMD_DO_ACS: + qca_nl80211_acs_select_ch(drv, data, len); + break; + default: + wpa_printf(MSG_DEBUG, + "nl80211: Ignore unsupported QCA vendor event %u", + subcmd); + break; + } +} + + +static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + u32 vendor_id, subcmd, wiphy = 0; + int wiphy_idx; + u8 *data = NULL; + size_t len = 0; + + if (!tb[NL80211_ATTR_VENDOR_ID] || + !tb[NL80211_ATTR_VENDOR_SUBCMD]) + return; + + vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]); + + if (tb[NL80211_ATTR_WIPHY]) + wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + + wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u", + wiphy, vendor_id, subcmd); + + if (tb[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]); + wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len); + } + + wiphy_idx = nl80211_get_wiphy_index(drv->first_bss); + if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) { + wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)", + wiphy, wiphy_idx); + return; + } + + switch (vendor_id) { + case OUI_QCA: + nl80211_vendor_event_qca(drv, subcmd, data, len); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event"); + break; + } +} + + +static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr *tb[]) +{ + union wpa_event_data data; + enum nl80211_reg_initiator init; + + wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change"); + + if (tb[NL80211_ATTR_REG_INITIATOR] == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]); + wpa_printf(MSG_DEBUG, " * initiator=%d", init); + switch (init) { + case NL80211_REGDOM_SET_BY_CORE: + data.channel_list_changed.initiator = REGDOM_SET_BY_CORE; + break; + case NL80211_REGDOM_SET_BY_USER: + data.channel_list_changed.initiator = REGDOM_SET_BY_USER; + break; + case NL80211_REGDOM_SET_BY_DRIVER: + data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER; + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE; + break; + } + + if (tb[NL80211_ATTR_REG_TYPE]) { + enum nl80211_reg_type type; + type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]); + wpa_printf(MSG_DEBUG, " * type=%d", type); + switch (type) { + case NL80211_REGDOM_TYPE_COUNTRY: + data.channel_list_changed.type = REGDOM_TYPE_COUNTRY; + break; + case NL80211_REGDOM_TYPE_WORLD: + data.channel_list_changed.type = REGDOM_TYPE_WORLD; + break; + case NL80211_REGDOM_TYPE_CUSTOM_WORLD: + data.channel_list_changed.type = + REGDOM_TYPE_CUSTOM_WORLD; + break; + case NL80211_REGDOM_TYPE_INTERSECTION: + data.channel_list_changed.type = + REGDOM_TYPE_INTERSECTION; + break; + } + } + + if (tb[NL80211_ATTR_REG_ALPHA2]) { + os_strlcpy(data.channel_list_changed.alpha2, + nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]), + sizeof(data.channel_list_changed.alpha2)); + wpa_printf(MSG_DEBUG, " * alpha2=%s", + data.channel_list_changed.alpha2); + } + + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data); +} + + +static void do_process_drv_event(struct i802_bss *bss, int cmd, + struct nlattr **tb) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s", + cmd, nl80211_command_to_string(cmd), bss->ifname); + + if (cmd == NL80211_CMD_ROAM && + (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) { + /* + * Device will use roam+auth vendor event to indicate + * roaming, so ignore the regular roam event. + */ + wpa_printf(MSG_DEBUG, + "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth", + cmd); + return; + } + + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED && + (cmd == NL80211_CMD_NEW_SCAN_RESULTS || + cmd == NL80211_CMD_SCAN_ABORTED)) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + + switch (cmd) { + case NL80211_CMD_TRIGGER_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger"); + drv->scan_state = SCAN_STARTED; + if (drv->scan_for_auth) { + /* + * Cannot indicate EVENT_SCAN_STARTED here since we skip + * EVENT_SCAN_RESULTS in scan_for_auth case and the + * upper layer implementation could get confused about + * scanning state. + */ + wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth"); + break; + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL); + break; + case NL80211_CMD_START_SCHED_SCAN: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started"); + drv->scan_state = SCHED_SCAN_STARTED; + break; + case NL80211_CMD_SCHED_SCAN_STOPPED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped"); + drv->scan_state = SCHED_SCAN_STOPPED; + wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL); + break; + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New scan results available"); + drv->scan_state = SCAN_COMPLETED; + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + send_scan_event(drv, 0, tb); + break; + case NL80211_CMD_SCHED_SCAN_RESULTS: + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: New sched scan results available"); + drv->scan_state = SCHED_SCAN_RESULTS; + send_scan_event(drv, 0, tb); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); + drv->scan_state = SCAN_ABORTED; + /* + * Need to indicate that scan results are available in order + * not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + send_scan_event(drv, 1, tb); + break; + case NL80211_CMD_AUTHENTICATE: + case NL80211_CMD_ASSOCIATE: + case NL80211_CMD_DEAUTHENTICATE: + case NL80211_CMD_DISASSOCIATE: + case NL80211_CMD_FRAME_TX_STATUS: + case NL80211_CMD_UNPROT_DEAUTHENTICATE: + case NL80211_CMD_UNPROT_DISASSOCIATE: + mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM], + tb[NL80211_ATTR_STA_WME]); + break; + case NL80211_CMD_CONNECT: + case NL80211_CMD_ROAM: + mlme_event_connect(drv, cmd, + tb[NL80211_ATTR_STATUS_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_REQ_IE], + tb[NL80211_ATTR_RESP_IE], + NULL, NULL, NULL, NULL); + break; + case NL80211_CMD_CH_SWITCH_NOTIFY: + mlme_event_ch_switch(drv, + tb[NL80211_ATTR_IFINDEX], + tb[NL80211_ATTR_WIPHY_FREQ], + tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE], + tb[NL80211_ATTR_CHANNEL_WIDTH], + tb[NL80211_ATTR_CENTER_FREQ1], + tb[NL80211_ATTR_CENTER_FREQ2]); + break; + case NL80211_CMD_DISCONNECT: + mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE], + tb[NL80211_ATTR_MAC], + tb[NL80211_ATTR_DISCONNECTED_BY_AP]); + break; + case NL80211_CMD_MICHAEL_MIC_FAILURE: + mlme_event_michael_mic_failure(bss, tb); + break; + case NL80211_CMD_JOIN_IBSS: + mlme_event_join_ibss(drv, tb); + break; + case NL80211_CMD_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 0, tb); + break; + case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: + mlme_event_remain_on_channel(drv, 1, tb); + break; + case NL80211_CMD_NOTIFY_CQM: + nl80211_cqm_event(drv, tb); + break; + case NL80211_CMD_REG_CHANGE: + nl80211_reg_change_event(drv, tb); + break; + case NL80211_CMD_REG_BEACON_HINT: + wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint"); + os_memset(&data, 0, sizeof(data)); + data.channel_list_changed.initiator = REGDOM_BEACON_HINT; + wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, + &data); + break; + case NL80211_CMD_NEW_STATION: + nl80211_new_station_event(drv, bss, tb); + break; + case NL80211_CMD_DEL_STATION: + nl80211_del_station_event(drv, tb); + break; + case NL80211_CMD_SET_REKEY_OFFLOAD: + nl80211_rekey_offload_event(drv, tb); + break; + case NL80211_CMD_PMKSA_CANDIDATE: + nl80211_pmksa_candidate_event(drv, tb); + break; + case NL80211_CMD_PROBE_CLIENT: + nl80211_client_probe_event(drv, tb); + break; + case NL80211_CMD_TDLS_OPER: + nl80211_tdls_oper_event(drv, tb); + break; + case NL80211_CMD_CONN_FAILED: + nl80211_connect_failed_event(drv, tb); + break; + case NL80211_CMD_FT_EVENT: + mlme_event_ft_event(drv, tb); + break; + case NL80211_CMD_RADAR_DETECT: + nl80211_radar_event(drv, tb); + break; + case NL80211_CMD_STOP_AP: + nl80211_stop_ap(drv, tb); + break; + case NL80211_CMD_VENDOR: + nl80211_vendor_event(drv, tb); + break; + case NL80211_CMD_NEW_PEER_CANDIDATE: + nl80211_new_peer_candidate(drv, tb); + break; + default: + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", cmd); + break; + } +} + + +int process_global_event(struct nl_msg *msg, void *arg) +{ + struct nl80211_global *global = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct wpa_driver_nl80211_data *drv, *tmp; + int ifidx = -1; + struct i802_bss *bss; + u64 wdev_id = 0; + int wdev_id_set = 0; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) + ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + else if (tb[NL80211_ATTR_WDEV]) { + wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); + wdev_id_set = 1; + } + + dl_list_for_each_safe(drv, tmp, &global->interfaces, + struct wpa_driver_nl80211_data, list) { + for (bss = drv->first_bss; bss; bss = bss->next) { + if ((ifidx == -1 && !wdev_id_set) || + ifidx == bss->ifindex || + (wdev_id_set && bss->wdev_id_set && + wdev_id == bss->wdev_id)) { + do_process_drv_event(bss, gnlh->cmd, tb); + return NL_SKIP; + } + } + wpa_printf(MSG_DEBUG, + "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)", + gnlh->cmd, ifidx, (long long unsigned int) wdev_id); + } + + return NL_SKIP; +} + + +int process_bss_event(struct nl_msg *msg, void *arg) +{ + struct i802_bss *bss = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s", + gnlh->cmd, nl80211_command_to_string(gnlh->cmd), + bss->ifname); + + switch (gnlh->cmd) { + case NL80211_CMD_FRAME: + case NL80211_CMD_FRAME_TX_STATUS: + mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME], + tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT], + tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK], + tb[NL80211_ATTR_COOKIE], + tb[NL80211_ATTR_RX_SIGNAL_DBM], + tb[NL80211_ATTR_STA_WME]); + break; + case NL80211_CMD_UNEXPECTED_FRAME: + nl80211_spurious_frame(bss, tb, 0); + break; + case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: + nl80211_spurious_frame(bss, tb, 1); + break; + default: + wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " + "(cmd=%d)", gnlh->cmd); + break; + } + + return NL_SKIP; +} diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c new file mode 100644 index 00000000..45385da9 --- /dev/null +++ b/src/drivers/driver_nl80211_monitor.c @@ -0,0 +1,491 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <netpacket/packet.h> +#include <linux/filter.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "linux_ioctl.h" +#include "radiotap_iter.h" +#include "driver_nl80211.h" + + +static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = hdr->addr1; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.ack = ok; + wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event); +} + + +static void from_unknown_sta(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len) +{ + struct ieee80211_hdr *hdr = (void *)buf; + u16 fc; + union wpa_event_data event; + + if (len < sizeof(*hdr)) + return; + + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len); + event.rx_from_unknown.addr = hdr->addr2; + event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) == + (WLAN_FC_FROMDS | WLAN_FC_TODS); + wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event); +} + + +static void handle_frame(struct wpa_driver_nl80211_data *drv, + u8 *buf, size_t len, int datarate, int ssi_signal) +{ + struct ieee80211_hdr *hdr; + u16 fc; + union wpa_event_data event; + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_MGMT: + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.datarate = datarate; + event.rx_mgmt.ssi_signal = ssi_signal; + wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); + break; + case WLAN_FC_TYPE_CTRL: + /* can only get here with PS-Poll frames */ + wpa_printf(MSG_DEBUG, "CTRL"); + from_unknown_sta(drv, buf, len); + break; + case WLAN_FC_TYPE_DATA: + from_unknown_sta(drv, buf, len); + break; + } +} + + +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 datarate = 0, ssi_signal = 0; + int injected = 0, failed = 0, rxflags = 0; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s", + strerror(errno)); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) { + wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame"); + return; + } + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_INFO, "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((*(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 */ + break; + case IEEE80211_RADIOTAP_RATE: + datarate = *iter.this_arg * 5; + break; + case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: + ssi_signal = (s8) *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) + handle_frame(drv, buf + iter._max_length, + len - iter._max_length, datarate, ssi_signal); + else + handle_tx_callback(drv->ctx, buf + iter._max_length, + len - iter._max_length, !failed); +} + + +/* + * 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. + * + * For now allow all To DS data frames through. + */ + /* load the IEEE 802.11 frame control field */ + BPF_STMT(BPF_LD | BPF_H | BPF_IND, 0), + /* mask off frame type, version and DS status */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03), + /* accept frame if version 0, type 2 and To DS, fall through otherwise + */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0), + +#if 0 + /* + * drop non-data 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), +#endif + /* load the upper byte of the frame control field */ + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1), + /* mask off toDS/fromDS */ + BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), + /* accept WDS frames */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0), + + /* + * 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 = ARRAY_SIZE(msock_filter_insns), + .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))) { + wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + return -1; + } + + return 0; +} + + +void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv) +{ + if (drv->monitor_refcount > 0) + drv->monitor_refcount--; + wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d", + drv->monitor_refcount); + if (drv->monitor_refcount > 0) + return; + + if (drv->monitor_ifidx >= 0) { + nl80211_remove_iface(drv, drv->monitor_ifidx); + drv->monitor_ifidx = -1; + } + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + drv->monitor_sock = -1; + } +} + + +int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval; + socklen_t optlen; + + if (drv->monitor_ifidx >= 0) { + drv->monitor_refcount++; + wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d", + drv->monitor_refcount); + return 0; + } + + if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) { + /* + * P2P interface name is of the format p2p-%s-%d. For monitor + * interface name corresponding to P2P GO, replace "p2p-" with + * "mon-" to retain the same interface name length and to + * indicate that it is a monitor interface. + */ + snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); + } else { + /* Non-P2P interface with AP functionality. */ + snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); + } + + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL, + 0, NULL, NULL, 0); + + if (drv->monitor_ifidx == -EOPNOTSUPP) { + /* + * This is backward compatibility for a few versions of + * the kernel only that didn't advertise the right + * attributes for the only driver that then supported + * AP mode w/o monitor -- ath6kl. + */ + wpa_printf(MSG_DEBUG, "nl80211: Driver does not support " + "monitor interface type - try to run without it"); + drv->device_ap_sme = 1; + } + + if (drv->monitor_ifidx < 0) + return -1; + + if (linux_set_iface_flags(drv->global->ioctl_sock, 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) { + wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s", + strerror(errno)); + 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) { + wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s", + strerror(errno)); + goto error; + } + + optlen = sizeof(optval); + optval = 20; + if (setsockopt + (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s", + strerror(errno)); + goto error; + } + + if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, + drv, NULL)) { + wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket"); + goto error; + } + + drv->monitor_refcount++; + return 0; + error: + nl80211_remove_monitor_interface(drv); + return -1; +} + + +int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, + const void *data, size_t len, + int encrypt, int noack) +{ + __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 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, + }; + int res; + u16 txflags = 0; + + if (encrypt) + rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP; + + if (drv->monitor_sock < 0) { + wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available " + "for %s", __func__); + return -1; + } + + if (noack) + txflags |= IEEE80211_RADIOTAP_F_TX_NOACK; + WPA_PUT_LE16(&rtap_hdr[12], txflags); + + res = sendmsg(drv->monitor_sock, &msg, 0); + if (res < 0) { + wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno)); + return -1; + } + return 0; +} diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c new file mode 100644 index 00000000..7538d602 --- /dev/null +++ b/src/drivers/driver_nl80211_scan.c @@ -0,0 +1,796 @@ +/* + * Driver interaction with Linux nl80211/cfg80211 - Scanning + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * Copyright (c) 2009-2010, Atheros Communications + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <netlink/genl/genl.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "driver_nl80211.h" + + +static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *scan_results = arg; + struct wpa_scan_res *scan_res; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + wpa_printf(MSG_DEBUG, "nl80211: Survey data missing"); + return NL_SKIP; + } + + if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, + tb[NL80211_ATTR_SURVEY_INFO], + survey_policy)) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested " + "attributes"); + return NL_SKIP; + } + + if (!sinfo[NL80211_SURVEY_INFO_NOISE]) + return NL_SKIP; + + if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) + return NL_SKIP; + + for (i = 0; i < scan_results->num; ++i) { + scan_res = scan_results->res[i]; + if (!scan_res) + continue; + if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != + scan_res->freq) + continue; + if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + scan_res->noise = (s8) + nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; + } + + return NL_SKIP; +} + + +static int nl80211_get_noise_for_scan_results( + struct wpa_driver_nl80211_data *drv, + struct wpa_scan_results *scan_res) +{ + struct nl_msg *msg; + + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, + scan_res); +} + + +/** + * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Driver private data + * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) { + wpa_driver_nl80211_set_mode(drv->first_bss, + drv->ap_scan_as_station); + drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED; + } + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static struct nl_msg * +nl80211_scan_common(struct i802_bss *bss, u8 cmd, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + size_t i; + u32 scan_flags = 0; + + msg = nl80211_cmd_msg(bss, 0, cmd); + if (!msg) + return NULL; + + if (params->num_ssids) { + struct nlattr *ssids; + + ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS); + if (ssids == NULL) + goto fail; + for (i = 0; i < params->num_ssids; i++) { + wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID", + params->ssids[i].ssid, + params->ssids[i].ssid_len); + if (nla_put(msg, i + 1, params->ssids[i].ssid_len, + params->ssids[i].ssid)) + goto fail; + } + nla_nest_end(msg, ssids); + } + + if (params->extra_ies) { + wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs", + params->extra_ies, params->extra_ies_len); + if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len, + params->extra_ies)) + goto fail; + } + + if (params->freqs) { + struct nlattr *freqs; + freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES); + if (freqs == NULL) + goto fail; + for (i = 0; params->freqs[i]; i++) { + wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u " + "MHz", params->freqs[i]); + if (nla_put_u32(msg, i + 1, params->freqs[i])) + goto fail; + } + nla_nest_end(msg, freqs); + } + + os_free(drv->filter_ssids); + drv->filter_ssids = params->filter_ssids; + params->filter_ssids = NULL; + drv->num_filter_ssids = params->num_filter_ssids; + + if (params->only_new_results) { + wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH"); + scan_flags |= NL80211_SCAN_FLAG_FLUSH; + } + + if (params->low_priority && drv->have_low_prio_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY"); + scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY; + } + + if (params->mac_addr_rand) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR"); + scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR; + + if (params->mac_addr) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR, + MAC2STR(params->mac_addr)); + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, + params->mac_addr)) + goto fail; + } + + if (params->mac_addr_mask) { + wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: " + MACSTR, MAC2STR(params->mac_addr_mask)); + if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN, + params->mac_addr_mask)) + goto fail; + } + } + + if (scan_flags && + nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + goto fail; + + return msg; + +fail: + nlmsg_free(msg); + return NULL; +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @bss: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_nl80211_scan(struct i802_bss *bss, + struct wpa_driver_scan_params *params) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1, timeout; + struct nl_msg *msg = NULL; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request"); + drv->scan_for_auth = 0; + + msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params); + if (!msg) + return -1; + + if (params->p2p_probe) { + struct nlattr *rates; + + wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates"); + + rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES); + if (rates == NULL) + goto fail; + + /* + * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates + * by masking out everything else apart from the OFDM rates 6, + * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz + * rates are left enabled. + */ + if (nla_put(msg, NL80211_BAND_2GHZ, 8, + "\x0c\x12\x18\x24\x30\x48\x60\x6c")) + goto fail; + nla_nest_end(msg, rates); + + if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) + goto fail; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " + "(%s)", ret, strerror(-ret)); + if (drv->hostapd && is_ap_interface(drv->nlmode)) { + enum nl80211_iftype old_mode = drv->nlmode; + + /* + * mac80211 does not allow scan requests in AP mode, so + * try to do this in station mode. + */ + if (wpa_driver_nl80211_set_mode( + bss, NL80211_IFTYPE_STATION)) + goto fail; + + if (wpa_driver_nl80211_scan(bss, params)) { + wpa_driver_nl80211_set_mode(bss, drv->nlmode); + goto fail; + } + + /* Restore AP mode when processing scan results */ + drv->ap_scan_as_station = old_mode; + ret = 0; + } else + goto fail; + } + + drv->scan_state = SCAN_REQUESTED; + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 10; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver events to notify when scan is + * complete, so use longer timeout to avoid race conditions + * with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @params: Scan parameters + * @interval: Interval between scan cycles in milliseconds + * Returns: 0 on success, -1 on failure or if not supported + */ +int wpa_driver_nl80211_sched_scan(void *priv, + struct wpa_driver_scan_params *params, + u32 interval) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret = -1; + struct nl_msg *msg; + size_t i; + + wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request"); + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_start(bss, params); +#endif /* ANDROID */ + + msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval)) + goto fail; + + if ((drv->num_filter_ssids && + (int) drv->num_filter_ssids <= drv->capa.max_match_sets) || + params->filter_rssi) { + struct nlattr *match_sets; + match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH); + if (match_sets == NULL) + goto fail; + + for (i = 0; i < drv->num_filter_ssids; i++) { + struct nlattr *match_set_ssid; + wpa_hexdump_ascii(MSG_MSGDUMP, + "nl80211: Sched scan filter SSID", + drv->filter_ssids[i].ssid, + drv->filter_ssids[i].ssid_len); + + match_set_ssid = nla_nest_start(msg, i + 1); + if (match_set_ssid == NULL || + nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID, + drv->filter_ssids[i].ssid_len, + drv->filter_ssids[i].ssid) || + (params->filter_rssi && + nla_put_u32(msg, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi))) + goto fail; + + nla_nest_end(msg, match_set_ssid); + } + + /* + * Due to backward compatibility code, newer kernels treat this + * matchset (with only an RSSI filter) as the default for all + * other matchsets, unless it's the only one, in which case the + * matchset will actually allow all SSIDs above the RSSI. + */ + if (params->filter_rssi) { + struct nlattr *match_set_rssi; + match_set_rssi = nla_nest_start(msg, 0); + if (match_set_rssi == NULL || + nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + params->filter_rssi)) + goto fail; + wpa_printf(MSG_MSGDUMP, + "nl80211: Sched scan RSSI filter %d dBm", + params->filter_rssi); + nla_nest_end(msg, match_set_rssi); + } + + nla_nest_end(msg, match_sets); + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + /* TODO: if we get an error here, we should fall back to normal scan */ + + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: " + "ret=%d (%s)", ret, strerror(-ret)); + goto fail; + } + + wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - " + "scan interval %d msec", ret, interval); + +fail: + nlmsg_free(msg); + return ret; +} + + +/** + * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * Returns: 0 on success, -1 on failure or if not supported + */ +int wpa_driver_nl80211_stop_sched_scan(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + int ret; + struct nl_msg *msg; + +#ifdef ANDROID + if (!drv->capa.sched_scan_supported) + return android_pno_stop(bss); +#endif /* ANDROID */ + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: Sched scan stop failed: ret=%d (%s)", + ret, strerror(-ret)); + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Sched scan stop sent"); + } + + return ret; +} + + +static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie) +{ + const u8 *end, *pos; + + if (ies == NULL) + return NULL; + + pos = ies; + end = ies + ies_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, + const u8 *ie, size_t ie_len) +{ + const u8 *ssid; + size_t i; + + if (drv->filter_ssids == NULL) + return 0; + + ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid == NULL) + return 1; + + for (i = 0; i < drv->num_filter_ssids; i++) { + if (ssid[1] == drv->filter_ssids[i].ssid_len && + os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) == + 0) + return 0; + } + + return 1; +} + + +int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, + [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, + }; + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + const u8 *ie, *beacon_ie; + size_t ie_len, beacon_ie_len; + u8 *pos; + size_t i; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->assoc_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + _arg->assoc_freq); + } + if (status == NL80211_BSS_STATUS_IBSS_JOINED && + bss[NL80211_BSS_FREQUENCY]) { + _arg->ibss_freq = + nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", + _arg->ibss_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(_arg->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(_arg->assoc_bssid)); + } + } + if (!res) + return NL_SKIP; + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + } else { + ie = NULL; + ie_len = 0; + } + if (bss[NL80211_BSS_BEACON_IES]) { + beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]); + beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]); + } else { + beacon_ie = NULL; + beacon_ie_len = 0; + } + + if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + ie ? ie_len : beacon_ie_len)) + return NL_SKIP; + + r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); + if (r == NULL) + return NL_SKIP; + if (bss[NL80211_BSS_BSSID]) + os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), + ETH_ALEN); + if (bss[NL80211_BSS_FREQUENCY]) + r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_BEACON_INTERVAL]) + r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); + if (bss[NL80211_BSS_CAPABILITY]) + r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + r->flags |= WPA_SCAN_NOISE_INVALID; + if (bss[NL80211_BSS_SIGNAL_MBM]) { + r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + r->level /= 100; /* mBm to dBm */ + r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID; + } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) { + r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + r->flags |= WPA_SCAN_QUAL_INVALID; + } else + r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID; + if (bss[NL80211_BSS_TSF]) + r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + if (bss[NL80211_BSS_SEEN_MS_AGO]) + r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); + r->ie_len = ie_len; + pos = (u8 *) (r + 1); + if (ie) { + os_memcpy(pos, ie, ie_len); + pos += ie_len; + } + r->beacon_ie_len = beacon_ie_len; + if (beacon_ie) + os_memcpy(pos, beacon_ie, beacon_ie_len); + + if (bss[NL80211_BSS_STATUS]) { + enum nl80211_bss_status status; + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + switch (status) { + case NL80211_BSS_STATUS_AUTHENTICATED: + r->flags |= WPA_SCAN_AUTHENTICATED; + break; + case NL80211_BSS_STATUS_ASSOCIATED: + r->flags |= WPA_SCAN_ASSOCIATED; + break; + default: + break; + } + } + + /* + * cfg80211 maintains separate BSS table entries for APs if the same + * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does + * not use frequency as a separate key in the BSS table, so filter out + * duplicated entries. Prefer associated BSS entry in such a case in + * order to get the correct frequency into the BSS table. Similarly, + * prefer newer entries over older. + */ + for (i = 0; i < res->num; i++) { + const u8 *s1, *s2; + if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) + continue; + + s1 = nl80211_get_ie((u8 *) (res->res[i] + 1), + res->res[i]->ie_len, WLAN_EID_SSID); + s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); + if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || + os_memcmp(s1, s2, 2 + s1[1]) != 0) + continue; + + /* Same BSSID,SSID was already included in scan results */ + wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " + "for " MACSTR, MAC2STR(r->bssid)); + + if (((r->flags & WPA_SCAN_ASSOCIATED) && + !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || + r->age < res->res[i]->age) { + os_free(res->res[i]); + res->res[i] = r; + } else + os_free(r); + return NL_SKIP; + } + + tmp = os_realloc_array(res->res, res->num + 1, + sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return NL_SKIP; + } + tmp[res->num++] = r; + res->res = tmp; + + return NL_SKIP; +} + + +static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, + const u8 *addr) +{ + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { + wpa_printf(MSG_DEBUG, "nl80211: Clear possible state " + "mismatch (" MACSTR ")", MAC2STR(addr)); + wpa_driver_nl80211_mlme(drv, addr, + NL80211_CMD_DEAUTHENTICATE, + WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + } +} + + +static void wpa_driver_nl80211_check_bss_status( + struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res) +{ + size_t i; + + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + if (r->flags & WPA_SCAN_AUTHENTICATED) { + wpa_printf(MSG_DEBUG, "nl80211: Scan results " + "indicates BSS status with " MACSTR + " as authenticated", + MAC2STR(r->bssid)); + if (is_sta_interface(drv->nlmode) && + os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 && + os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID" + " in local state (auth=" MACSTR + " assoc=" MACSTR ")", + MAC2STR(drv->auth_bssid), + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + } + } + + if (r->flags & WPA_SCAN_ASSOCIATED) { + wpa_printf(MSG_DEBUG, "nl80211: Scan results " + "indicate BSS status with " MACSTR + " as associated", + MAC2STR(r->bssid)); + if (is_sta_interface(drv->nlmode) && + !drv->associated) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(not associated) does not match " + "with BSS state"); + clear_state_mismatch(drv, r->bssid); + } else if (is_sta_interface(drv->nlmode) && + os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != + 0) { + wpa_printf(MSG_DEBUG, "nl80211: Local state " + "(associated with " MACSTR ") does " + "not match with BSS state", + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + clear_state_mismatch(drv, drv->bssid); + } + } + } +} + + +static struct wpa_scan_results * +nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct wpa_scan_results *res; + int ret; + struct nl80211_bss_info_arg arg; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, + NL80211_CMD_GET_SCAN))) { + wpa_scan_results_free(res); + return NULL; + } + + arg.drv = drv; + arg.res = res; + ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + if (ret == 0) { + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " + "BSSes)", (unsigned long) res->num); + nl80211_get_noise_for_scan_results(drv, res); + return res; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); + wpa_scan_results_free(res); + return NULL; +} + + +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct wpa_scan_results *res; + + res = nl80211_get_scan_results(drv); + if (res) + wpa_driver_nl80211_check_bss_status(drv, res); + return res; +} + + +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct wpa_scan_results *res; + size_t i; + + res = nl80211_get_scan_results(drv); + if (res == NULL) { + wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); + return; + } + + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + for (i = 0; i < res->num; i++) { + struct wpa_scan_res *r = res->res[i]; + wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s", + (int) i, (int) res->num, MAC2STR(r->bssid), + r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "", + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + } + + wpa_scan_results_free(res); +} diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c index d75c14b1..6ff3eae6 100644 --- a/src/drivers/driver_none.c +++ b/src/drivers/driver_none.c @@ -74,13 +74,6 @@ static void none_driver_deinit(void *priv) } -static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto, - const u8 *data, size_t data_len) -{ - return -1; -} - - const struct wpa_driver_ops wpa_driver_none_ops = { .name = "none", .desc = "no driver (RADIUS server/WPS ER)", @@ -89,5 +82,4 @@ const struct wpa_driver_ops wpa_driver_none_ops = { .send_ether = none_driver_send_ether, .init = none_driver_init, .deinit = none_driver_deinit, - .send_eapol = none_driver_send_eapol, }; diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index ed88e71c..de23fbd2 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -35,7 +35,7 @@ static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) (struct sockaddr *) &drv->priv_addr, sizeof(drv->priv_addr)); if (res < 0) - perror("sendto"); + wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno)); return res < 0 ? -1 : 0; } @@ -59,7 +59,8 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, msg.msg_namelen = sizeof(drv->priv_addr); if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { - perror("sendmsg(cmd_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s", + strerror(errno)); return -1; } @@ -74,14 +75,15 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, tv.tv_usec = 0; res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { - perror("select"); + wpa_printf(MSG_ERROR, "select: %s", strerror(errno)); return -1; } if (FD_ISSET(drv->cmd_socket, &rfds)) { res = recv(drv->cmd_socket, reply, *reply_len, 0); if (res < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", + strerror(errno)); return -1; } *reply_len = res; @@ -228,7 +230,7 @@ static int wpa_driver_privsep_associate( 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, + __func__, priv, params->freq.freq, params->pairwise_suite, params->group_suite, params->key_mgmt_suite, params->auth_alg, params->mode); @@ -241,7 +243,9 @@ static int wpa_driver_privsep_associate( 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->hwmode = params->freq.mode; + data->freq = params->freq.freq; + data->channel = params->freq.channel; data->pairwise_suite = params->pairwise_suite; data->group_suite = params->group_suite; data->key_mgmt_suite = params->key_mgmt_suite; @@ -439,7 +443,8 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, buflen, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(priv_socket)"); + wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s", + strerror(errno)); os_free(buf); return; } @@ -629,7 +634,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->priv_socket < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(drv->own_socket_path); drv->own_socket_path = NULL; return -1; @@ -640,7 +645,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) 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("privsep-set-params priv-sock: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "privsep-set-params priv-sock: bind(PF_UNIX): %s", + strerror(errno)); close(drv->priv_socket); drv->priv_socket = -1; unlink(drv->own_socket_path); @@ -654,7 +661,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->cmd_socket < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(drv->own_cmd_path); drv->own_cmd_path = NULL; return -1; @@ -665,7 +672,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param) 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("privsep-set-params cmd-sock: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "privsep-set-params cmd-sock: bind(PF_UNIX): %s", + strerror(errno)); close(drv->cmd_socket); drv->cmd_socket = -1; unlink(drv->own_cmd_path); diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 9ce3fa2f..d3e05955 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -91,7 +91,8 @@ static u16 wpa_driver_roboswitch_mdio_read( mii->reg_num = reg; if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { - perror("ioctl[SIOCGMIIREG]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s", + strerror(errno)); return 0x00; } return mii->val_out; @@ -108,7 +109,8 @@ static void wpa_driver_roboswitch_mdio_write( mii->val_in = val; if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { - perror("ioctl[SIOCSMIIREG"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s", + strerror(errno)); } } @@ -394,7 +396,8 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) 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]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s", + strerror(errno)); os_free(drv); return NULL; } diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c deleted file mode 100644 index 66edfa73..00000000 --- a/src/drivers/driver_test.c +++ /dev/null @@ -1,2683 +0,0 @@ -/* - * Testing driver interface for a simulated network driver - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -/* Make sure 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 "utils/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 "utils/common.h" -#include "utils/eloop.h" -#include "utils/list.h" -#include "utils/trace.h" -#include "common/ieee802_11_defs.h" -#include "crypto/sha1.h" -#include "l2_packet/l2_packet.h" -#include "wps/wps.h" -#include "driver.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 wpa_driver_test_data *drv; - struct dl_list list; - void *bss_ctx; - char ifname[IFNAMSIZ]; - 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 wpa_driver_test_global { - int bss_add_used; - u8 req_addr[ETH_ALEN]; -}; - -struct wpa_driver_test_data { - struct wpa_driver_test_global *global; - void *ctx; - WPA_TRACE_REF(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; -#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 associated; - u8 *probe_req_ie; - size_t probe_req_ie_len; - u8 probe_req_ssid[32]; - size_t probe_req_ssid_len; - int ibss; - int ap; - - struct test_client_socket *cli; - struct dl_list bss; - int udp_port; - - int alloc_iface_idx; - - int probe_req_report; - unsigned int remain_on_channel_freq; - unsigned int remain_on_channel_duration; - - int current_freq; -}; - - -static void wpa_driver_test_deinit(void *priv); -static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, - const char *dir, int ap); -static void wpa_driver_test_close_test_socket( - struct wpa_driver_test_data *drv); -static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx); - - -static void test_driver_free_bss(struct test_driver_bss *bss) -{ - os_free(bss->ie); - os_free(bss->wps_beacon_ie); - os_free(bss->wps_probe_resp_ie); - os_free(bss); -} - - -static void test_driver_free_bsses(struct wpa_driver_test_data *drv) -{ - struct test_driver_bss *bss, *tmp; - - dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss, - list) { - dl_list_del(&bss->list); - test_driver_free_bss(bss); - } -} - - -static struct test_client_socket * -test_driver_get_cli(struct wpa_driver_test_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, u32 flags) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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->test_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d " - "test_dir=%p)", - __func__, drv->test_socket, drv->test_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->test_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->test_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 wpa_driver_test_send_mlme(void *priv, const u8 *data, - size_t data_len, int noack) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct msghdr msg; - struct iovec io[2]; - const u8 *dest; - struct sockaddr_un addr; - struct dirent *dent; - DIR *dir; - int broadcast; - int ret = 0; - struct ieee80211_hdr *hdr; - u16 fc; - char cmd[50]; - int freq; -#ifdef HOSTAPD - char desttxt[30]; -#endif /* HOSTAPD */ - union wpa_event_data event; - - wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); - if (drv->test_socket < 0 || data_len < 10) { - wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu" - " test_dir=%p)", - __func__, drv->test_socket, - (unsigned long) data_len, - drv->test_dir); - return -1; - } - - dest = data + 4; - broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; - -#ifdef HOSTAPD - snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); -#endif /* HOSTAPD */ - - if (drv->remain_on_channel_freq) - freq = drv->remain_on_channel_freq; - else - freq = drv->current_freq; - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz", - dbss->ifname, freq); - os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq); - io[0].iov_base = cmd; - io[0].iov_len = os_strlen(cmd); - io[1].iov_base = (void *) data; - io[1].iov_len = data_len; - - os_memset(&msg, 0, sizeof(msg)); - msg.msg_iov = io; - msg.msg_iovlen = 2; - -#ifdef HOSTAPD - if (drv->test_dir == NULL) { - wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__); - return -1; - } - - dir = opendir(drv->test_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 (os_strcmp(dent->d_name, ".") == 0 || - os_strcmp(dent->d_name, "..") == 0) - continue; - - 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); - - if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0) - continue; - if (!broadcast && os_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(test_socket)"); - } - closedir(dir); -#else /* HOSTAPD */ - - if (os_memcmp(dest, dbss->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 (broadcast) { - 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); - - ret = sendmsg(drv->test_socket, &msg, 0); - if (ret < 0) - perror("driver_test: sendmsg(test_socket)"); - } - closedir(dir); - return ret; - } 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; - } -#endif /* HOSTAPD */ - - hdr = (struct ieee80211_hdr *) data; - fc = le_to_host16(hdr->frame_control); - - os_memset(&event, 0, sizeof(event)); - event.tx_status.type = WLAN_FC_GET_TYPE(fc); - event.tx_status.stype = WLAN_FC_GET_STYPE(fc); - event.tx_status.dst = hdr->addr1; - event.tx_status.data = data; - event.tx_status.data_len = data_len; - event.tx_status.ack = ret >= 0; - wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event); - - return ret; -} - - -static void test_driver_scan(struct wpa_driver_test_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; - union wpa_event_data event; - - /* 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); - - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = sa; - event.rx_probe_req.ie = ie; - event.rx_probe_req.ie_len = ielen; - wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event); - } - - dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { - 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 void test_driver_assoc(struct wpa_driver_test_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, *tmp; - - /* 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); - os_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__); - os_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; - } - - bss = NULL; - dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) { - if (tmp->ssid_len == ssid_len && - os_memcmp(tmp->ssid, ssid, ssid_len) == 0) { - bss = tmp; - break; - } - } - if (bss == NULL) { - wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " - "configured BSSes", __func__); - os_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); - - drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0); -} - - -static void test_driver_disassoc(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen) -{ - struct test_client_socket *cli; - - cli = test_driver_get_cli(drv, from, fromlen); - if (!cli) - return; - - drv_event_disassoc(drv->ctx, cli->addr); -} - - -static void test_driver_eapol(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ -#ifdef HOSTAPD - struct test_client_socket *cli; -#endif /* HOSTAPD */ - const u8 *src = NULL; - - if (datalen > 14) { - /* Skip Ethernet header */ - src = data + ETH_ALEN; - wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src=" - MACSTR " proto=%04x", - MAC2STR(data), MAC2STR(src), - WPA_GET_BE16(data + 2 * ETH_ALEN)); - data += 14; - datalen -= 14; - } - -#ifdef HOSTAPD - cli = test_driver_get_cli(drv, from, fromlen); - if (cli) { - drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data, - datalen); - } else { - wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " - "client"); - } -#else /* HOSTAPD */ - if (src) - drv_event_eapol_rx(drv->ctx, src, data, datalen); -#endif /* HOSTAPD */ -} - - -static void test_driver_ether(struct wpa_driver_test_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) { - union wpa_event_data ev; - os_memset(&ev, 0, sizeof(ev)); - ev.ft_rrb_rx.src = eth->h_source; - ev.ft_rrb_rx.data = data + sizeof(*eth); - ev.ft_rrb_rx.data_len = datalen - sizeof(*eth); - } -#endif /* CONFIG_IEEE80211R */ -} - - -static void test_driver_mlme(struct wpa_driver_test_data *drv, - struct sockaddr_un *from, socklen_t fromlen, - u8 *data, size_t datalen) -{ - struct ieee80211_hdr *hdr; - u16 fc; - union wpa_event_data event; - int freq = 0, own_freq; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) { - size_t pos; - for (pos = 5; pos < datalen; pos++) { - if (data[pos] == ' ') - break; - } - if (pos < datalen) { - freq = atoi((const char *) &data[5]); - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " - "freq %d MHz", bss->ifname, freq); - pos++; - data += pos; - datalen -= pos; - } - } - - if (drv->remain_on_channel_freq) - own_freq = drv->remain_on_channel_freq; - else - own_freq = drv->current_freq; - - if (freq && own_freq && freq != own_freq) { - wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " - "another frequency %d MHz (own %d MHz)", - bss->ifname, freq, own_freq); - return; - } - - 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; - } - - os_memset(&event, 0, sizeof(event)); - event.rx_mgmt.frame = data; - event.rx_mgmt.frame_len = datalen; - wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event); -} - - -static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wpa_driver_test_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 int test_driver_set_generic_elem(void *priv, - const u8 *elem, size_t elem_len) -{ - struct test_driver_bss *bss = priv; - - os_free(bss->ie); - - if (elem == NULL) { - bss->ie = NULL; - bss->ielen = 0; - return 0; - } - - bss->ie = os_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_ap_wps_ie(void *priv, const struct wpabuf *beacon, - const struct wpabuf *proberesp, - const struct wpabuf *assocresp) -{ - struct test_driver_bss *bss = priv; - - if (beacon == NULL) - wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE"); - else - wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE", - beacon); - - os_free(bss->wps_beacon_ie); - - if (beacon == NULL) { - bss->wps_beacon_ie = NULL; - bss->wps_beacon_ie_len = 0; - } else { - bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon)); - if (bss->wps_beacon_ie == NULL) { - bss->wps_beacon_ie_len = 0; - return -1; - } - - os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon), - wpabuf_len(beacon)); - bss->wps_beacon_ie_len = wpabuf_len(beacon); - } - - if (proberesp == NULL) - wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS " - "IE"); - else - wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS " - "IE", proberesp); - - os_free(bss->wps_probe_resp_ie); - - if (proberesp == NULL) { - bss->wps_probe_resp_ie = NULL; - bss->wps_probe_resp_ie_len = 0; - } else { - bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp)); - if (bss->wps_probe_resp_ie == NULL) { - bss->wps_probe_resp_ie_len = 0; - return -1; - } - - os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp), - wpabuf_len(proberesp)); - bss->wps_probe_resp_ie_len = wpabuf_len(proberesp); - } - - return 0; -} - - -static int test_driver_sta_deauth(void *priv, const u8 *own_addr, - const u8 *addr, int reason) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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 *own_addr, - const u8 *addr, int reason) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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 int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid, - void *bss_ctx, void **drv_priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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; - - bss->bss_ctx = bss_ctx; - bss->drv = drv; - os_strlcpy(bss->ifname, ifname, IFNAMSIZ); - os_memcpy(bss->bssid, bssid, ETH_ALEN); - - dl_list_add(&drv->bss, &bss->list); - if (drv->global) { - drv->global->bss_add_used = 1; - os_memcpy(drv->global->req_addr, bssid, ETH_ALEN); - } - - if (drv_priv) - *drv_priv = bss; - - return 0; -} - - -static int test_driver_bss_remove(void *priv, const char *ifname) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_driver_bss *bss; - struct test_client_socket *cli, *prev_c; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); - - dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) { - if (strcmp(bss->ifname, ifname) != 0) - continue; - - 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; - os_free(cli); - break; - } - - dl_list_del(&bss->list); - test_driver_free_bss(bss); - return 0; - } - - return -1; -} - - -static int test_driver_if_add(void *priv, enum wpa_driver_if_type type, - const char *ifname, const u8 *addr, - void *bss_ctx, void **drv_priv, - char *force_ifname, u8 *if_addr, - const char *bridge, int use_existing) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)", - __func__, type, ifname, bss_ctx); - if (addr) - os_memcpy(if_addr, addr, ETH_ALEN); - else { - drv->alloc_iface_idx++; - if_addr[0] = 0x02; /* locally administered */ - sha1_prf(drv->own_addr, ETH_ALEN, - "hostapd test addr generation", - (const u8 *) &drv->alloc_iface_idx, - sizeof(drv->alloc_iface_idx), - if_addr + 1, ETH_ALEN - 1); - } - if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || - type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) - return test_driver_bss_add(priv, ifname, if_addr, bss_ctx, - drv_priv); - return 0; -} - - -static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type, - const char *ifname) -{ - wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); - if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO || - type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP) - return test_driver_bss_remove(priv, ifname); - return 0; -} - - -static int test_driver_set_ssid(void *priv, const u8 *buf, int len) -{ - struct test_driver_bss *bss = priv; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname); - if (len < 0) - return -1; - wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); - - if ((size_t) len > sizeof(bss->ssid)) - return -1; - - os_memcpy(bss->ssid, buf, len); - bss->ssid_len = len; - - return 0; -} - - -static int test_driver_set_privacy(void *priv, int enabled) -{ - struct test_driver_bss *dbss = priv; - - wpa_printf(MSG_DEBUG, "%s(enabled=%d)", __func__, enabled); - dbss->privacy = enabled; - - 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(void *priv, - struct hostapd_sta_add_params *params) -{ - struct test_driver_bss *bss = priv; - struct wpa_driver_test_data *drv = bss->drv; - struct test_client_socket *cli; - - wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d " - "capability=0x%x listen_interval=%d)", - __func__, bss->ifname, MAC2STR(params->addr), params->aid, - params->capability, params->listen_interval); - wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", - params->supp_rates, params->supp_rates_len); - - cli = drv->cli; - while (cli) { - if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0) - break; - cli = cli->next; - } - if (!cli) { - wpa_printf(MSG_DEBUG, "%s: no matching client entry", - __func__); - return -1; - } - - cli->bss = bss; - - return 0; -} - - -static struct wpa_driver_test_data * test_alloc_data(void *ctx, - const char *ifname) -{ - struct wpa_driver_test_data *drv; - struct test_driver_bss *bss; - - drv = os_zalloc(sizeof(struct wpa_driver_test_data)); - if (drv == NULL) { - wpa_printf(MSG_ERROR, "Could not allocate memory for test " - "driver data"); - return NULL; - } - - bss = os_zalloc(sizeof(struct test_driver_bss)); - if (bss == NULL) { - os_free(drv); - return NULL; - } - - drv->ctx = ctx; - wpa_trace_add_ref(drv, ctx, ctx); - dl_list_init(&drv->bss); - dl_list_add(&drv->bss, &bss->list); - os_strlcpy(bss->ifname, ifname, IFNAMSIZ); - bss->bss_ctx = ctx; - bss->drv = drv; - - /* 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), - "test mac addr generation", - NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); - - return drv; -} - - -static void * test_driver_init(struct hostapd_data *hapd, - struct wpa_init_params *params) -{ - struct wpa_driver_test_data *drv; - struct sockaddr_un addr_un; - struct sockaddr_in addr_in; - struct sockaddr *addr; - socklen_t alen; - struct test_driver_bss *bss; - - drv = test_alloc_data(hapd, params->ifname); - if (drv == NULL) - return NULL; - drv->ap = 1; - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - drv->global = params->global_priv; - - bss->bss_ctx = hapd; - os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN); - os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN); - - if (params->test_socket) { - if (os_strlen(params->test_socket) >= - sizeof(addr_un.sun_path)) { - printf("Too long test_socket path\n"); - wpa_driver_test_deinit(bss); - return NULL; - } - if (strncmp(params->test_socket, "DIR:", 4) == 0) { - size_t len = strlen(params->test_socket) + 30; - drv->test_dir = os_strdup(params->test_socket + 4); - drv->own_socket_path = os_malloc(len); - if (drv->own_socket_path) { - snprintf(drv->own_socket_path, len, - "%s/AP-" MACSTR, - params->test_socket + 4, - MAC2STR(params->own_addr)); - } - } else if (strncmp(params->test_socket, "UDP:", 4) == 0) { - drv->udp_port = atoi(params->test_socket + 4); - } else { - drv->own_socket_path = os_strdup(params->test_socket); - } - if (drv->own_socket_path == NULL && drv->udp_port == 0) { - wpa_driver_test_deinit(bss); - return NULL; - } - - drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX, - SOCK_DGRAM, 0); - if (drv->test_socket < 0) { - perror("socket"); - wpa_driver_test_deinit(bss); - 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("test-driver-init: bind(PF_UNIX)"); - close(drv->test_socket); - if (drv->own_socket_path) - unlink(drv->own_socket_path); - wpa_driver_test_deinit(bss); - return NULL; - } - eloop_register_read_sock(drv->test_socket, - test_driver_receive_unix, drv, NULL); - } else - drv->test_socket = -1; - - return bss; -} - - -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 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); - } - if (drv->probe_req_ssid_len) { - /* Add SSID IE */ - ret = os_snprintf(pos, end - pos, "%02x%02x", - WLAN_EID_SSID, - (unsigned int) drv->probe_req_ssid_len); - if (ret >= 0 && ret < end - pos) - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid, - drv->probe_req_ssid_len); - } - end[-1] = '\0'; - - while ((dent = readdir(dir))) { - if (os_strncmp(dent->d_name, "AP-", 3) != 0 && - os_strncmp(dent->d_name, "STA-", 4) != 0) - continue; - if (drv->own_socket_path) { - size_t olen, dlen; - olen = os_strlen(drv->own_socket_path); - dlen = os_strlen(dent->d_name); - if (olen >= dlen && - os_strcmp(dent->d_name, - drv->own_socket_path + olen - dlen) == 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, - struct wpa_driver_scan_params *params) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - size_t i; - - wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); - - os_free(drv->probe_req_ie); - if (params->extra_ies) { - drv->probe_req_ie = os_malloc(params->extra_ies_len); - if (drv->probe_req_ie == NULL) { - drv->probe_req_ie_len = 0; - return -1; - } - os_memcpy(drv->probe_req_ie, params->extra_ies, - params->extra_ies_len); - drv->probe_req_ie_len = params->extra_ies_len; - } else { - drv->probe_req_ie = NULL; - drv->probe_req_ie_len = 0; - } - - for (i = 0; i < params->num_ssids; i++) - wpa_hexdump(MSG_DEBUG, "Scan SSID", - params->ssids[i].ssid, params->ssids[i].ssid_len); - drv->probe_req_ssid_len = 0; - if (params->num_ssids) { - os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid, - params->ssids[0].ssid_len); - drv->probe_req_ssid_len = params->ssids[0].ssid_len; - } - wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)", - params->extra_ies, params->extra_ies_len); - - 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 test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct wpa_scan_results *res; - size_t i; - - res = os_zalloc(sizeof(*res)); - if (res == NULL) - return NULL; - - res->res = os_calloc(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(const char *ifname, void *priv, - enum wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, - const u8 *seq, size_t seq_len, - const u8 *key, size_t key_len) -{ - wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d " - "set_tx=%d", - __func__, ifname, 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_key(MSG_DEBUG, " key", key, key_len); - return 0; -} - - -static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap) -{ - if (ap && !drv->ap) { - wpa_driver_test_close_test_socket(drv); - wpa_driver_test_attach(drv, drv->test_dir, 1); - drv->ap = 1; - } else if (!ap && drv->ap) { - wpa_driver_test_close_test_socket(drv); - wpa_driver_test_attach(drv, drv->test_dir, 0); - drv->ap = 0; - } - - return 0; -} - - -static int wpa_driver_test_associate( - void *priv, struct wpa_driver_associate_params *params) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " - "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", - __func__, priv, params->freq.freq, params->pairwise_suite, - params->group_suite, params->key_mgmt_suite, - params->auth_alg, params->mode); - wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); - 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; - - wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP); - - drv->ibss = params->mode == IEEE80211_MODE_IBSS; - dbss->privacy = params->key_mgmt_suite & - (WPA_KEY_MGMT_IEEE8021X | - WPA_KEY_MGMT_PSK | - WPA_KEY_MGMT_WPA_NONE | - WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_PSK | - WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256); - if (params->wep_key_len[params->wep_tx_keyidx]) - dbss->privacy = 1; - -#ifdef DRIVER_TEST_UNIX - if (drv->test_dir && params->bssid && - params->mode != IEEE80211_MODE_IBSS) { - 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 (params->mode == IEEE80211_MODE_AP) { - if (params->ssid) - os_memcpy(dbss->ssid, params->ssid, params->ssid_len); - dbss->ssid_len = params->ssid_len; - os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN); - if (params->wpa_ie && params->wpa_ie_len) { - dbss->ie = os_malloc(params->wpa_ie_len); - if (dbss->ie) { - os_memcpy(dbss->ie, params->wpa_ie, - params->wpa_ie_len); - dbss->ielen = params->wpa_ie_len; - } - } - } else 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; - if (params->ssid) - 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; - } - - if (params->ssid) - os_memcpy(dbss->ssid, params->ssid, params->ssid_len); - dbss->ssid_len = params->ssid_len; - } else { - drv->associated = 1; - if (params->mode == IEEE80211_MODE_IBSS) { - if (params->ssid) - os_memcpy(dbss->ssid, params->ssid, - params->ssid_len); - dbss->ssid_len = params->ssid_len; - if (params->bssid) - os_memcpy(dbss->bssid, params->bssid, - ETH_ALEN); - else { - os_get_random(dbss->bssid, ETH_ALEN); - dbss->bssid[0] &= ~0x01; - dbss->bssid[0] |= 0x02; - } - } - wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); - } - - return 0; -} - - -static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) -{ - struct test_driver_bss *dbss = priv; - os_memcpy(bssid, dbss->bssid, ETH_ALEN); - return 0; -} - - -static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) -{ - struct test_driver_bss *dbss = priv; - os_memcpy(ssid, dbss->ssid, 32); - return dbss->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 test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", - __func__, MAC2STR(addr), reason_code); - os_memset(dbss->bssid, 0, ETH_ALEN); - drv->associated = 0; - wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - return wpa_driver_test_send_disassoc(drv); -} - - -static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) -{ - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -static 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 - const u8 *ds_params; - - 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_strstr(pos, "PRIVACY")) - res->caps |= IEEE80211_CAP_PRIVACY; - if (os_strstr(pos, "IBSS")) - res->caps |= IEEE80211_CAP_IBSS; - } - - ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS); - if (ds_params && ds_params[1] > 0) { - if (ds_params[2] >= 1 && ds_params[2] <= 13) - res->freq = 2407 + ds_params[2] * 5; - } - - 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) -{ - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - /* ASSOCRESP BSSID <res> */ - if (hwaddr_aton(data, bss->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; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - if (data_len > 14) { - /* Skip Ethernet header */ - src = data + ETH_ALEN; - data += 14; - data_len -= 14; - } else - src = bss->bssid; - - drv_event_eapol_rx(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) -{ - int freq = 0, own_freq; - union wpa_event_data event; - const struct ieee80211_mgmt *mgmt; - u16 fc; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) { - size_t pos; - for (pos = 5; pos < data_len; pos++) { - if (data[pos] == ' ') - break; - } - if (pos < data_len) { - freq = atoi((const char *) &data[5]); - wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on " - "freq %d MHz", bss->ifname, freq); - pos++; - data += pos; - data_len -= pos; - } - } - - if (drv->remain_on_channel_freq) - own_freq = drv->remain_on_channel_freq; - else - own_freq = drv->current_freq; - - if (freq && own_freq && freq != own_freq) { - wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on " - "another frequency %d MHz (own %d MHz)", - bss->ifname, freq, own_freq); - return; - } - - os_memset(&event, 0, sizeof(event)); - event.mlme_rx.buf = data; - event.mlme_rx.len = data_len; - event.mlme_rx.freq = freq; - wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event); - - mgmt = (const struct ieee80211_mgmt *) data; - fc = le_to_host16(mgmt->frame_control); - - if (drv->probe_req_report && data_len >= 24) { - if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && - WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) { - os_memset(&event, 0, sizeof(event)); - event.rx_probe_req.sa = mgmt->sa; - event.rx_probe_req.da = mgmt->da; - event.rx_probe_req.bssid = mgmt->bssid; - event.rx_probe_req.ie = mgmt->u.probe_req.variable; - event.rx_probe_req.ie_len = - data_len - (mgmt->u.probe_req.variable - data); - wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, - &event); - } - } -} - - -static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv, - struct sockaddr *from, - socklen_t fromlen, - const u8 *data, size_t data_len) -{ - char buf[512], *pos, *end; - int ret; - struct test_driver_bss *bss; - - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - - /* data: optional [ STA-addr | ' ' | IEs(hex) ] */ - - if (bss == NULL || !drv->ibss) - return; - - 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, drv->assoc_wpa_ie, - drv->assoc_wpa_ie_len); - - if (bss->privacy) { - ret = snprintf(pos, end - pos, " PRIVACY"); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - } - - ret = snprintf(pos, end - pos, " IBSS"); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - - sendto(drv->test_socket, buf, pos - buf, 0, - (struct sockaddr *) from, fromlen); -} - - -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; - - if (drv->ap) { - test_driver_receive_unix(sock, eloop_ctx, sock_ctx); - return; - } - - 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 if (os_strncmp(buf, "SCAN ", 5) == 0) { - wpa_driver_test_scan_cmd(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; - struct wpa_driver_test_global *global = global_priv; - struct test_driver_bss *bss; - - drv = test_alloc_data(ctx, ifname); - if (drv == NULL) - return NULL; - bss = dl_list_first(&drv->bss, struct test_driver_bss, list); - drv->global = global_priv; - drv->test_socket = -1; - - /* Set dummy BSSID and SSID for testing. */ - bss->bssid[0] = 0x02; - bss->bssid[1] = 0x00; - bss->bssid[2] = 0x00; - bss->bssid[3] = 0x00; - bss->bssid[4] = 0x00; - bss->bssid[5] = 0x01; - os_memcpy(bss->ssid, "test", 5); - bss->ssid_len = 4; - - if (global->bss_add_used) { - os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN); - global->bss_add_used = 0; - } - - eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); - - return bss; -} - - -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 test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - struct test_client_socket *cli, *prev; - int i; - - cli = drv->cli; - while (cli) { - prev = cli; - cli = cli->next; - os_free(prev); - } - -#ifdef HOSTAPD - /* There should be only one BSS remaining at this point. */ - if (dl_list_len(&drv->bss) != 1) - wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries", - __func__, dl_list_len(&drv->bss)); -#endif /* HOSTAPD */ - - test_driver_free_bsses(drv); - - 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); - eloop_cancel_timeout(test_remain_on_channel_timeout, 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); - wpa_trace_remove_ref(drv, ctx, drv->ctx); - os_free(drv); -} - - -static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, - const char *dir, int ap) -{ -#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/%s-" MACSTR, - dir, ap ? "AP" : "STA", 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("test-driver-attach: 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 test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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, 0)) - 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, 0)) - return -1; - } - - if (os_strstr(param, "use_associnfo=1")) { - wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); - drv->use_associnfo = 1; - } - - return 0; -} - - -static const u8 * wpa_driver_test_get_mac_addr(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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 test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - 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, dbss->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) -{ - 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; - capa->flags |= WPA_DRIVER_FLAGS_AP; - capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT; - capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE; - capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE; - capa->max_scan_ssids = 2; - capa->max_remain_on_chan = 60000; - - 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; -} - - -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; -} - - -static struct hostapd_hw_modes * -wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) -{ - struct hostapd_hw_modes *modes; - size_t i; - - *num_modes = 3; - *flags = 0; - modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes)); - if (modes == NULL) - return NULL; - modes[0].mode = HOSTAPD_MODE_IEEE80211G; - modes[0].num_channels = 11; - modes[0].num_rates = 12; - modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); - modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int)); - if (modes[0].channels == NULL || modes[0].rates == NULL) - goto fail; - for (i = 0; i < 11; i++) { - modes[0].channels[i].chan = i + 1; - modes[0].channels[i].freq = 2412 + 5 * i; - modes[0].channels[i].flag = 0; - } - modes[0].rates[0] = 10; - modes[0].rates[1] = 20; - modes[0].rates[2] = 55; - modes[0].rates[3] = 110; - modes[0].rates[4] = 60; - modes[0].rates[5] = 90; - modes[0].rates[6] = 120; - modes[0].rates[7] = 180; - modes[0].rates[8] = 240; - modes[0].rates[9] = 360; - modes[0].rates[10] = 480; - modes[0].rates[11] = 540; - - modes[1].mode = HOSTAPD_MODE_IEEE80211B; - modes[1].num_channels = 11; - modes[1].num_rates = 4; - modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data)); - modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int)); - if (modes[1].channels == NULL || modes[1].rates == NULL) - goto fail; - for (i = 0; i < 11; i++) { - modes[1].channels[i].chan = i + 1; - modes[1].channels[i].freq = 2412 + 5 * i; - modes[1].channels[i].flag = 0; - } - modes[1].rates[0] = 10; - modes[1].rates[1] = 20; - modes[1].rates[2] = 55; - modes[1].rates[3] = 110; - - modes[2].mode = HOSTAPD_MODE_IEEE80211A; - modes[2].num_channels = 1; - modes[2].num_rates = 8; - modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data)); - modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int)); - if (modes[2].channels == NULL || modes[2].rates == NULL) - goto fail; - modes[2].channels[0].chan = 60; - modes[2].channels[0].freq = 5300; - modes[2].channels[0].flag = 0; - modes[2].rates[0] = 60; - modes[2].rates[1] = 90; - modes[2].rates[2] = 120; - modes[2].rates[3] = 180; - modes[2].rates[4] = 240; - modes[2].rates[5] = 360; - modes[2].rates[6] = 480; - modes[2].rates[7] = 540; - - return modes; - -fail: - if (modes) { - for (i = 0; i < *num_modes; i++) { - os_free(modes[i].channels); - os_free(modes[i].rates); - } - os_free(modes); - } - return NULL; -} - - -static int wpa_driver_test_set_freq(void *priv, - struct hostapd_freq_params *freq) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq); - drv->current_freq = freq->freq; - return 0; -} - - -static int wpa_driver_test_send_action(void *priv, unsigned int freq, - unsigned int wait, - const u8 *dst, const u8 *src, - const u8 *bssid, - const u8 *data, size_t data_len, - int no_cck) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - int ret = -1; - u8 *buf; - struct ieee80211_hdr *hdr; - - wpa_printf(MSG_DEBUG, "test: Send Action frame"); - - if ((drv->remain_on_channel_freq && - freq != drv->remain_on_channel_freq) || - (drv->remain_on_channel_freq == 0 && - freq != (unsigned int) drv->current_freq)) { - wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on " - "unexpected channel: freq=%u MHz (current_freq=%u " - "MHz, remain-on-channel freq=%u MHz)", - freq, drv->current_freq, - drv->remain_on_channel_freq); - return -1; - } - - buf = os_zalloc(24 + data_len); - if (buf == NULL) - return ret; - os_memcpy(buf + 24, data, data_len); - hdr = (struct ieee80211_hdr *) buf; - hdr->frame_control = - IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION); - os_memcpy(hdr->addr1, dst, ETH_ALEN); - os_memcpy(hdr->addr2, src, ETH_ALEN); - os_memcpy(hdr->addr3, bssid, ETH_ALEN); - - ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0); - os_free(buf); - return ret; -} - - -static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_driver_test_data *drv = eloop_ctx; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout"); - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = drv->remain_on_channel_freq; - data.remain_on_channel.duration = drv->remain_on_channel_duration; - - drv->remain_on_channel_freq = 0; - - wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data); -} - - -static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq, - unsigned int duration) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - union wpa_event_data data; - - wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)", - __func__, freq, duration); - if (drv->remain_on_channel_freq && - drv->remain_on_channel_freq != freq) { - wpa_printf(MSG_DEBUG, "test: Refuse concurrent " - "remain_on_channel request"); - return -1; - } - - drv->remain_on_channel_freq = freq; - drv->remain_on_channel_duration = duration; - eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); - eloop_register_timeout(duration / 1000, (duration % 1000) * 1000, - test_remain_on_channel_timeout, drv, NULL); - - os_memset(&data, 0, sizeof(data)); - data.remain_on_channel.freq = freq; - data.remain_on_channel.duration = duration; - wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data); - - return 0; -} - - -static int wpa_driver_test_cancel_remain_on_channel(void *priv) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s", __func__); - if (!drv->remain_on_channel_freq) - return -1; - drv->remain_on_channel_freq = 0; - eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL); - return 0; -} - - -static int wpa_driver_test_probe_req_report(void *priv, int report) -{ - struct test_driver_bss *dbss = priv; - struct wpa_driver_test_data *drv = dbss->drv; - wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report); - drv->probe_req_report = report; - return 0; -} - - -const struct wpa_driver_ops wpa_driver_test_ops = { - "test", - "wpa_supplicant test driver", - .hapd_init = test_driver_init, - .hapd_deinit = wpa_driver_test_deinit, - .hapd_send_eapol = test_driver_send_eapol, - .send_mlme = wpa_driver_test_send_mlme, - .set_generic_elem = test_driver_set_generic_elem, - .sta_deauth = test_driver_sta_deauth, - .sta_disassoc = test_driver_sta_disassoc, - .get_hw_feature_data = wpa_driver_test_get_hw_feature_data, - .if_add = test_driver_if_add, - .if_remove = test_driver_if_remove, - .hapd_set_ssid = test_driver_set_ssid, - .set_privacy = test_driver_set_privacy, - .set_sta_vlan = test_driver_set_sta_vlan, - .sta_add = test_driver_sta_add, - .send_ether = test_driver_send_ether, - .set_ap_wps_ie = test_driver_set_ap_wps_ie, - .get_bssid = wpa_driver_test_get_bssid, - .get_ssid = wpa_driver_test_get_ssid, - .set_key = wpa_driver_test_set_key, - .deinit = wpa_driver_test_deinit, - .set_param = wpa_driver_test_set_param, - .deauthenticate = wpa_driver_test_deauthenticate, - .associate = wpa_driver_test_associate, - .get_capa = wpa_driver_test_get_capa, - .get_mac_addr = wpa_driver_test_get_mac_addr, - .send_eapol = wpa_driver_test_send_eapol, - .mlme_setprotection = wpa_driver_test_mlme_setprotection, - .get_scan_results2 = wpa_driver_test_get_scan_results2, - .global_init = wpa_driver_test_global_init, - .global_deinit = wpa_driver_test_global_deinit, - .init2 = wpa_driver_test_init2, - .get_interfaces = wpa_driver_test_get_interfaces, - .scan2 = wpa_driver_test_scan, - .set_freq = wpa_driver_test_set_freq, - .send_action = wpa_driver_test_send_action, - .remain_on_channel = wpa_driver_test_remain_on_channel, - .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel, - .probe_req_report = wpa_driver_test_probe_req_report, -}; diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 1b3a757b..a1581b8c 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -78,7 +78,7 @@ int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { - perror("ioctl[SIOCGIWAP]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno)); ret = -1; } os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); @@ -108,7 +108,7 @@ int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { - perror("ioctl[SIOCSIWAP]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno)); ret = -1; } @@ -134,7 +134,8 @@ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) iwr.u.essid.length = 32; if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { - perror("ioctl[SIOCGIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s", + strerror(errno)); ret = -1; } else { ret = iwr.u.essid.length; @@ -192,7 +193,8 @@ int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) iwr.u.essid.length = ssid_len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { - perror("ioctl[SIOCSIWESSID]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s", + strerror(errno)); ret = -1; } @@ -218,7 +220,8 @@ int wpa_driver_wext_set_freq(void *priv, int freq) iwr.u.freq.e = 1; if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { - perror("ioctl[SIOCSIWFREQ]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s", + strerror(errno)); ret = -1; } @@ -815,7 +818,8 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname) drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); if (drv->ioctl_sock < 0) { - perror("socket(PF_INET,SOCK_DGRAM)"); + wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s", + strerror(errno)); goto err1; } @@ -1027,7 +1031,8 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params) } if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { - perror("ioctl[SIOCSIWSCAN]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s", + strerror(errno)); ret = -1; } @@ -1082,7 +1087,8 @@ static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, "trying larger buffer (%lu bytes)", (unsigned long) res_buf_len); } else { - perror("ioctl[SIOCGIWSCAN]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s", + strerror(errno)); os_free(res_buf); return NULL; } @@ -1533,7 +1539,8 @@ static int wpa_driver_wext_get_range(void *priv) sizeof(range->enc_capa); if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { - perror("ioctl[SIOCGIWRANGE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s", + strerror(errno)); os_free(range); return -1; } else if (iwr.u.data.length >= minlen && @@ -1568,8 +1575,9 @@ static int wpa_driver_wext_get_range(void *priv) drv->capa.max_scan_ssids = 1; 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); + "flags 0x%llx", + drv->capa.key_mgmt, drv->capa.enc, + (unsigned long long) drv->capa.flags); } else { wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " "assuming WPA is not supported"); @@ -1612,7 +1620,8 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); if (ret < 0) - perror("ioctl[SIOCSIWENCODEEXT] PMK"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s", + strerror(errno)); os_free(ext); return ret; @@ -1704,7 +1713,8 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg, ret = -2; } - perror("ioctl[SIOCSIWENCODEEXT]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s", + strerror(errno)); } os_free(ext); @@ -1778,7 +1788,8 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, iwr.u.encoding.length = key_len; if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s", + strerror(errno)); ret = -1; } @@ -1790,7 +1801,9 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg, 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)"); + wpa_printf(MSG_ERROR, + "ioctl[SIOCSIWENCODE] (set_tx): %s", + strerror(errno)); ret = -1; } } @@ -1839,7 +1852,8 @@ static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, iwr.u.data.length = sizeof(mlme); if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { - perror("ioctl[SIOCSIWMLME]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s", + strerror(errno)); ret = -1; } @@ -1862,7 +1876,8 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) 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]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s", + strerror(errno)); iwr.u.mode = IW_MODE_INFRA; } @@ -1927,7 +1942,8 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, iwr.u.data.length = ie_len; if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { - perror("ioctl[SIOCSIWGENIE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s", + strerror(errno)); ret = -1; } @@ -2004,7 +2020,8 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, } if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { - perror("ioctl[SIOCSIWENCODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s", + strerror(errno)); ret = -1; } @@ -2060,12 +2077,12 @@ int wpa_driver_wext_associate(void *priv, 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) + if (params->wpa_proto & WPA_PROTO_RSN) value = IW_AUTH_WPA_VERSION_WPA2; - else + else if (params->wpa_proto & WPA_PROTO_WPA) value = IW_AUTH_WPA_VERSION_WPA; + else + value = IW_AUTH_WPA_VERSION_DISABLED; if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_VERSION, value) < 0) ret = -1; @@ -2084,7 +2101,7 @@ int wpa_driver_wext_associate(void *priv, value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE || params->pairwise_suite != WPA_CIPHER_NONE || params->group_suite != WPA_CIPHER_NONE || - params->wpa_ie_len; + (params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA)); if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_PRIVACY_INVOKED, value) < 0) ret = -1; @@ -2181,7 +2198,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) } if (errno != EBUSY) { - perror("ioctl[SIOCSIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s", + strerror(errno)); goto done; } @@ -2190,7 +2208,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) * down, try to set the mode again, and bring it back up. */ if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { - perror("ioctl[SIOCGIWMODE]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s", + strerror(errno)); goto done; } @@ -2203,7 +2222,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode) /* 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]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s", + strerror(errno)); else ret = 0; @@ -2236,7 +2256,8 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { if (errno != EOPNOTSUPP) - perror("ioctl[SIOCSIWPMKSA]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s", + strerror(errno)); ret = -1; } @@ -2352,6 +2373,33 @@ static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si) } +static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen) +{ + struct wpa_driver_wext_data *drv = priv; + int res; + char *pos, *end; + unsigned char addr[ETH_ALEN]; + + pos = buf; + end = buf + buflen; + + if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr)) + return -1; + + res = os_snprintf(pos, end - pos, + "ifindex=%d\n" + "ifname=%s\n" + "addr=" MACSTR "\n", + drv->ifindex, + drv->ifname, + MAC2STR(addr)); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + return pos - buf; +} + const struct wpa_driver_ops wpa_driver_wext_ops = { .name = "wext", .desc = "Linux wireless extensions (generic)", @@ -2372,4 +2420,5 @@ const struct wpa_driver_ops wpa_driver_wext_ops = { .set_operstate = wpa_driver_wext_set_operstate, .get_radio_name = wext_get_radio_name, .signal_poll = wpa_driver_wext_signal_poll, + .status = wpa_driver_wext_status, }; diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 21f5e424..f95f3ccf 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -100,7 +100,7 @@ static int wired_multicast_membership(int sock, int ifindex, if (setsockopt(sock, SOL_PACKET, add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { - perror("setsockopt"); + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); return -1; } return 0; @@ -158,7 +158,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -176,7 +176,7 @@ static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) len = recv(sock, buf, sizeof(buf), 0); if (len < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); return; } @@ -209,19 +209,21 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); if (drv->sock < 0) { - perror("socket[PF_PACKET,SOCK_RAW]"); + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); return -1; } if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { - printf("Could not register read socket\n"); + wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { - perror("ioctl(SIOCGIFINDEX)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); return -1; } @@ -232,7 +234,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) addr.sll_ifindex); if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -247,26 +249,28 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) os_memset(&ifr, 0, sizeof(ifr)); os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { - perror("ioctl(SIOCGIFHWADDR)"); + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); return -1; } if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { - printf("Invalid HW-addr family 0x%04x\n", - ifr.ifr_hwaddr.sa_family); + wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x", + ifr.ifr_hwaddr.sa_family); return -1; } os_memcpy(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"); + wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s", + strerror(errno)); return -1; } if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, NULL)) { - printf("Could not register read socket\n"); + wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } @@ -277,12 +281,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s", + strerror(errno)); return -1; } if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) { - perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s", + strerror(errno)); return -1; } @@ -290,13 +296,15 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr)) < 0) { - perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + wpa_printf(MSG_ERROR, + "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s", + strerror(errno)); return -1; } if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, sizeof(struct sockaddr)) == -1) { - perror("bind"); + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } @@ -320,8 +328,9 @@ static int wired_send_eapol(void *priv, const u8 *addr, 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); + wpa_printf(MSG_INFO, + "malloc() failed for wired_send_eapol(len=%lu)", + (unsigned long) len); return -1; } @@ -337,9 +346,9 @@ static int wired_send_eapol(void *priv, const u8 *addr, os_free(hdr); if (res < 0) { - perror("wired_send_eapol: send"); - printf("wired_send_eapol - packet len: %lu - failed\n", - (unsigned long) len); + wpa_printf(MSG_ERROR, + "wired_send_eapol - packet len: %lu - failed: send: %s", + (unsigned long) len, strerror(errno)); } return res; @@ -353,7 +362,8 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd, drv = os_zalloc(sizeof(struct wpa_driver_wired_data)); if (drv == NULL) { - printf("Could not allocate memory for wired driver data\n"); + wpa_printf(MSG_INFO, + "Could not allocate memory for wired driver data"); return NULL; } @@ -374,11 +384,15 @@ static void wired_driver_hapd_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - if (drv->sock >= 0) + if (drv->sock >= 0) { + eloop_unregister_read_sock(drv->sock); close(drv->sock); + } - if (drv->dhcp_sock >= 0) + if (drv->dhcp_sock >= 0) { + eloop_unregister_read_sock(drv->dhcp_sock); close(drv->dhcp_sock); + } os_free(drv); } @@ -414,14 +428,15 @@ static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); 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]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -438,7 +453,7 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -446,7 +461,8 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); ifr.ifr_flags = flags & 0xffff; if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOCSIFFLAGS]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); close(s); return -1; } @@ -463,14 +479,15 @@ static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } os_memset(&ifmr, 0, sizeof(ifmr)); os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - perror("ioctl[SIOCGIFMEDIA]"); + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", + strerror(errno)); close(s); return -1; } @@ -494,7 +511,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) s = socket(PF_INET, SOCK_DGRAM, 0); if (s < 0) { - perror("socket"); + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); return -1; } @@ -528,7 +545,8 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - perror("ioctl[SIOC{ADD/DEL}MULTI]"); + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); close(s); return -1; } @@ -551,7 +569,7 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) #ifdef __linux__ drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); if (drv->pf_sock < 0) - perror("socket(PF_PACKET)"); + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); #else /* __linux__ */ drv->pf_sock = -1; #endif /* __linux__ */ diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index d0e42ecb..f0c3bb3c 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -19,9 +19,6 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_MADWIFI -extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */ -#endif /* CONFIG_DRIVER_MADWIFI */ #ifdef CONFIG_DRIVER_BSD extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */ #endif /* CONFIG_DRIVER_BSD */ @@ -38,9 +35,6 @@ extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ /* driver_macsec_qca.c */ extern struct wpa_driver_ops wpa_driver_macsec_qca_ops; #endif /* CONFIG_DRIVER_MACSEC_QCA */ -#ifdef CONFIG_DRIVER_TEST -extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */ -#endif /* CONFIG_DRIVER_TEST */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; @@ -64,9 +58,6 @@ struct wpa_driver_ops *wpa_drivers[] = #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_MADWIFI - &wpa_driver_madwifi_ops, -#endif /* CONFIG_DRIVER_MADWIFI */ #ifdef CONFIG_DRIVER_BSD &wpa_driver_bsd_ops, #endif /* CONFIG_DRIVER_BSD */ @@ -82,9 +73,6 @@ struct wpa_driver_ops *wpa_drivers[] = #ifdef CONFIG_DRIVER_MACSEC_QCA &wpa_driver_macsec_qca_ops, #endif /* CONFIG_DRIVER_MACSEC_QCA */ -#ifdef CONFIG_DRIVER_TEST - &wpa_driver_test_ops, -#endif /* CONFIG_DRIVER_TEST */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index cdb913e3..ab392bca 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -25,6 +25,10 @@ endif ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += ../src/drivers/driver_nl80211.o +DRV_OBJS += ../src/drivers/driver_nl80211_capa.o +DRV_OBJS += ../src/drivers/driver_nl80211_event.o +DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o +DRV_OBJS += ../src/drivers/driver_nl80211_scan.o DRV_OBJS += ../src/utils/radiotap.o NEED_SME=y NEED_AP_MLME=y @@ -72,12 +76,6 @@ DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD DRV_OBJS += ../src/drivers/driver_openbsd.o endif -ifdef CONFIG_DRIVER_TEST -DRV_CFLAGS += -DCONFIG_DRIVER_TEST -DRV_OBJS += ../src/drivers/driver_test.o -NEED_AP_MLME=y -endif - ifdef CONFIG_DRIVER_NONE DRV_CFLAGS += -DCONFIG_DRIVER_NONE DRV_OBJS += ../src/drivers/driver_none.o @@ -94,15 +92,6 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_AP_OBJS += ../src/drivers/driver_madwifi.o -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += ../src/drivers/driver_atheros.o diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index 9fa70d9c..8da4c53e 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -20,6 +20,11 @@ endif ifdef CONFIG_DRIVER_NL80211 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 DRV_OBJS += src/drivers/driver_nl80211.c +DRV_OBJS += src/drivers/driver_nl80211_android.c +DRV_OBJS += src/drivers/driver_nl80211_capa.c +DRV_OBJS += src/drivers/driver_nl80211_event.c +DRV_OBJS += src/drivers/driver_nl80211_monitor.c +DRV_OBJS += src/drivers/driver_nl80211_scan.c DRV_OBJS += src/utils/radiotap.c NEED_SME=y NEED_AP_MLME=y @@ -67,12 +72,6 @@ DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD DRV_OBJS += src/drivers/driver_openbsd.c endif -ifdef CONFIG_DRIVER_TEST -DRV_CFLAGS += -DCONFIG_DRIVER_TEST -DRV_OBJS += src/drivers/driver_test.c -NEED_AP_MLME=y -endif - ifdef CONFIG_DRIVER_NONE DRV_CFLAGS += -DCONFIG_DRIVER_NONE DRV_OBJS += src/drivers/driver_none.c @@ -89,15 +88,6 @@ NEED_NETLINK=y NEED_LINUX_IOCTL=y endif -ifdef CONFIG_DRIVER_MADWIFI -DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI -DRV_AP_OBJS += src/drivers/driver_madwifi.c -CONFIG_WIRELESS_EXTENSION=y -CONFIG_L2_PACKET=linux -NEED_NETLINK=y -NEED_LINUX_IOCTL=y -endif - ifdef CONFIG_DRIVER_ATHEROS DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS DRV_AP_OBJS += src/drivers/driver_atheros.c diff --git a/src/drivers/linux_defines.h b/src/drivers/linux_defines.h new file mode 100644 index 00000000..a107479a --- /dev/null +++ b/src/drivers/linux_defines.h @@ -0,0 +1,46 @@ +/* + * Linux defines for values that are not yet included in common C libraries + * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef LINUX_DEFINES_H +#define LINUX_DEFINES_H + +#ifndef SO_WIFI_STATUS +# if defined(__sparc__) +# define SO_WIFI_STATUS 0x0025 +# elif defined(__parisc__) +# define SO_WIFI_STATUS 0x4022 +# else +# define SO_WIFI_STATUS 41 +# endif + +# define SCM_WIFI_STATUS SO_WIFI_STATUS +#endif + +#ifndef SO_EE_ORIGIN_TXSTATUS +#define SO_EE_ORIGIN_TXSTATUS 4 +#endif + +#ifndef PACKET_TX_TIMESTAMP +#define PACKET_TX_TIMESTAMP 16 +#endif + +#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 + +#endif /* LINUX_DEFINES_H */ diff --git a/src/drivers/linux_wext.h b/src/drivers/linux_wext.h index 55cf9553..e7c7001e 100644 --- a/src/drivers/linux_wext.h +++ b/src/drivers/linux_wext.h @@ -19,13 +19,13 @@ #define _LINUX_SOCKET_H #define _LINUX_IF_H -#include <sys/types.h> +#include <stdint.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; +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 */ diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c index 2fa20b1e..0e960f48 100644 --- a/src/drivers/netlink.c +++ b/src/drivers/netlink.c @@ -199,8 +199,7 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 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)); + req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); } if (operstate != -1) { rta = aliasing_hide_typecast( @@ -209,8 +208,7 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, 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)); + req.hdr.nlmsg_len += RTA_SPACE(sizeof(char)); } wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)", diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 4b28dc07..b37bd5a1 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -227,7 +227,11 @@ * 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. + * by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and + * %NL80211_ATTR_REASON_CODE can optionally be used to specify which type + * of disconnection indication should be sent to the station + * (Deauthentication or Disassociation frame and reason code for that + * frame). * * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to * destination %NL80211_ATTR_MAC on the interface identified by @@ -639,7 +643,18 @@ * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the - * attributes determining channel width. + * attributes determining channel width. This indication may also be + * sent when a remotely-initiated switch (e.g., when a STA receives a CSA + * from the remote AP) is completed; + * + * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch + * has been started on an interface, regardless of the initiator + * (ie. whether it was requested from a remote device or + * initiated on our own). It indicates that + * %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ + * after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may + * decide to react to this indication by requesting other + * interfaces to change channel as well. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with @@ -738,6 +753,27 @@ * before removing a station entry entirely, or before disassociating * or similar, cleanup will happen in the driver/device in this case. * + * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and + * bandwidth of a channel must be given. + * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the + * network is determined by the network interface. + * + * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer, + * identified by the %NL80211_ATTR_MAC parameter. A target channel is + * provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining + * channel width/type. The target operating class is given via + * %NL80211_ATTR_OPER_CLASS. + * The driver is responsible for continually initiating channel-switching + * operations and returning to the base channel for communication with the + * AP. + * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS + * peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel + * when this command completes. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -912,6 +948,16 @@ enum nl80211_commands { NL80211_CMD_ADD_TX_TS, NL80211_CMD_DEL_TX_TS, + NL80211_CMD_GET_MPP, + + NL80211_CMD_JOIN_OCB, + NL80211_CMD_LEAVE_OCB, + + NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, + + NL80211_CMD_TDLS_CHANNEL_SWITCH, + NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1606,9 +1652,9 @@ enum nl80211_commands { * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32. * As specified in the &enum nl80211_tdls_peer_capability. * - * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface + * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface * creation then the new interface will be owned by the netlink socket - * that created it and will be destroyed when the socket is closed + * that created it and will be destroyed when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. @@ -1638,6 +1684,11 @@ enum nl80211_commands { * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see * &enum nl80211_smps_mode. * + * @NL80211_ATTR_OPER_CLASS: operating class + * + * @NL80211_ATTR_MAC_MASK: MAC address mask + * + * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1973,7 +2024,7 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_PEER_CAPABILITY, - NL80211_ATTR_IFACE_SOCKET_OWNER, + NL80211_ATTR_SOCKET_OWNER, NL80211_ATTR_CSA_C_OFFSETS_TX, NL80211_ATTR_MAX_CSA_COUNTERS, @@ -1990,15 +2041,21 @@ enum nl80211_attrs { NL80211_ATTR_SMPS_MODE, + NL80211_ATTR_OPER_CLASS, + + NL80211_ATTR_MAC_MASK, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, + NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST, NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 }; /* source-level API compatibility */ #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG +#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -2064,6 +2121,8 @@ enum nl80211_attrs { * and therefore can't be created in the normal ways, use the * %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE * commands to create and destroy one + * @NL80211_IF_TYPE_OCB: Outside Context of a BSS + * This mode corresponds to the MIB variable dot11OCBActivated=true * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -2083,6 +2142,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_CLIENT, NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, + NL80211_IFTYPE_OCB, /* keep last */ NUM_NL80211_IFTYPES, @@ -2631,6 +2691,11 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated * base on contiguous rules and wider channels will be allowed to cross * multiple contiguous/overlapping frequency ranges. + * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT + * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation + * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation + * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed + * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -2643,11 +2708,18 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_IR = 1<<7, __NL80211_RRF_NO_IBSS = 1<<8, NL80211_RRF_AUTO_BW = 1<<11, + NL80211_RRF_GO_CONCURRENT = 1<<12, + NL80211_RRF_NO_HT40MINUS = 1<<13, + NL80211_RRF_NO_HT40PLUS = 1<<14, + NL80211_RRF_NO_80MHZ = 1<<15, + NL80211_RRF_NO_160MHZ = 1<<16, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR #define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR #define NL80211_RRF_NO_IR NL80211_RRF_NO_IR +#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\ + NL80211_RRF_NO_HT40PLUS) /* For backport compatibility with older userspace */ #define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS) @@ -3379,6 +3451,8 @@ enum nl80211_ps_state { * interval in which %NL80211_ATTR_CQM_TXE_PKTS and * %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. + * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon + * loss event * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -3391,6 +3465,7 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_TXE_RATE, NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, + NL80211_ATTR_CQM_BEACON_LOSS_EVENT, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -3403,9 +3478,7 @@ enum nl80211_attr_cqm { * configured threshold * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the * configured threshold - * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss. - * (Note that deauth/disassoc will still follow if the AP is not - * available. This event might get used as roaming event, etc.) + * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent) */ enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, @@ -3545,6 +3618,25 @@ struct nl80211_pattern_support { * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only, * the TCP connection ran out of tokens to use for data to send to the * service + * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network + * is detected. This is a nested attribute that contains the + * same attributes used with @NL80211_CMD_START_SCHED_SCAN. It + * specifies how the scan is performed (e.g. the interval and the + * channels to scan) as well as the scan results that will + * trigger a wake (i.e. the matchsets). + * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute + * containing an array with information about what triggered the + * wake up. If no elements are present in the array, it means + * that the information is not available. If more than one + * element is present, it means that more than one match + * occurred. + * Each element in the array is a nested attribute that contains + * one optional %NL80211_ATTR_SSID attribute and one optional + * %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of + * these attributes must be present. If + * %NL80211_ATTR_SCAN_FREQUENCIES contains more than one + * frequency, it means that the match occurred in more than one + * channel. * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number * @@ -3570,6 +3662,8 @@ enum nl80211_wowlan_triggers { NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST, NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS, + NL80211_WOWLAN_TRIG_NET_DETECT, + NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS, /* keep last */ NUM_NL80211_WOWLAN_TRIG, @@ -4042,6 +4136,27 @@ enum nl80211_ap_sme_features { * multiplexing powersave, ie. can turn off all but one chain * and then wake the rest up as required after, for example, * rts/cts handshake. + * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM + * TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS + * command. Standard IEEE 802.11 TSPEC setup is not yet supported, it + * needs to be able to handle Block-Ack agreements and other things. + * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring + * the vif's MAC address upon creation. + * See 'macaddr' field in the vif_params (cfg80211.h). + * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when + * operating as a TDLS peer. + * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address during scan (if the device is unassociated); the + * %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC + * address mask/value will be used. + * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports + * using a random MAC address for every scan iteration during scheduled + * scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. + * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a + * random MAC address for every scan iteration during "net detect", i.e. + * scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may + * be set for scheduled scan and the MAC address mask/value will be used. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -4070,6 +4185,12 @@ enum nl80211_feature_flags { NL80211_FEATURE_ACKTO_ESTIMATION = 1 << 23, NL80211_FEATURE_STATIC_SMPS = 1 << 24, NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25, + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26, + NL80211_FEATURE_MAC_ON_CREATE = 1 << 27, + NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28, + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29, + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30, + NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31, }; /** @@ -4118,11 +4239,21 @@ enum nl80211_connect_failed_reason { * dangerous because will destroy stations performance as a lot of frames * will be lost while scanning off-channel, therefore it must be used only * when really needed + * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or + * for scheduled scan: a different one for every scan iteration). When the + * flag is set, depending on device capabilities the @NL80211_ATTR_MAC and + * @NL80211_ATTR_MAC_MASK attributes may also be given in which case only + * the masked bits will be preserved from the MAC address and the remainder + * randomised. If the attributes are not given full randomisation (46 bits, + * locally administered 1, multicast 0) is assumed. + * This flag must not be requested when the feature isn't supported, check + * the nl80211 feature flags for the device. */ enum nl80211_scan_flags { NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, NL80211_SCAN_FLAG_FLUSH = 1<<1, NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, }; /** diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h index 62320880..d3f091c3 100644 --- a/src/drivers/priv_netlink.h +++ b/src/drivers/priv_netlink.h @@ -68,6 +68,7 @@ ((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_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) #define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0)) diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c index 7b077cb9..1de13281 100644 --- a/src/eap_common/eap_common.c +++ b/src/eap_common/eap_common.c @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -203,3 +203,86 @@ EapType eap_get_type(const struct wpabuf *msg) return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)]; } + + +#ifdef CONFIG_ERP +int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs, + int stop_at_keyname) +{ + os_memset(tlvs, 0, sizeof(*tlvs)); + + while (pos < end) { + u8 tlv_type, tlv_len; + + tlv_type = *pos++; + switch (tlv_type) { + case EAP_ERP_TV_RRK_LIFETIME: + case EAP_ERP_TV_RMSK_LIFETIME: + /* 4-octet TV */ + if (pos + 4 > end) { + wpa_printf(MSG_DEBUG, "EAP: Too short TV"); + return -1; + } + pos += 4; + break; + case EAP_ERP_TLV_DOMAIN_NAME: + case EAP_ERP_TLV_KEYNAME_NAI: + case EAP_ERP_TLV_CRYPTOSUITES: + case EAP_ERP_TLV_AUTHORIZATION_INDICATION: + case EAP_ERP_TLV_CALLED_STATION_ID: + case EAP_ERP_TLV_CALLING_STATION_ID: + case EAP_ERP_TLV_NAS_IDENTIFIER: + case EAP_ERP_TLV_NAS_IP_ADDRESS: + case EAP_ERP_TLV_NAS_IPV6_ADDRESS: + if (pos >= end) { + wpa_printf(MSG_DEBUG, "EAP: Too short TLV"); + return -1; + } + tlv_len = *pos++; + if (tlv_len > (unsigned) (end - pos)) { + wpa_printf(MSG_DEBUG, "EAP: Truncated TLV"); + return -1; + } + if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) { + if (tlvs->keyname) { + wpa_printf(MSG_DEBUG, + "EAP: More than one keyName-NAI"); + return -1; + } + tlvs->keyname = pos; + tlvs->keyname_len = tlv_len; + if (stop_at_keyname) + return 0; + } else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) { + tlvs->domain = pos; + tlvs->domain_len = tlv_len; + } + pos += tlv_len; + break; + default: + if (tlv_type >= 128 && tlv_type <= 191) { + /* Undefined TLV */ + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "EAP: Too short TLV"); + return -1; + } + tlv_len = *pos++; + if (tlv_len > (unsigned) (end - pos)) { + wpa_printf(MSG_DEBUG, + "EAP: Truncated TLV"); + return -1; + } + pos += tlv_len; + break; + } + wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u", + tlv_type); + pos = end; + break; + } + } + + return 0; +} +#endif /* CONFIG_ERP */ diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h index 8850c1fe..e62f1676 100644 --- a/src/eap_common/eap_common.h +++ b/src/eap_common/eap_common.h @@ -1,6 +1,6 @@ /* * EAP common peer/server definitions - * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,14 @@ #include "wpabuf.h" +struct erp_tlvs { + const u8 *keyname; + const u8 *domain; + + u8 keyname_len; + u8 domain_len; +}; + int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload); const u8 * eap_hdr_validate(int vendor, EapType eap_type, const struct wpabuf *msg, size_t *plen); @@ -19,5 +27,7 @@ struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len, void eap_update_len(struct wpabuf *msg); u8 eap_get_id(const struct wpabuf *msg); EapType eap_get_type(const struct wpabuf *msg); +int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs, + int stop_at_keyname); #endif /* EAP_COMMON_H */ diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h index 4f14a01e..54f26ca3 100644 --- a/src/eap_common/eap_defs.h +++ b/src/eap_common/eap_defs.h @@ -1,6 +1,6 @@ /* * EAP server/peer: Shared EAP definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,11 +27,39 @@ struct eap_hdr { #endif /* _MSC_VER */ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, - EAP_CODE_FAILURE = 4 }; + EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 }; /* EAP Request and Response data begins with one octet Type. Success and * Failure do not have additional data. */ +/* Type field in EAP-Initiate and EAP-Finish messages */ +enum eap_erp_type { + EAP_ERP_TYPE_REAUTH_START = 1, + EAP_ERP_TYPE_REAUTH = 2, +}; + +/* ERP TV/TLV types */ +enum eap_erp_tlv_type { + EAP_ERP_TLV_KEYNAME_NAI = 1, + EAP_ERP_TV_RRK_LIFETIME = 2, + EAP_ERP_TV_RMSK_LIFETIME = 3, + EAP_ERP_TLV_DOMAIN_NAME = 4, + EAP_ERP_TLV_CRYPTOSUITES = 5, + EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6, + EAP_ERP_TLV_CALLED_STATION_ID = 128, + EAP_ERP_TLV_CALLING_STATION_ID = 129, + EAP_ERP_TLV_NAS_IDENTIFIER = 130, + EAP_ERP_TLV_NAS_IP_ADDRESS = 131, + EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132, +}; + +/* ERP Cryptosuite */ +enum eap_erp_cryptosuite { + EAP_ERP_CS_HMAC_SHA256_64 = 1, + EAP_ERP_CS_HMAC_SHA256_128 = 2, + EAP_ERP_CS_HMAC_SHA256_256 = 3, +}; + /* * EAP Method Types as allocated by IANA: * http://www.iana.org/assignments/eap-numbers @@ -84,5 +112,7 @@ enum { #define EAP_MSK_LEN 64 #define EAP_EMSK_LEN 64 +#define EAP_EMSK_NAME_LEN 8 +#define ERP_MAX_KEY_LEN 64 #endif /* EAP_DEFS_H */ diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c index b3bbacc6..0e80ef51 100644 --- a/src/eap_common/eap_pax_common.c +++ b/src/eap_common/eap_pax_common.c @@ -121,10 +121,11 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, * @mk: Buffer for the derived Master Key * @ck: Buffer for the derived Confirmation Key * @ick: Buffer for the derived Integrity Check Key + * @mid: Buffer for the derived Method ID * Returns: 0 on success, -1 on failure */ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick) + u8 *mk, u8 *ck, u8 *ick, u8 *mid) { wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", @@ -132,13 +133,16 @@ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", - e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Method ID", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MID_LEN, mid)) return -1; wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MID", mid, EAP_PAX_MID_LEN); return 0; } diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h index fb03df25..e6cdf4df 100644 --- a/src/eap_common/eap_pax_common.h +++ b/src/eap_common/eap_pax_common.h @@ -74,6 +74,7 @@ enum { #define EAP_PAX_MK_LEN 16 #define EAP_PAX_CK_LEN 16 #define EAP_PAX_ICK_LEN 16 +#define EAP_PAX_MID_LEN 16 int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, @@ -86,6 +87,6 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, const u8 *data3, size_t data3_len, u8 *mac); int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, - u8 *mk, u8 *ck, u8 *ick); + u8 *mk, u8 *ck, u8 *ick, u8 *mid); #endif /* EAP_PAX_COMMON_H */ diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c index 3d4fb6f9..4f9e64ec 100644 --- a/src/eap_common/ikev2_common.c +++ b/src/eap_common/ikev2_common.c @@ -251,25 +251,29 @@ int ikev2_parse_payloads(struct ikev2_payloads *payloads, os_memset(payloads, 0, sizeof(*payloads)); while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) { - int plen, pdatalen; + unsigned int plen, pdatalen, left; const u8 *pdata; wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u", next_payload); - if (end - pos < (int) sizeof(*phdr)) { + if (end < pos) + return -1; + left = end - pos; + if (left < sizeof(*phdr)) { wpa_printf(MSG_INFO, "IKEV2: Too short message for " "payload header (left=%ld)", (long) (end - pos)); + return -1; } phdr = (const struct ikev2_payload_hdr *) pos; plen = WPA_GET_BE16(phdr->payload_length); - if (plen < (int) sizeof(*phdr) || pos + plen > end) { + if (plen < sizeof(*phdr) || plen > left) { wpa_printf(MSG_INFO, "IKEV2: Invalid payload header " "length %d", plen); return -1; } wpa_printf(MSG_DEBUG, "IKEV2: Next Payload: %u Flags: 0x%x" - " Payload Length: %d", + " Payload Length: %u", phdr->next_payload, phdr->flags, plen); pdata = (const u8 *) (phdr + 1); diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 9880d3bc..31c1a29c 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -23,6 +23,7 @@ #include "ext_password.h" #include "crypto/crypto.h" #include "crypto/tls.h" +#include "crypto/sha256.h" #include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" #include "eap_i.h" @@ -190,6 +191,8 @@ SM_STATE(EAP, INITIALIZE) sm->num_rounds = 0; sm->prev_failure = 0; sm->expected_failure = 0; + sm->reauthInit = FALSE; + sm->erp_seq = (u32) -1; } @@ -353,6 +356,267 @@ nak: } +#ifdef CONFIG_ERP + +static char * eap_home_realm(struct eap_sm *sm) +{ + struct eap_peer_config *config = eap_get_config(sm); + char *realm; + size_t i, realm_len; + + if (!config) + return NULL; + + if (config->identity) { + for (i = 0; i < config->identity_len; i++) { + if (config->identity[i] == '@') + break; + } + if (i < config->identity_len) { + realm_len = config->identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->identity[i + 1], realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + if (config->anonymous_identity) { + for (i = 0; i < config->anonymous_identity_len; i++) { + if (config->anonymous_identity[i] == '@') + break; + } + if (i < config->anonymous_identity_len) { + realm_len = config->anonymous_identity_len - i - 1; + realm = os_malloc(realm_len + 1); + if (realm == NULL) + return NULL; + os_memcpy(realm, &config->anonymous_identity[i + 1], + realm_len); + realm[realm_len] = '\0'; + return realm; + } + } + + return os_strdup(""); +} + + +static struct eap_erp_key * +eap_erp_get_key(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + char *pos; + + pos = os_strchr(erp->keyname_nai, '@'); + if (!pos) + continue; + pos++; + if (os_strcmp(pos, realm) == 0) + return erp; + } + + return NULL; +} + + +static struct eap_erp_key * +eap_erp_get_key_nai(struct eap_sm *sm, const char *nai) +{ + struct eap_erp_key *erp; + + dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) { + if (os_strcmp(erp->keyname_nai, nai) == 0) + return erp; + } + + return NULL; +} + + +static void eap_peer_erp_free_key(struct eap_erp_key *erp) +{ + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); +} + + +static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm) +{ + struct eap_erp_key *erp; + + while ((erp = eap_erp_get_key(sm, realm)) != NULL) { + wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s", + erp->keyname_nai); + eap_peer_erp_free_key(erp); + } +} + +#endif /* CONFIG_ERP */ + + +void eap_peer_erp_free_keys(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + struct eap_erp_key *erp, *tmp; + + dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list) + eap_peer_erp_free_key(erp); +#endif /* CONFIG_ERP */ +} + + +static void eap_peer_erp_init(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + u8 *emsk = NULL; + size_t emsk_len = 0; + u8 EMSKname[EAP_EMSK_NAME_LEN]; + u8 len[2]; + char *realm; + size_t realm_len, nai_buf_len; + struct eap_erp_key *erp = NULL; + int pos; + + realm = eap_home_realm(sm); + if (!realm) + return; + realm_len = os_strlen(realm); + wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm); + eap_erp_remove_keys_realm(sm, realm); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len; + if (nai_buf_len > 253) { + /* + * keyName-NAI has a maximum length of 253 octet to fit in + * RADIUS attributes. + */ + wpa_printf(MSG_DEBUG, + "EAP: Too long realm for ERP keyName-NAI maximum length"); + goto fail; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable EMSK available for ERP"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); + + WPA_PUT_BE16(len, 8); + if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK", + len, sizeof(len), + EMSKname, EAP_EMSK_NAME_LEN) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); + + pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, + EMSKname, EAP_EMSK_NAME_LEN); + erp->keyname_nai[pos] = '@'; + os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len); + + WPA_PUT_BE16(len, emsk_len); + if (hmac_sha256_kdf(emsk, emsk_len, + "EAP Re-authentication Root Key@ietf.org", + len, sizeof(len), erp->rRK, emsk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); + goto fail; + } + erp->rRK_len = emsk_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "EAP Re-authentication Integrity Key@ietf.org", + len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); + goto fail; + } + erp->rIK_len = erp->rRK_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); + + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai); + dl_list_add(&sm->erp_keys, &erp->list); + erp = NULL; +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); + os_free(realm); +#endif /* CONFIG_ERP */ +} + + +#ifdef CONFIG_ERP +static int eap_peer_erp_reauth_start(struct eap_sm *sm, + const struct eap_hdr *hdr, size_t len) +{ + char *realm; + struct eap_erp_key *erp; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + + realm = eap_home_realm(sm); + if (!realm) + return -1; + + erp = eap_erp_get_key(sm, realm); + os_free(realm); + realm = NULL; + if (!erp) + return -1; + + if (erp->next_seq >= 65536) + return -1; /* SEQ has range of 0..65535 */ + + /* TODO: check rRK lifetime expiration */ + + wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)", + erp->keyname_nai, erp->next_seq); + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, + EAP_CODE_INITIATE, hdr->identifier); + if (msg == NULL) + return -1; + + wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */ + wpabuf_put_be16(msg, erp->next_seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(erp->keyname_nai)); + wpabuf_put_str(msg, erp->keyname_nai); + + wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */ + + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_data(msg, hash, 16); + + wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); + sm->erp_seq = erp->next_seq; + erp->next_seq++; + wpabuf_free(sm->eapRespData); + sm->eapRespData = msg; + sm->reauthInit = TRUE; + return 0; +} +#endif /* CONFIG_ERP */ + + /* * The method processing happens here. The request from the authenticator is * processed, and an appropriate response packet is built. @@ -414,6 +678,8 @@ SM_STATE(EAP, METHOD) if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { + struct eap_peer_config *config = eap_get_config(sm); + eap_sm_free_key(sm); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); @@ -426,6 +692,8 @@ SM_STATE(EAP, METHOD) wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", sm->eapSessionId, sm->eapSessionIdLen); } + if (config->erp && sm->m->get_emsk && sm->eapSessionId) + eap_peer_erp_init(sm); } } @@ -450,6 +718,7 @@ SM_STATE(EAP, SEND_RESPONSE) } eapol_set_bool(sm, EAPOL_eapReq, FALSE); eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout); + sm->reauthInit = FALSE; } @@ -709,6 +978,8 @@ static void eap_peer_sm_step_received(struct eap_sm *sm) else if (sm->selectedMethod == EAP_TYPE_LEAP && (sm->rxSuccess || sm->rxResp)) SM_ENTER(EAP, METHOD); + else if (sm->reauthInit) + SM_ENTER(EAP, SEND_RESPONSE); else SM_ENTER(EAP, DISCARD); } @@ -1231,6 +1502,219 @@ static struct wpabuf * eap_sm_buildNotify(int id) } +static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH_START) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Initiate Type=%u", + *pos); + return; + } + + pos++; + if (pos >= end) { + wpa_printf(MSG_DEBUG, + "EAP: Too short EAP-Initiate/Re-auth-Start"); + return; + } + pos++; /* Reserved */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs", + pos, end - pos); + + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + goto invalid; + + if (parse.domain) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - Domain name", + parse.domain, parse.domain_len); + /* TODO: Derivation of domain specific keys for local ER */ + } + + if (eap_peer_erp_reauth_start(sm, hdr, len) == 0) + return; + +invalid: +#endif /* CONFIG_ERP */ + wpa_printf(MSG_DEBUG, + "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication"); + eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE); +} + + +static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, + size_t len) +{ +#ifdef CONFIG_ERP + const u8 *pos = (const u8 *) (hdr + 1); + const u8 *end = ((const u8 *) hdr) + len; + const u8 *start; + struct erp_tlvs parse; + u8 flags; + u16 seq; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct eap_erp_key *erp; + int max_len; + char nai[254]; + u8 seed[4]; + int auth_tag_ok = 0; + + if (len < sizeof(*hdr) + 1) { + wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish"); + return; + } + + if (*pos != EAP_ERP_TYPE_REAUTH) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored unexpected EAP-Finish Type=%u", *pos); + return; + } + + if (len < sizeof(*hdr) + 4) { + wpa_printf(MSG_DEBUG, + "EAP: Ignored too short EAP-Finish/Re-auth"); + return; + } + + pos++; + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + + if (seq != sm->erp_seq) { + wpa_printf(MSG_DEBUG, + "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq); + return; + } + + /* + * Parse TVs/TLVs. Since we do not yet know the length of the + * Authentication Tag, stop parsing if an unknown TV/TLV is seen and + * just try to find the keyName-NAI first so that we can check the + * Authentication Tag. + */ + if (erp_parse_tlvs(pos, end, &parse, 1) < 0) + return; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet"); + return; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI", + parse.keyname, parse.keyname_len); + if (parse.keyname_len > 253) { + wpa_printf(MSG_DEBUG, + "EAP: Too long keyName-NAI in EAP-Finish/Re-auth"); + return; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + erp = eap_erp_get_key_nai(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + return; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + hash_len = 16; + if (max_len < 1 + (int) hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) { + wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used"); + if (flags & 0x80) + goto no_auth_tag; + return; + } + + if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr, + end - ((const u8 *) hdr) - hash_len, hash) < 0) + return; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + return; + } + auth_tag_ok = 1; + end -= 1 + hash_len; + +no_auth_tag: + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs", + pos, end - pos); + if (erp_parse_tlvs(pos, end, &parse, 0) < 0) + return; + + if (flags & 0x80 || !auth_tag_ok) { + wpa_printf(MSG_DEBUG, + "EAP: EAP-Finish/Re-auth indicated failure"); + eapol_set_bool(sm, EAPOL_eapFail, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + "EAP authentication failed"); + sm->prev_failure = 1; + wpa_printf(MSG_DEBUG, + "EAP: Drop ERP key to try full authentication on next attempt"); + eap_peer_erp_free_key(erp); + return; + } + + eap_sm_free_key(sm); + sm->eapKeyDataLen = 0; + sm->eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eapKeyData) + return; + sm->eapKeyDataLen = erp->rRK_len; + + WPA_PUT_BE16(seed, seq); + WPA_PUT_BE16(&seed[2], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Master Session Key@ietf.org", + seed, sizeof(seed), + sm->eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + eap_sm_free_key(sm); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eapKeyData, sm->eapKeyDataLen); + sm->eapKeyAvailable = TRUE; + eapol_set_bool(sm, EAPOL_eapSuccess, TRUE); + eapol_set_bool(sm, EAPOL_eapReq, FALSE); + eapol_set_bool(sm, EAPOL_eapNoResp, TRUE); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + "EAP re-authentication completed successfully"); +#endif /* CONFIG_ERP */ +} + + static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) { const struct eap_hdr *hdr; @@ -1322,6 +1806,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) eap_notify_status(sm, "completion", "failure"); sm->rxFailure = TRUE; break; + case EAP_CODE_INITIATE: + eap_peer_initiate(sm, hdr, plen); + break; + case EAP_CODE_FINISH: + eap_peer_finish(sm, hdr, plen); + break; default: wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown " "code %d", hdr->code); @@ -1413,11 +1903,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, sm->msg_ctx = msg_ctx; sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT; sm->wps = conf->wps; + dl_list_init(&sm->erp_keys); os_memset(&tlsconf, 0, sizeof(tlsconf)); tlsconf.opensc_engine_path = conf->opensc_engine_path; tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; tlsconf.pkcs11_module_path = conf->pkcs11_module_path; + tlsconf.openssl_ciphers = conf->openssl_ciphers; #ifdef CONFIG_FIPS tlsconf.fips_mode = 1; #endif /* CONFIG_FIPS */ @@ -1459,6 +1951,7 @@ void eap_peer_sm_deinit(struct eap_sm *sm) if (sm->ssl_ctx2) tls_deinit(sm->ssl_ctx2); tls_deinit(sm->ssl_ctx); + eap_peer_erp_free_keys(sm); os_free(sm); } @@ -1607,7 +2100,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) len = os_snprintf(buf, buflen, "EAP state=%s\n", eap_sm_state_txt(sm->EAP_state)); - if (len < 0 || (size_t) len >= buflen) + if (os_snprintf_error(buflen, len)) return 0; if (sm->selectedMethod != EAP_TYPE_NONE) { @@ -1626,7 +2119,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) ret = os_snprintf(buf + len, buflen - len, "selectedMethod=%d (EAP-%s)\n", sm->selectedMethod, name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1647,7 +2140,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose) eap_sm_method_state_txt(sm->methodState), eap_sm_decision_txt(sm->decision), sm->ClientTimeout); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 712e929d..bc207e74 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -94,7 +94,14 @@ enum eapol_bool_var { * * EAP state machines reads this value. */ - EAPOL_altReject + EAPOL_altReject, + + /** + * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start + * + * EAP state machine writes this value. + */ + EAPOL_eapTriggerStart }; /** @@ -268,6 +275,14 @@ struct eap_config { */ const char *pkcs11_module_path; /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + const char *openssl_ciphers; + /** * wps - WPS context data * * This is only used by EAP-WSC and can be left %NULL if not available. @@ -321,6 +336,7 @@ struct ext_password_data; void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); int eap_peer_was_failure_expected(struct eap_sm *sm); +void eap_peer_erp_free_keys(struct eap_sm *sm); #endif /* IEEE8021X_EAPOL */ diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index 2591e113..3584bdbc 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -686,6 +686,20 @@ struct eap_peer_config { * has more than one. */ int sim_num; + + /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * ciphers for this connection. If not set, the default cipher suite + * list is used. + */ + char *openssl_ciphers; + + /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + */ + int erp; }; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 0739187c..68d7fba8 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -1666,7 +1666,7 @@ static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf, ret = os_snprintf(buf + len, buflen - len, "EAP-FAST Phase2 method=%s\n", data->phase2_method->name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index 21d60983..89e604ec 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -504,28 +504,28 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, end = *buf + *buf_len; ret = os_snprintf(*pos, end - *pos, "%s=", field); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); ret = os_snprintf(*pos, end - *pos, "\n"); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; if (txt) { ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; for (i = 0; i < len; i++) { ret = os_snprintf(*pos, end - *pos, "%c", data[i]); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; } ret = os_snprintf(*pos, end - *pos, "\n"); - if (ret < 0 || ret >= end - *pos) + if (os_snprintf_error(end - *pos, ret)) return; *pos += ret; } @@ -578,7 +578,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, ret = os_snprintf(*pos, *buf + *buf_len - *pos, "START\nPAC-Type=%d\n", pac->pac_type); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) return -1; *pos += ret; @@ -600,7 +600,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf, return -1; } ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + if (os_snprintf_error(*buf + *buf_len - *pos, ret)) return -1; *pos += ret; @@ -632,7 +632,7 @@ int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root, return -1; ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr); - if (ret < 0 || ret >= buf + buf_len - pos) { + if (os_snprintf_error(buf + buf_len - pos, ret)) { os_free(buf); return -1; } @@ -714,7 +714,7 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) pos += 2; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > (unsigned int) (end - pos)) break; if (type == PAC_TYPE_A_ID) { @@ -799,7 +799,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, pos = buf + 6; end = buf + len; while (pos < end) { - if (end - pos < 2 + 32 + 2 + 2) + u16 val; + + if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) goto parse_fail; pac = os_zalloc(sizeof(*pac)); @@ -810,19 +812,23 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, pos += 2; os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN); pos += EAP_FAST_PAC_KEY_LEN; - pac->pac_opaque_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (pos + pac->pac_opaque_len + 2 > end) + if (val > end - pos) goto parse_fail; + pac->pac_opaque_len = val; pac->pac_opaque = os_malloc(pac->pac_opaque_len); if (pac->pac_opaque == NULL) goto parse_fail; os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); pos += pac->pac_opaque_len; - pac->pac_info_len = WPA_GET_BE16(pos); + if (2 > end - pos) + goto parse_fail; + val = WPA_GET_BE16(pos); pos += 2; - if (pos + pac->pac_info_len > end) + if (val > end - pos) goto parse_fail; + pac->pac_info_len = val; pac->pac_info = os_malloc(pac->pac_info_len); if (pac->pac_info == NULL) goto parse_fail; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index fde809c3..2d7fdea2 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -1,6 +1,6 @@ /* * EAP peer state machines internal structures (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define EAP_I_H #include "wpabuf.h" +#include "utils/list.h" #include "eap_peer/eap.h" #include "eap_common/eap_common.h" @@ -277,6 +278,16 @@ struct eap_method { }; +struct eap_erp_key { + struct dl_list list; + size_t rRK_len; + size_t rIK_len; + u8 rRK[ERP_MAX_KEY_LEN]; + u8 rIK[ERP_MAX_KEY_LEN]; + u32 next_seq; + char keyname_nai[]; +}; + /** * struct eap_sm - EAP state machine data */ @@ -321,6 +332,8 @@ struct eap_sm { void *eap_method_priv; int init_phase2; int fast_reauth; + Boolean reauthInit; /* send EAP-Identity/Re-auth */ + u32 erp_seq; Boolean rxResp /* LEAP only */; Boolean leap_done; @@ -353,6 +366,8 @@ struct eap_sm { int external_sim; unsigned int expected_failure:1; + + struct dl_list erp_keys; /* struct eap_erp_key */ }; const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len); diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index c12b5193..b5ef71ba 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -301,6 +301,13 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, if (data->in_buf == NULL) { /* First fragment of the message */ + if (message_length > 50000) { + /* Limit maximum memory allocation */ + wpa_printf(MSG_DEBUG, + "EAP-IKEV2: Ignore too long message"); + ret->ignore = TRUE; + return NULL; + } data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " @@ -315,6 +322,7 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data, (unsigned long) wpabuf_tailroom(data->in_buf)); } + ret->ignore = FALSE; return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE); } diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c index 83a14579..1bdd81e1 100644 --- a/src/eap_peer/eap_methods.c +++ b/src/eap_peer/eap_methods.c @@ -103,7 +103,7 @@ size_t eap_get_names(char *buf, size_t buflen) for (m = eap_methods; m; m = m->next) { ret = os_snprintf(pos, end - pos, "%s%s", m == eap_methods ? "" : " ", m->name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -133,7 +133,7 @@ char ** eap_get_names_as_string_array(size_t *num) for (m = eap_methods; m; m = m->next) array_len++; - array = os_zalloc(sizeof(char *) * (array_len + 1)); + array = os_calloc(array_len + 1, sizeof(char *)); if (array == NULL) return NULL; diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index 1c111c28..6d1ff208 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -38,6 +38,7 @@ struct eap_pax_data { u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; + u8 mid[EAP_PAX_MID_LEN]; }; @@ -178,8 +179,8 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, data->rand.r.y, EAP_PAX_RAND_LEN); if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, - data->mk, data->ck, data->ick) < 0) - { + data->mk, data->ck, data->ick, + data->mid) < 0) { ret->ignore = TRUE; return NULL; } @@ -501,6 +502,26 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *sid; + + if (data->state != PAX_DONE) + return NULL; + + sid = os_malloc(1 + EAP_PAX_MID_LEN); + if (sid == NULL) + return NULL; + + *len = 1 + EAP_PAX_MID_LEN; + sid[0] = EAP_TYPE_PAX; + os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); + + return sid; +} + + int eap_peer_pax_register(void) { struct eap_method *eap; @@ -517,6 +538,7 @@ int eap_peer_pax_register(void) eap->isKeyAvailable = eap_pax_isKeyAvailable; eap->getKey = eap_pax_getKey; eap->get_emsk = eap_pax_get_emsk; + eap->getSessionId = eap_pax_get_session_id; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 472e861b..86a18bb8 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -1156,7 +1156,7 @@ static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf, "EAP-PEAPv%d Phase2 method=%s\n", data->peap_version, data->phase2_method->name); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index 1c915ed4..059bbeec 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -952,7 +952,6 @@ int eap_peer_pwd_register(void) struct eap_method *eap; int ret; - EVP_add_digest(EVP_sha256()); eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD"); if (eap == NULL) diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index fe9bfe00..3641a2c8 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -147,6 +147,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, } else { wpa_printf(MSG_DEBUG, "TLS: using phase1 config options"); eap_tls_params_from_conf1(params, config); + if (data->eap_type == EAP_TYPE_FAST) + params->flags |= TLS_CONN_EAP_FAST; } /* @@ -167,6 +169,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, return -1; } + params->openssl_ciphers = config->openssl_ciphers; + return 0; } @@ -377,15 +381,10 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_keys keys; u8 *out; - /* - * TLS library did not support session ID generation, - * so get the needed TLS session parameters - */ if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) return NULL; - if (keys.client_random == NULL || keys.server_random == NULL || - keys.master_key == NULL) + if (keys.client_random == NULL || keys.server_random == NULL) return NULL; *len = 1 + keys.client_random_len + keys.server_random_len; @@ -397,7 +396,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, out[0] = eap_type; os_memcpy(out + 1, keys.client_random, keys.client_random_len); os_memcpy(out + 1 + keys.client_random_len, keys.server_random, - keys.server_random_len); + keys.server_random_len); return out; } @@ -795,8 +794,11 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data, if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0) { ret = os_snprintf(buf + len, buflen - len, - "EAP TLS cipher=%s\n", name); - if (ret < 0 || (size_t) ret >= buflen - len) + "EAP TLS cipher=%s\n" + "tls_session_reused=%d\n", + name, tls_connection_resumed(data->ssl_ctx, + data->conn)); + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 771da584..6fbc27b7 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -136,7 +136,7 @@ static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm, static void eap_ttls_free_key(struct eap_ttls_data *data) { if (data->key_data) { - bin_clear_free(data->key_data, EAP_TLS_KEY_LEN); + bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); data->key_data = NULL; } } @@ -225,7 +225,8 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, eap_ttls_free_key(data); data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, "ttls keying material", - EAP_TLS_KEY_LEN); + EAP_TLS_KEY_LEN + + EAP_EMSK_LEN); if (!data->key_data) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key"); return -1; @@ -233,6 +234,9 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", data->key_data, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK", + data->key_data + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); os_free(data->session_id); data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl, @@ -1567,7 +1571,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, ret = os_snprintf(buf + len, buflen - len, "EAP-TTLSv%d Phase2 method=", data->ttls_version); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; switch (data->phase2_type) { @@ -1592,7 +1596,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf, ret = 0; break; } - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1645,6 +1649,25 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *key; + + if (data->key_data == NULL) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_EMSK_LEN; + os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); + + return key; +} + + int eap_peer_ttls_register(void) { struct eap_method *eap; @@ -1665,6 +1688,7 @@ int eap_peer_ttls_register(void) eap->has_reauth_data = eap_ttls_has_reauth_data; eap->deinit_for_reauth = eap_ttls_deinit_for_reauth; eap->init_for_reauth = eap_ttls_init_for_reauth; + eap->get_emsk = eap_ttls_get_emsk; ret = eap_peer_method_register(eap); if (ret) diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 23e98237..7ce0a53d 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -462,7 +462,7 @@ static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE16(pos); pos += 2; - if (message_length < end - pos) { + if (message_length < end - pos || message_length > 50000) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); ret->ignore = TRUE; diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index 8186afb5..55ab72ae 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -213,7 +213,7 @@ static int ikev2_parse_proposal(struct ikev2_proposal_data *prop, p = (const struct ikev2_proposal *) pos; proposal_len = WPA_GET_BE16(p->proposal_length); - if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) { + if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) { wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d", proposal_len); return -1; @@ -369,7 +369,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data, } if (kei_len < 4 + 96) { - wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload"); + wpa_printf(MSG_INFO, "IKEV2: Too short Key Exchange Payload"); return -1; } diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 1253bd6e..9de6cb62 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -1,6 +1,6 @@ /* * hostapd / EAP Full Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define EAP_H #include "common/defs.h" +#include "utils/list.h" #include "eap_common/eap_defs.h" #include "eap_server/eap_methods.h" #include "wpabuf.h" @@ -58,6 +59,8 @@ struct eap_eapol_interface { struct wpabuf *eapReqData; u8 *eapKeyData; size_t eapKeyDataLen; + u8 *eapSessionId; + size_t eapSessionIdLen; Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */ /* AAA interface to full authenticator variables */ @@ -78,11 +81,27 @@ struct eap_eapol_interface { Boolean aaaTimeout; }; +struct eap_server_erp_key { + struct dl_list list; + size_t rRK_len; + size_t rIK_len; + u8 rRK[ERP_MAX_KEY_LEN]; + u8 rIK[ERP_MAX_KEY_LEN]; + u32 recv_seq; + u8 cryptosuite; + char keyname_nai[]; +}; + struct eapol_callbacks { int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); const char * (*get_eap_req_id_text)(void *ctx, size_t *len); void (*log_msg)(void *ctx, const char *msg); + int (*get_erp_send_reauth_start)(void *ctx); + const char * (*get_erp_domain)(void *ctx); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); }; struct eap_config { @@ -111,6 +130,7 @@ struct eap_config { const u8 *server_id; size_t server_id_len; + int erp; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index 3a6802b7..7d723091 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -88,6 +88,19 @@ struct eap_method { * private data or this function may derive the key. */ u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); + + /** + * getSessionId - Get EAP method specific Session-Id + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store Session-Id length + * Returns: Session-Id or %NULL if not available + * + * This function can be used to get the Session-Id from the EAP method. + * The Session-Id may already be stored in the method-specific private + * data or this function may derive the Session-Id. + */ + u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len); }; /** @@ -103,7 +116,8 @@ struct eap_sm { EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2, EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2, EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE, - EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2 + EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2, + EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED } EAP_state; /* Constants */ @@ -125,6 +139,7 @@ struct eap_sm { /* Short-term (not maintained between packets) */ Boolean rxResp; + Boolean rxInitiate; int respId; EapType respMethod; int respVendor; @@ -132,7 +147,7 @@ struct eap_sm { Boolean ignore; enum { DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE, - DECISION_PASSTHROUGH + DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START } decision; /* Miscellaneous variables */ @@ -192,6 +207,10 @@ struct eap_sm { const u8 *server_id; size_t server_id_len; + Boolean initiate_reauth_start_sent; + Boolean try_initiate_reauth; + int erp; + #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; #endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index c1bb6b83..bd919e57 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1,6 +1,6 @@ /* * hostapd / EAP Full Authenticator state machine (RFC 4137) - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,6 +15,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "eap_i.h" #include "state_machine.h" #include "common/wpa_ctrl.h" @@ -44,6 +45,73 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm); static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method); +static int eap_get_erp_send_reauth_start(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_send_reauth_start) + return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx); + return 0; +} + + +static const char * eap_get_erp_domain(struct eap_sm *sm) +{ + if (sm->eapol_cb->get_erp_domain) + return sm->eapol_cb->get_erp_domain(sm->eapol_ctx); + return NULL; +} + + +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm, + const char *keyname) +{ + if (sm->eapol_cb->erp_get_key) + return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname); + return NULL; +} + + +static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp) +{ + if (sm->eapol_cb->erp_add_key) + return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp); + return -1; +} + +#endif /* CONFIG_ERP */ + + +static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm, + u8 id) +{ + const char *domain; + size_t plen = 1; + struct wpabuf *msg; + size_t domain_len = 0; + + domain = eap_get_erp_domain(sm); + if (domain) { + domain_len = os_strlen(domain); + plen += 2 + domain_len; + } + + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen, + EAP_CODE_INITIATE, id); + if (msg == NULL) + return NULL; + wpabuf_put_u8(msg, 0); /* Reserved */ + if (domain) { + /* Domain name TLV */ + wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME); + wpabuf_put_u8(msg, domain_len); + wpabuf_put_data(msg, domain, domain_len); + } + + return msg; +} + + static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src) { if (src == NULL) @@ -164,6 +232,7 @@ SM_STATE(EAP, INITIALIZE) eap_server_clear_identity(sm); } + sm->try_initiate_reauth = FALSE; sm->currentId = -1; sm->eap_if.eapSuccess = FALSE; sm->eap_if.eapFail = FALSE; @@ -171,6 +240,9 @@ SM_STATE(EAP, INITIALIZE) bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + sm->eap_if.eapSessionIdLen = 0; sm->eap_if.eapKeyAvailable = FALSE; sm->eap_if.eapRestart = FALSE; @@ -336,6 +408,95 @@ SM_STATE(EAP, METHOD_REQUEST) } +static void eap_server_erp_init(struct eap_sm *sm) +{ +#ifdef CONFIG_ERP + u8 *emsk = NULL; + size_t emsk_len = 0; + u8 EMSKname[EAP_EMSK_NAME_LEN]; + u8 len[2]; + const char *domain; + size_t domain_len, nai_buf_len; + struct eap_server_erp_key *erp = NULL; + int pos; + + domain = eap_get_erp_domain(sm); + if (!domain) + return; + + domain_len = os_strlen(domain); + + nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len; + if (nai_buf_len > 253) { + /* + * keyName-NAI has a maximum length of 253 octet to fit in + * RADIUS attributes. + */ + wpa_printf(MSG_DEBUG, + "EAP: Too long realm for ERP keyName-NAI maximum length"); + return; + } + nai_buf_len++; /* null termination */ + erp = os_zalloc(sizeof(*erp) + nai_buf_len); + if (erp == NULL) + goto fail; + erp->recv_seq = (u32) -1; + + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable EMSK available for ERP"); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); + + WPA_PUT_BE16(len, 8); + if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen, + "EMSK", len, sizeof(len), + EMSKname, EAP_EMSK_NAME_LEN) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN); + + pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len, + EMSKname, EAP_EMSK_NAME_LEN); + erp->keyname_nai[pos] = '@'; + os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len); + + WPA_PUT_BE16(len, emsk_len); + if (hmac_sha256_kdf(emsk, emsk_len, + "EAP Re-authentication Root Key@ietf.org", + len, sizeof(len), erp->rRK, emsk_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP"); + goto fail; + } + erp->rRK_len = emsk_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "EAP Re-authentication Integrity Key@ietf.org", + len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); + goto fail; + } + erp->rIK_len = erp->rRK_len; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len); + + if (eap_erp_add_key(sm, erp) == 0) { + wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", + erp->keyname_nai); + erp = NULL; + } + +fail: + bin_clear_free(emsk, emsk_len); + bin_clear_free(erp, sizeof(*erp)); +#endif /* CONFIG_ERP */ +} + + SM_STATE(EAP, METHOD_RESPONSE) { SM_ENTRY(EAP, METHOD_RESPONSE); @@ -355,6 +516,18 @@ SM_STATE(EAP, METHOD_RESPONSE) sm->eap_if.eapKeyData = NULL; sm->eap_if.eapKeyDataLen = 0; } + os_free(sm->eap_if.eapSessionId); + sm->eap_if.eapSessionId = NULL; + if (sm->m->getSessionId) { + sm->eap_if.eapSessionId = sm->m->getSessionId( + sm, sm->eap_method_priv, + &sm->eap_if.eapSessionIdLen); + wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", + sm->eap_if.eapSessionId, + sm->eap_if.eapSessionIdLen); + } + if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId) + eap_server_erp_init(sm); sm->methodState = METHOD_END; } else { sm->methodState = METHOD_CONTINUE; @@ -369,6 +542,7 @@ SM_STATE(EAP, PROPOSE_METHOD) SM_ENTRY(EAP, PROPOSE_METHOD); + sm->try_initiate_reauth = FALSE; try_another_method: type = eap_sm_Policy_getNextMethod(sm, &vendor); if (vendor == EAP_VENDOR_IETF) @@ -492,12 +666,326 @@ SM_STATE(EAP, SUCCESS) } +SM_STATE(EAP, INITIATE_REAUTH_START) +{ + SM_ENTRY(EAP, INITIATE_REAUTH_START); + + sm->initiate_reauth_start_sent = TRUE; + sm->try_initiate_reauth = TRUE; + sm->currentId = eap_sm_nextId(sm, sm->currentId); + wpa_printf(MSG_DEBUG, + "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d", + sm->currentId); + sm->lastId = sm->currentId; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm, + sm->currentId); + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; +} + + +#ifdef CONFIG_ERP + +static void erp_send_finish_reauth(struct eap_sm *sm, + struct eap_server_erp_key *erp, u8 id, + u8 flags, u16 seq, const char *nai) +{ + size_t plen; + struct wpabuf *msg; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + u8 seed[4]; + + if (erp) { + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + hash_len = 16; + break; + default: + return; + } + } else + hash_len = 0; + + plen = 1 + 2 + 2 + os_strlen(nai); + if (hash_len) + plen += 1 + hash_len; + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen, + EAP_CODE_FINISH, id); + if (msg == NULL) + return; + wpabuf_put_u8(msg, flags); + wpabuf_put_be16(msg, seq); + + wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI); + wpabuf_put_u8(msg, os_strlen(nai)); + wpabuf_put_str(msg, nai); + + if (erp) { + wpabuf_put_u8(msg, erp->cryptosuite); + if (hmac_sha256(erp->rIK, erp->rIK_len, + wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { + wpabuf_free(msg); + return; + } + wpabuf_put_data(msg, hash, hash_len); + } + + wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)", + flags & 0x80 ? "failure" : "success"); + + sm->lastId = sm->currentId; + sm->currentId = id; + wpabuf_free(sm->eap_if.eapReqData); + sm->eap_if.eapReqData = msg; + wpabuf_free(sm->lastReqData); + sm->lastReqData = NULL; + + if (flags & 0x80) { + sm->eap_if.eapFail = TRUE; + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); + return; + } + + bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapKeyDataLen = 0; + sm->eap_if.eapKeyData = os_malloc(erp->rRK_len); + if (!sm->eap_if.eapKeyData) + return; + + WPA_PUT_BE16(seed, seq); + WPA_PUT_BE16(&seed[2], erp->rRK_len); + if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, + "Re-authentication Master Session Key@ietf.org", + seed, sizeof(seed), + sm->eap_if.eapKeyData, erp->rRK_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP"); + bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len); + sm->eap_if.eapKeyData = NULL; + return; + } + sm->eap_if.eapKeyDataLen = erp->rRK_len; + sm->eap_if.eapKeyAvailable = TRUE; + wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK", + sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + sm->eap_if.eapSuccess = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); +} + + +SM_STATE(EAP, INITIATE_RECEIVED) +{ + const u8 *pos, *end, *start, *tlvs, *hdr; + const struct eap_hdr *ehdr; + size_t len; + u8 flags; + u16 seq; + char nai[254]; + struct eap_server_erp_key *erp; + int max_len; + u8 hash[SHA256_MAC_LEN]; + size_t hash_len; + struct erp_tlvs parse; + u8 resp_flags = 0x80; /* default to failure; cleared on success */ + + SM_ENTRY(EAP, INITIATE_RECEIVED); + + sm->rxInitiate = FALSE; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, + sm->eap_if.eapRespData, &len); + if (pos == NULL) { + wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame"); + goto fail; + } + hdr = wpabuf_head(sm->eap_if.eapRespData); + ehdr = wpabuf_head(sm->eap_if.eapRespData); + + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len); + if (len < 4) { + wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth"); + goto fail; + } + end = pos + len; + + flags = *pos++; + seq = WPA_GET_BE16(pos); + pos += 2; + wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq); + tlvs = pos; + + /* + * Parse TVs/TLVs. Since we do not yet know the length of the + * Authentication Tag, stop parsing if an unknown TV/TLV is seen and + * just try to find the keyName-NAI first so that we can check the + * Authentication Tag. + */ + if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0) + goto fail; + + if (!parse.keyname) { + wpa_printf(MSG_DEBUG, + "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet"); + goto fail; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI", + parse.keyname, parse.keyname_len); + if (parse.keyname_len > 253) { + wpa_printf(MSG_DEBUG, + "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth"); + goto fail; + } + os_memcpy(nai, parse.keyname, parse.keyname_len); + nai[parse.keyname_len] = '\0'; + + if (!sm->eap_server) { + /* + * In passthrough case, EAP-Initiate/Re-auth replaces + * EAP Identity exchange. Use keyName-NAI as the user identity + * and forward EAP-Initiate/Re-auth to the backend + * authentication server. + */ + wpa_printf(MSG_DEBUG, + "EAP: Use keyName-NAI as user identity for backend authentication"); + eap_server_clear_identity(sm); + sm->identity = (u8 *) dup_binstr(parse.keyname, + parse.keyname_len); + if (!sm->identity) + goto fail; + sm->identity_len = parse.keyname_len; + return; + } + + erp = eap_erp_get_key(sm, nai); + if (!erp) { + wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s", + nai); + goto report_error; + } + + if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) { + wpa_printf(MSG_DEBUG, + "EAP: SEQ=%u replayed (already received SEQ=%u)", + seq, erp->recv_seq); + goto fail; + } + + /* Is there enough room for Cryptosuite and Authentication Tag? */ + start = parse.keyname + parse.keyname_len; + max_len = end - start; + if (max_len < + 1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) { + wpa_printf(MSG_DEBUG, + "EAP: Not enough room for Authentication Tag"); + goto fail; + } + + switch (erp->cryptosuite) { + case EAP_ERP_CS_HMAC_SHA256_256: + if (end[-33] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 32; + break; + case EAP_ERP_CS_HMAC_SHA256_128: + if (end[-17] != erp->cryptosuite) { + wpa_printf(MSG_DEBUG, + "EAP: Different Cryptosuite used"); + goto fail; + } + hash_len = 16; + break; + default: + hash_len = 0; + break; + } + + if (hash_len) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - hash_len, hash) < 0) + goto fail; + if (os_memcmp(end - hash_len, hash, hash_len) != 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag mismatch"); + goto fail; + } + } + + /* Check if any supported CS results in matching tag */ + if (!hash_len && max_len >= 1 + 32 && + end[-33] == EAP_ERP_CS_HMAC_SHA256_256) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 32, hash) < 0) + goto fail; + if (os_memcmp(end - 32, hash, 32) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-256"); + hash_len = 32; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256; + } + } + + if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) { + if (hmac_sha256(erp->rIK, erp->rIK_len, hdr, + end - hdr - 16, hash) < 0) + goto fail; + if (os_memcmp(end - 16, hash, 16) == 0) { + wpa_printf(MSG_DEBUG, + "EAP: Authentication Tag match using HMAC-SHA256-128"); + hash_len = 16; + erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128; + } + } + + if (!hash_len) { + wpa_printf(MSG_DEBUG, + "EAP: No supported cryptosuite matched Authentication Tag"); + goto fail; + } + end -= 1 + hash_len; + + /* + * Parse TVs/TLVs again now that we know the exact part of the buffer + * that contains them. + */ + wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs", + tlvs, end - tlvs); + if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0) + goto fail; + + wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u", + erp->keyname_nai, seq); + erp->recv_seq = seq; + resp_flags &= ~0x80; /* R=0 - success */ + +report_error: + erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai); + return; + +fail: + sm->ignore = TRUE; +} + +#endif /* CONFIG_ERP */ + + SM_STATE(EAP, INITIALIZE_PASSTHROUGH) { SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH); wpabuf_free(sm->eap_if.aaaEapRespData); sm->eap_if.aaaEapRespData = NULL; + sm->try_initiate_reauth = FALSE; } @@ -691,9 +1179,14 @@ SM_STEP(EAP) SM_ENTER(EAP, INITIALIZE); break; case EAP_IDLE: - if (sm->eap_if.retransWhile == 0) - SM_ENTER(EAP, RETRANSMIT); - else if (sm->eap_if.eapResp) + if (sm->eap_if.retransWhile == 0) { + if (sm->try_initiate_reauth) { + sm->try_initiate_reauth = FALSE; + SM_ENTER(EAP, SELECT_ACTION); + } else { + SM_ENTER(EAP, RETRANSMIT); + } + } else if (sm->eap_if.eapResp) SM_ENTER(EAP, RECEIVED); break; case EAP_RETRANSMIT: @@ -716,6 +1209,10 @@ SM_STEP(EAP) sm->respVendor == EAP_VENDOR_IETF && sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); +#ifdef CONFIG_ERP + else if (sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ else { wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " "rxResp=%d respId=%d currentId=%d " @@ -804,9 +1301,22 @@ SM_STEP(EAP) SM_ENTER(EAP, SUCCESS); else if (sm->decision == DECISION_PASSTHROUGH) SM_ENTER(EAP, INITIALIZE_PASSTHROUGH); + else if (sm->decision == DECISION_INITIATE_REAUTH_START) + SM_ENTER(EAP, INITIATE_REAUTH_START); +#ifdef CONFIG_ERP + else if (sm->eap_server && sm->erp && sm->rxInitiate) + SM_ENTER(EAP, INITIATE_RECEIVED); +#endif /* CONFIG_ERP */ else SM_ENTER(EAP, PROPOSE_METHOD); break; + case EAP_INITIATE_REAUTH_START: + SM_ENTER(EAP, SEND_REQUEST); + break; + case EAP_INITIATE_RECEIVED: + if (!sm->eap_server) + SM_ENTER(EAP, SELECT_ACTION); + break; case EAP_TIMEOUT_FAILURE: break; case EAP_FAILURE: @@ -876,6 +1386,12 @@ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, { int rto, i; + if (sm->try_initiate_reauth) { + wpa_printf(MSG_DEBUG, + "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start"); + return 1; + } + if (methodTimeout) { /* * EAP method (either internal or through AAA server, provided @@ -929,6 +1445,7 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) /* parse rxResp, respId, respMethod */ sm->rxResp = FALSE; + sm->rxInitiate = FALSE; sm->respId = -1; sm->respMethod = EAP_TYPE_NONE; sm->respVendor = EAP_VENDOR_IETF; @@ -955,6 +1472,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) if (hdr->code == EAP_CODE_RESPONSE) sm->rxResp = TRUE; + else if (hdr->code == EAP_CODE_INITIATE) + sm->rxInitiate = TRUE; if (plen > sizeof(*hdr)) { u8 *pos = (u8 *) (hdr + 1); @@ -972,10 +1491,10 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp) } } - wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " - "respMethod=%u respVendor=%u respVendorMethod=%u", - sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, - sm->respVendorMethod); + wpa_printf(MSG_DEBUG, + "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod, + sm->respVendor, sm->respVendorMethod); } @@ -1216,6 +1735,13 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) return DECISION_CONTINUE; } + if (!sm->identity && eap_get_erp_send_reauth_start(sm) && + !sm->initiate_reauth_start_sent) { + wpa_printf(MSG_DEBUG, + "EAP: getDecision: send EAP-Initiate/Re-auth-Start"); + return DECISION_INITIATE_REAUTH_START; + } + if (sm->identity == NULL || sm->currentId == -1) { wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known " "yet -> CONTINUE"); @@ -1326,6 +1852,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->pbc_in_m1 = conf->pbc_in_m1; sm->server_id = conf->server_id; sm->server_id_len = conf->server_id_len; + sm->erp = conf->erp; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1353,6 +1880,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) sm->m->reset(sm, sm->eap_method_priv); wpabuf_free(sm->eap_if.eapReqData); bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen); + os_free(sm->eap_if.eapSessionId); wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index 09b976e6..db9b6aa2 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1294,6 +1294,28 @@ static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = data->eap_method; + os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN); + os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_aka_register(void) { struct eap_method *eap; @@ -1313,6 +1335,7 @@ int eap_server_aka_register(void) eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; + eap->getSessionId = eap_aka_get_session_id; ret = eap_server_method_register(eap); if (ret) @@ -1342,6 +1365,7 @@ int eap_server_aka_prime_register(void) eap->getKey = eap_aka_getKey; eap->isSuccess = eap_aka_isSuccess; eap->get_emsk = eap_aka_get_emsk; + eap->getSessionId = eap_aka_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 2692bced..56ac7f43 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -186,7 +186,6 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len, switch (*pos) { case PAC_OPAQUE_TYPE_PAD: - pos = end; goto done; case PAC_OPAQUE_TYPE_KEY: if (pos[1] != EAP_FAST_PAC_KEY_LEN) { @@ -1017,7 +1016,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, if (m->check(sm, priv, &buf)) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to " "ignore the packet"); - next_type = eap_fast_req_failure(sm, data); + eap_fast_req_failure(sm, data); return; } @@ -1590,6 +1589,18 @@ static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_fast_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_FAST, + len); +} + + int eap_server_fast_register(void) { struct eap_method *eap; @@ -1609,6 +1620,7 @@ int eap_server_fast_register(void) eap->getKey = eap_fast_getKey; eap->get_emsk = eap_fast_get_emsk; eap->isSuccess = eap_fast_isSuccess; + eap->getSessionId = eap_fast_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index cb369e44..50f15c31 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -24,6 +24,8 @@ struct eap_gpsk_data { size_t sk_len; u8 pk[EAP_GPSK_MAX_PK_LEN]; size_t pk_len; + u8 session_id[128]; + size_t id_len; u8 *id_peer; size_t id_peer_len; #define MAX_NUM_CSUITES 2 @@ -417,6 +419,21 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } + if (eap_gpsk_derive_session_id(sm->user->password, + sm->user->password_len, + data->vendor, data->specifier, + data->rand_peer, data->rand_server, + data->id_peer, data->id_peer_len, + sm->server_id, sm->server_id_len, + EAP_TYPE_GPSK, + data->session_id, &data->id_len) < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", + data->session_id, data->id_len); + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); if (end - pos < (int) miclen) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " @@ -593,6 +610,24 @@ static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(data->id_len); + if (sid == NULL) + return NULL; + os_memcpy(sid, data->session_id, data->id_len); + *len = data->id_len; + + return sid; +} + + int eap_server_gpsk_register(void) { struct eap_method *eap; @@ -612,6 +647,7 @@ int eap_server_gpsk_register(void) eap->getKey = eap_gpsk_getKey; eap->isSuccess = eap_gpsk_isSuccess; eap->get_emsk = eap_gpsk_get_emsk; + eap->getSessionId = eap_gpsk_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 65b2ef69..16e62764 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -309,6 +309,12 @@ static int eap_ikev2_process_fragment(struct eap_ikev2_data *data, if (data->in_buf == NULL) { /* First fragment of the message */ + if (message_length > 50000) { + /* Limit maximum memory allocation */ + wpa_printf(MSG_DEBUG, + "EAP-IKEV2: Ignore too long message"); + return -1; + } data->in_buf = wpabuf_alloc(message_length); if (data->in_buf == NULL) { wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for " @@ -511,6 +517,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ikev2_data *data = priv; + u8 *sid; + size_t sid_len; + size_t offset; + + if (data->state != DONE || !data->keymat_ok) + return NULL; + + sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len; + sid = os_malloc(sid_len); + if (sid) { + offset = 0; + sid[offset] = EAP_TYPE_IKEV2; + offset++; + os_memcpy(sid + offset, data->ikev2.i_nonce, + data->ikev2.i_nonce_len); + offset += data->ikev2.i_nonce_len; + os_memcpy(sid + offset, data->ikev2.r_nonce, + data->ikev2.r_nonce_len); + *len = sid_len; + wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id", + sid, sid_len); + } + + return sid; +} + + int eap_server_ikev2_register(void) { struct eap_method *eap; @@ -531,6 +567,7 @@ int eap_server_ikev2_register(void) eap->getKey = eap_ikev2_getKey; eap->isSuccess = eap_ikev2_isSuccess; eap->get_emsk = eap_ikev2_get_emsk; + eap->getSessionId = eap_ikev2_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index f7a753de..05848d2e 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -414,13 +414,16 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, } pw_hash = pw_hash_buf; } - generate_authenticator_response_pwhash( - pw_hash, peer_challenge, data->auth_challenge, - username, username_len, nt_response, - data->auth_response); - - hash_nt_password_hash(pw_hash, pw_hash_hash); - get_master_key(pw_hash_hash, nt_response, data->master_key); + if (generate_authenticator_response_pwhash( + pw_hash, peer_challenge, data->auth_challenge, + username, username_len, nt_response, + data->auth_response) < 0 || + hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 || + get_master_key(pw_hash_hash, nt_response, + data->master_key)) { + data->state = FAILURE; + return; + } data->master_key_valid = 1; wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", data->master_key, MSCHAPV2_KEY_LEN); diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index d9d4375a..0e6b4a06 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -36,6 +36,7 @@ struct eap_pax_data { u8 mk[EAP_PAX_MK_LEN]; u8 ck[EAP_PAX_CK_LEN]; u8 ick[EAP_PAX_ICK_LEN]; + u8 mid[EAP_PAX_MID_LEN]; int keys_set; char *cid; size_t cid_len; @@ -148,7 +149,6 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm, (u8 *) data->cid, data->cid_len, NULL, 0, pos); wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", pos, EAP_PAX_MAC_LEN); - pos += EAP_PAX_MAC_LEN; /* Optional ADE could be added here, if needed */ @@ -388,7 +388,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, if (eap_pax_initial_key_derivation(data->mac_id, data->ak, data->rand.e, data->mk, data->ck, - data->ick) < 0) { + data->ick, data->mid) < 0) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " "key derivation"); data->state = FAILURE; @@ -542,6 +542,26 @@ static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *sid; + + if (data->state != SUCCESS) + return NULL; + + sid = os_malloc(1 + EAP_PAX_MID_LEN); + if (sid == NULL) + return NULL; + + *len = 1 + EAP_PAX_MID_LEN; + sid[0] = EAP_TYPE_PAX; + os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN); + + return sid; +} + + int eap_server_pax_register(void) { struct eap_method *eap; @@ -561,6 +581,7 @@ int eap_server_pax_register(void) eap->getKey = eap_pax_getKey; eap->isSuccess = eap_pax_isSuccess; eap->get_emsk = eap_pax_get_emsk; + eap->getSessionId = eap_pax_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c index 594e02dd..98d608bf 100644 --- a/src/eap_server/eap_server_peap.c +++ b/src/eap_server/eap_server_peap.c @@ -1229,6 +1229,18 @@ static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_peap_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP, + len); +} + + int eap_server_peap_register(void) { struct eap_method *eap; @@ -1247,6 +1259,7 @@ int eap_server_peap_register(void) eap->isDone = eap_peap_isDone; eap->getKey = eap_peap_getKey; eap->isSuccess = eap_peap_isSuccess; + eap->getSessionId = eap_peap_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index db394e98..12b5d25d 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -485,6 +485,28 @@ static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_PSK_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_PSK; + os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN); + os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_psk_register(void) { struct eap_method *eap; @@ -504,6 +526,7 @@ int eap_server_psk_register(void) eap->getKey = eap_psk_getKey; eap->isSuccess = eap_psk_isSuccess; eap->get_emsk = eap_psk_get_emsk; + eap->getSessionId = eap_psk_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 7e1278dd..943af0d1 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -1020,6 +1020,25 @@ static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv) } +static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pwd_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + id = os_malloc(1 + SHA256_MAC_LEN); + if (id == NULL) + return NULL; + + os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); + *len = 1 + SHA256_MAC_LEN; + + return id; +} + + int eap_server_pwd_register(void) { struct eap_method *eap; @@ -1028,8 +1047,6 @@ int eap_server_pwd_register(void) struct timezone tz; u32 sr; - EVP_add_digest(EVP_sha256()); - sr = 0xdeaddada; (void) gettimeofday(&tp, &tz); sr ^= (tp.tv_sec ^ tp.tv_usec); @@ -1050,6 +1067,7 @@ int eap_server_pwd_register(void) eap->getKey = eap_pwd_getkey; eap->get_emsk = eap_pwd_get_emsk; eap->isSuccess = eap_pwd_is_success; + eap->getSessionId = eap_pwd_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 1937621c..de707773 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -495,6 +495,28 @@ static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + 2 * EAP_SAKE_RAND_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SAKE; + os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_sake_register(void) { struct eap_method *eap; @@ -514,6 +536,7 @@ int eap_server_sake_register(void) eap->getKey = eap_sake_getKey; eap->isSuccess = eap_sake_isSuccess; eap->get_emsk = eap_sake_get_emsk; + eap->getSessionId = eap_sake_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 23ee2b60..ddfb71cf 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -820,6 +820,29 @@ static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sim_data *data = priv; + u8 *id; + + if (data->state != SUCCESS) + return NULL; + + *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN; + id = os_malloc(*len); + if (id == NULL) + return NULL; + + id[0] = EAP_TYPE_SIM; + os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN); + os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt, + EAP_SIM_NONCE_MT_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len); + + return id; +} + + int eap_server_sim_register(void) { struct eap_method *eap; @@ -839,6 +862,7 @@ int eap_server_sim_register(void) eap->getKey = eap_sim_getKey; eap->isSuccess = eap_sim_isSuccess; eap->get_emsk = eap_sim_get_emsk; + eap->getSessionId = eap_sim_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 6bed62f8..58cfe8ac 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -287,7 +287,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (emsk) os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); - os_free(eapKeyData); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); } else emsk = NULL; @@ -310,6 +310,18 @@ static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS, + len); +} + + int eap_server_tls_register(void) { struct eap_method *eap; @@ -329,6 +341,7 @@ int eap_server_tls_register(void) eap->getKey = eap_tls_getKey; eap->isSuccess = eap_tls_isSuccess; eap->get_emsk = eap_tls_get_emsk; + eap->getSessionId = eap_tls_get_session_id; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 01853e68..56916c45 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -140,6 +140,47 @@ fail: } +/** + * eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @data: Data for TLS processing + * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) + * @len: Pointer to length of the session ID generated + * Returns: Pointer to allocated Session-Id on success or %NULL on failure + * + * This function derive the Session-Id based on the TLS session data + * (client/server random and method type). + * + * The caller is responsible for freeing the returned buffer. + */ +u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len) +{ + struct tls_keys keys; + u8 *out; + + if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) + return NULL; + + if (keys.client_random == NULL || keys.server_random == NULL) + return NULL; + + *len = 1 + keys.client_random_len + keys.server_random_len; + out = os_malloc(*len); + if (out == NULL) + return NULL; + + /* Session-Id = EAP type || client.random || server.random */ + out[0] = eap_type; + os_memcpy(out + 1, keys.client_random, keys.client_random_len); + os_memcpy(out + 1 + keys.client_random_len, keys.server_random, + keys.server_random_len); + + return out; +} + + struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id) { diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index 31e3871d..12a31b07 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -409,7 +409,7 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; ret = os_snprintf((char *) pos, end - pos, "S="); - if (ret >= 0 && ret < end - pos) + if (!os_snprintf_error(end - pos, ret)) pos += ret; pos += wpa_snprintf_hex_uppercase( (char *) pos, end - pos, data->mschapv2_auth_response, @@ -1181,6 +1181,50 @@ static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) } +static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + + if (data->state != SUCCESS) + return NULL; + + return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS, + len); +} + + +static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_ttls_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = os_malloc(EAP_EMSK_LEN); + if (emsk) + os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + } else + emsk = NULL; + + if (emsk) { + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK"); + } + + return emsk; +} + + int eap_server_ttls_register(void) { struct eap_method *eap; @@ -1199,6 +1243,8 @@ int eap_server_ttls_register(void) eap->isDone = eap_ttls_isDone; eap->getKey = eap_ttls_getKey; eap->isSuccess = eap_ttls_isSuccess; + eap->getSessionId = eap_ttls_get_session_id; + eap->get_emsk = eap_ttls_get_emsk; ret = eap_server_method_register(eap); if (ret) diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 97ec0c0e..9d9c28d7 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -380,7 +380,7 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, message_length = WPA_GET_BE16(pos); pos += 2; - if (message_length < end - pos) { + if (message_length < end - pos || message_length > 50000) { wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " "Length"); return; diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c index bc2cbe5b..acf54353 100644 --- a/src/eap_server/eap_sim_db.c +++ b/src/eap_server/eap_sim_db.c @@ -573,16 +573,14 @@ static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) char buf[1000], *pos, *cmd, *imsi; int res; - res = recv(sock, buf, sizeof(buf), 0); + res = recv(sock, buf, sizeof(buf) - 1, 0); if (res < 0) return; + buf[res] = '\0'; wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " "external source", (u8 *) buf, res); if (res == 0) return; - if (res >= (int) sizeof(buf)) - res = sizeof(buf) - 1; - buf[res] = '\0'; if (data->get_complete_cb == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " @@ -924,12 +922,13 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return EAP_SIM_DB_FAILURE; len += ret; @@ -966,7 +965,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix) pos = id; end = id + sizeof(buf) * 2 + 2; *pos++ = prefix; - pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); + wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); return id; } @@ -1387,7 +1386,8 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return EAP_SIM_DB_FAILURE; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; @@ -1451,19 +1451,20 @@ int eap_sim_db_resynchronize(struct eap_sim_db_data *data, imsi_len = os_strlen(imsi); len = os_snprintf(msg, sizeof(msg), "AKA-AUTS "); - if (len < 0 || len + imsi_len >= sizeof(msg)) + if (os_snprintf_error(sizeof(msg), len) || + len + imsi_len >= sizeof(msg)) return -1; os_memcpy(msg + len, imsi, imsi_len); len += imsi_len; ret = os_snprintf(msg + len, sizeof(msg) - len, " "); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, auts, EAP_AKA_AUTS_LEN); ret = os_snprintf(msg + len, sizeof(msg) - len, " "); - if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + if (os_snprintf_error(sizeof(msg) - len, ret)) return -1; len += ret; len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index 91449afd..ddf90b85 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -74,6 +74,9 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len); +u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, + struct eap_ssl_data *data, u8 eap_type, + size_t *len); struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, int eap_type, int version, u8 id); struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c index 6c6969b5..55795828 100644 --- a/src/eapol_auth/eapol_auth_dump.c +++ b/src/eapol_auth/eapol_auth_dump.c @@ -130,7 +130,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n" "reAuthWhen=%d\n", sm->aWhile, sm->quietWhile, sm->reAuthWhen); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -173,7 +173,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), _SB(sm->reAuthenticate)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -215,7 +215,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, sm->authAuthReauthsWhileAuthenticated, sm->authAuthEapStartsWhileAuthenticated, sm->authAuthEapLogoffWhileAuthenticated); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -240,7 +240,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -251,14 +251,14 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, reauth_timer_state_txt(sm->reauth_timer_state), sm->reAuthPeriod, _SB(sm->reAuthEnabled)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "auth_key_tx_state=%s\n", auth_key_tx_state_txt(sm->auth_key_tx_state)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -267,7 +267,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, "rxKey=%s\n", key_rx_state_txt(sm->key_rx_state), _SB(sm->rxKey)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -280,7 +280,7 @@ int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf, ctrl_dir_txt(sm->adminControlledDirections), ctrl_dir_txt(sm->operControlledDirections), _SB(sm->operEdge)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #undef _SB diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index a76fa13b..0df6eb56 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -1,6 +1,6 @@ /* * IEEE 802.1X-2004 Authenticator - EAPOL state machine - * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -43,6 +43,7 @@ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); static void eapol_auth_initialize(struct eapol_state_machine *sm); +static void eapol_auth_conf_free(struct eapol_auth_config *conf); static void eapol_auth_logger(struct eapol_authenticator *eapol, @@ -833,6 +834,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; eap_conf.server_id = eapol->conf.server_id; eap_conf.server_id_len = eapol->conf.server_id_len; + eap_conf.erp = eapol->conf.erp; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -851,6 +853,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, sm->radius_cui = wpabuf_alloc_copy(radius_cui, os_strlen(radius_cui)); + sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++; + if (eapol->acct_multi_session_id_lo == 0) + eapol->acct_multi_session_id_hi++; + sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi; + return sm; } @@ -1020,11 +1027,44 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) } +static int eapol_sm_get_erp_send_reauth_start(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_send_reauth_start; +} + + +static const char * eapol_sm_get_erp_domain(void *ctx) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->conf.erp_domain; +} + + +static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx, + const char *keyname) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname); +} + + +static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp); +} + + static struct eapol_callbacks eapol_cb = { eapol_sm_get_eap_user, eapol_sm_get_eap_req_id_text, - NULL + NULL, + eapol_sm_get_erp_send_reauth_start, + eapol_sm_get_erp_domain, + eapol_sm_erp_get_key, + eapol_sm_erp_add_key, }; @@ -1069,21 +1109,16 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, } if (src->pac_opaque_encr_key) { dst->pac_opaque_encr_key = os_malloc(16); - if (dst->pac_opaque_encr_key == NULL) { - os_free(dst->eap_req_id_text); - return -1; - } + if (dst->pac_opaque_encr_key == NULL) + goto fail; os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); - if (dst->eap_fast_a_id == NULL) { - os_free(dst->eap_req_id_text); - os_free(dst->pac_opaque_encr_key); - return -1; - } + if (dst->eap_fast_a_id == NULL) + goto fail; os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; @@ -1091,12 +1126,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_fast_a_id = NULL; if (src->eap_fast_a_id_info) { dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); - if (dst->eap_fast_a_id_info == NULL) { - os_free(dst->eap_req_id_text); - os_free(dst->pac_opaque_encr_key); - os_free(dst->eap_fast_a_id); - return -1; - } + if (dst->eap_fast_a_id_info == NULL) + goto fail; } else dst->eap_fast_a_id_info = NULL; dst->eap_fast_prov = src->eap_fast_prov; @@ -1106,7 +1137,23 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->tnc = src->tnc; dst->wps = src->wps; dst->fragment_size = src->fragment_size; + + os_free(dst->erp_domain); + if (src->erp_domain) { + dst->erp_domain = os_strdup(src->erp_domain); + if (dst->erp_domain == NULL) + goto fail; + } else { + dst->erp_domain = NULL; + } + dst->erp_send_reauth_start = src->erp_send_reauth_start; + dst->erp = src->erp; + return 0; + +fail: + eapol_auth_conf_free(dst); + return -1; } @@ -1120,6 +1167,8 @@ static void eapol_auth_conf_free(struct eapol_auth_config *conf) conf->eap_fast_a_id = NULL; os_free(conf->eap_fast_a_id_info); conf->eap_fast_a_id_info = NULL; + os_free(conf->erp_domain); + conf->erp_domain = NULL; } @@ -1127,6 +1176,7 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, struct eapol_auth_cb *cb) { struct eapol_authenticator *eapol; + struct os_time now; eapol = os_zalloc(sizeof(*eapol)); if (eapol == NULL) @@ -1152,6 +1202,14 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, eapol->cb.abort_auth = cb->abort_auth; eapol->cb.tx_key = cb->tx_key; eapol->cb.eapol_event = cb->eapol_event; + eapol->cb.erp_get_key = cb->erp_get_key; + eapol->cb.erp_add_key = cb->erp_add_key; + + /* Acct-Multi-Session-Id should be unique over reboots. If reliable + * clock is not available, this could be replaced with reboot counter, + * etc. */ + os_get_time(&now); + eapol->acct_multi_session_id_hi = now.sec; return eapol; } diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index 320a0adb..ebed19ad 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -24,6 +24,9 @@ struct eapol_auth_config { void *eap_sim_db_priv; char *eap_req_id_text; /* a copy of this will be allocated */ size_t eap_req_id_text_len; + int erp_send_reauth_start; + char *erp_domain; /* a copy of this will be allocated */ + int erp; /* Whether ERP is enabled on authentication server */ u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; @@ -45,6 +48,7 @@ struct eapol_auth_config { }; struct eap_user; +struct eap_server_erp_key; typedef enum { EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING @@ -71,6 +75,9 @@ struct eapol_auth_cb { void (*abort_auth)(void *ctx, void *sta_ctx); void (*tx_key)(void *ctx, void *sta_ctx); void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type); + struct eap_server_erp_key * (*erp_get_key)(void *ctx, + const char *keyname); + int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp); }; diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h index 25baddba..a29b49c9 100644 --- a/src/eapol_auth/eapol_auth_sm_i.h +++ b/src/eapol_auth/eapol_auth_sm_i.h @@ -30,6 +30,9 @@ struct eapol_authenticator { u8 *default_wep_key; u8 default_wep_key_idx; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; @@ -175,6 +178,9 @@ struct eapol_state_machine { void *sta; /* station context pointer to use in callbacks */ int remediation; + + u32 acct_multi_session_id_hi; + u32 acct_multi_session_id_lo; }; #endif /* EAPOL_AUTH_SM_I_H */ diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 70258be2..941a2694 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -128,6 +128,7 @@ struct eapol_sm { struct wpabuf *eapReqData; /* for EAP */ Boolean altAccept; /* for EAP */ Boolean altReject; /* for EAP */ + Boolean eapTriggerStart; Boolean replay_counter_valid; u8 last_replay_counter[16]; struct eapol_config conf; @@ -222,6 +223,7 @@ SM_STATE(SUPP_PAE, DISCONNECTED) SM_ENTRY(SUPP_PAE, DISCONNECTED); sm->sPortMode = Auto; sm->startCount = 0; + sm->eapTriggerStart = FALSE; sm->logoffSent = FALSE; eapol_sm_set_port_unauthorized(sm); sm->suppAbort = TRUE; @@ -244,6 +246,11 @@ SM_STATE(SUPP_PAE, CONNECTING) { int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING; SM_ENTRY(SUPP_PAE, CONNECTING); + + if (sm->eapTriggerStart) + send_start = 1; + sm->eapTriggerStart = FALSE; + if (send_start) { sm->startWhen = sm->startPeriod; sm->startCount++; @@ -255,7 +262,7 @@ SM_STATE(SUPP_PAE, CONNECTING) * delay authentication. Use a short timeout to send the first * EAPOL-Start if Authenticator does not start authentication. */ - if (sm->conf.wps) { + if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) { /* Reduce latency on starting WPS negotiation. */ wpa_printf(MSG_DEBUG, "EAPOL: Using shorter startWhen for WPS"); @@ -386,6 +393,8 @@ SM_STEP(SUPP_PAE) SM_ENTER(SUPP_PAE, HELD); else if (sm->suppTimeout) SM_ENTER(SUPP_PAE, CONNECTING); + else if (sm->eapTriggerStart) + SM_ENTER(SUPP_PAE, CONNECTING); break; case SUPP_PAE_HELD: if (sm->heldWhile == 0) @@ -1099,7 +1108,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, "suppPortStatus=%s\n", eapol_supp_pae_state(sm->SUPP_PAE_state), eapol_port_status(sm->suppPortStatus)); - if (len < 0 || (size_t) len >= buflen) + if (os_snprintf_error(buflen, len)) return 0; if (verbose) { @@ -1116,7 +1125,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen, sm->maxStart, eapol_port_control(sm->portControl), eapol_supp_be_state(sm->SUPP_BE_state)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } @@ -1170,7 +1179,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) "Authorized" : "Unauthorized", sm->SUPP_BE_state); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; len = ret; @@ -1198,7 +1207,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen) sm->dot1xSuppLastEapolFrameVersion, MAC2STR(sm->dot1xSuppLastEapolFrameSource)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1822,6 +1831,8 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) return sm->altAccept; case EAPOL_altReject: return sm->altReject; + case EAPOL_eapTriggerStart: + return sm->eapTriggerStart; } return FALSE; } @@ -1861,6 +1872,9 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, case EAPOL_altReject: sm->altReject = value; break; + case EAPOL_eapTriggerStart: + sm->eapTriggerStart = value; + break; } } @@ -2026,6 +2040,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) conf.opensc_engine_path = ctx->opensc_engine_path; conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; + conf.openssl_ciphers = ctx->openssl_ciphers; conf.wps = ctx->wps; conf.cert_in_cb = ctx->cert_in_cb; @@ -2106,3 +2121,10 @@ int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) return -1; #endif /* CONFIG_EAP_PROXY */ } + + +void eapol_sm_erp_flush(struct eapol_sm *sm) +{ + if (sm) + eap_peer_erp_free_keys(sm->eap); +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 5b37314f..e089e88b 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -59,6 +59,8 @@ struct eapol_config { */ int external_sim; +#define EAPOL_LOCAL_WPS_IN_USE BIT(0) +#define EAPOL_PEER_IS_WPS20_AP BIT(1) /** * wps - Whether this connection is used for WPS */ @@ -210,6 +212,15 @@ struct eapol_ctx { const char *pkcs11_module_path; /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + const char *openssl_ciphers; + + /** * wps - WPS context data * * This is only used by EAP-WSC and can be left %NULL if not available. @@ -305,6 +316,7 @@ const char * eapol_sm_get_method_name(struct eapol_sm *sm); void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext); int eapol_sm_failed(struct eapol_sm *sm); +void eapol_sm_erp_flush(struct eapol_sm *sm); int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) @@ -405,6 +417,9 @@ static inline int eapol_sm_failed(struct eapol_sm *sm) { return 0; } +static inline void eapol_sm_erp_flush(struct eapol_sm *sm) +{ +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index dd825b56..7537f93e 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -39,6 +39,11 @@ struct l2_ethhdr { #pragma pack(pop) #endif /* _MSC_VER */ +enum l2_packet_filter_type { + L2_PACKET_FILTER_DHCP, + L2_PACKET_FILTER_NDISC, +}; + /** * l2_packet_init - Initialize l2_packet interface * @ifname: Interface name @@ -121,4 +126,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); */ void l2_packet_notify_auth_start(struct l2_packet_data *l2); +/** + * l2_packet_set_packet_filter - Set socket filter for l2_packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @type: enum l2_packet_filter_type, type of filter + * Returns: 0 on success, -1 on failure + * + * This function is used to set the socket filter for l2_packet socket. + * + */ +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type); + #endif /* L2_PACKET_H */ diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c index 2e9a04c8..d87c32b2 100644 --- a/src/l2_packet/l2_packet_freebsd.c +++ b/src/l2_packet/l2_packet_freebsd.c @@ -308,3 +308,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index 1419830d..89ff7db5 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -10,6 +10,7 @@ #include <sys/ioctl.h> #include <netpacket/packet.h> #include <net/if.h> +#include <linux/filter.h> #include "common.h" #include "eloop.h" @@ -28,6 +29,50 @@ struct l2_packet_data { * buffers */ }; +/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and + * src port bootps and dst port bootpc' + */ +static struct sock_filter dhcp_sock_filter_insns[] = { + { 0x80, 0, 0, 0x00000000 }, + { 0x35, 0, 12, 0x00000116 }, + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 10, 0x00000800 }, + { 0x30, 0, 0, 0x00000017 }, + { 0x15, 0, 8, 0x00000011 }, + { 0x28, 0, 0, 0x00000014 }, + { 0x45, 6, 0, 0x00001fff }, + { 0xb1, 0, 0, 0x0000000e }, + { 0x48, 0, 0, 0x0000000e }, + { 0x15, 0, 3, 0x00000043 }, + { 0x48, 0, 0, 0x00000010 }, + { 0x15, 0, 1, 0x00000044 }, + { 0x6, 0, 0, 0x00000bb8 }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog dhcp_sock_filter = { + .len = ARRAY_SIZE(dhcp_sock_filter_insns), + .filter = dhcp_sock_filter_insns, +}; + + +/* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */ +static struct sock_filter ndisc_sock_filter_insns[] = { + { 0x30, 0, 0, 0x00000000 }, + { 0x45, 0, 5, 0x00000001 }, + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 3, 0x000086dd }, + { 0x30, 0, 0, 0x00000014 }, + { 0x15, 0, 1, 0x0000003a }, + { 0x6, 0, 0, 0x000005dc }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog ndisc_sock_filter = { + .len = ARRAY_SIZE(ndisc_sock_filter_insns), + .filter = ndisc_sock_filter_insns, +}; + int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { @@ -202,3 +247,31 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + const struct sock_fprog *sock_filter; + + switch (type) { + case L2_PACKET_FILTER_DHCP: + sock_filter = &dhcp_sock_filter; + break; + case L2_PACKET_FILTER_NDISC: + sock_filter = &ndisc_sock_filter; + break; + default: + return -1; + } + + if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER, + sock_filter, sizeof(struct sock_fprog))) { + wpa_printf(MSG_ERROR, + "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s", + strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c index 23b8ddcc..39a62a0a 100644 --- a/src/l2_packet/l2_packet_ndis.c +++ b/src/l2_packet/l2_packet_ndis.c @@ -514,3 +514,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) void l2_packet_notify_auth_start(struct l2_packet_data *l2) { } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c index 6896c4e4..0501925c 100644 --- a/src/l2_packet/l2_packet_none.c +++ b/src/l2_packet/l2_packet_none.c @@ -116,3 +116,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) { /* This function can be left empty */ } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c index 45aef56b..bb4f4a31 100644 --- a/src/l2_packet/l2_packet_pcap.c +++ b/src/l2_packet/l2_packet_pcap.c @@ -54,15 +54,16 @@ static int l2_packet_init_libdnet(struct l2_packet_data *l2) l2->eth = eth_open(l2->ifname); if (!l2->eth) { - printf("Failed to open interface '%s'.\n", l2->ifname); - perror("eth_open"); + wpa_printf(MSG_ERROR, + "Failed to open interface '%s' - eth_open: %s", + l2->ifname, strerror(errno)); 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"); + wpa_printf(MSG_ERROR, + "Failed to get own hw address from interface '%s' - eth_get: %s", + l2->ifname, strerror(errno)); eth_close(l2->eth); l2->eth = NULL; return -1; @@ -378,3 +379,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) l2, l2->pcap); #endif /* CONFIG_WINPCAP */ } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index 6b117ca2..76dcccc7 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -44,7 +44,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { - perror("L2: sendmsg(cmd)"); + wpa_printf(MSG_ERROR, "L2: sendmsg(cmd): %s", strerror(errno)); return -1; } @@ -82,7 +82,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, msg.msg_namelen = sizeof(l2->priv_addr); if (sendmsg(l2->fd, &msg, 0) < 0) { - perror("L2: sendmsg(packet_send)"); + wpa_printf(MSG_ERROR, "L2: sendmsg(packet_send): %s", + strerror(errno)); return -1; } @@ -102,7 +103,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("l2_packet_receive - recvfrom"); + wpa_printf(MSG_ERROR, "l2_packet_receive - recvfrom: %s", + strerror(errno)); return; } if (res < ETH_ALEN) { @@ -162,7 +164,7 @@ struct l2_packet_data * l2_packet_init( l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (l2->fd < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); os_free(l2->own_socket_path); l2->own_socket_path = NULL; os_free(l2); @@ -173,7 +175,8 @@ struct l2_packet_data * l2_packet_init( 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("l2-pkt-privsep: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "l2-pkt-privsep: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } @@ -191,14 +194,14 @@ struct l2_packet_data * l2_packet_init( tv.tv_usec = 0; res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); if (res < 0 && errno != EINTR) { - perror("select"); + wpa_printf(MSG_ERROR, "select: %s", strerror(errno)); goto fail; } if (FD_ISSET(l2->fd, &rfds)) { res = recv(l2->fd, reply, sizeof(reply), 0); if (res < 0) { - perror("recv"); + wpa_printf(MSG_ERROR, "recv: %s", strerror(errno)); goto fail; } } else { @@ -259,3 +262,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2) { wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); } + + +int l2_packet_set_packet_filter(struct l2_packet_data *l2, + enum l2_packet_filter_type type) +{ + return -1; +} diff --git a/src/lib.rules b/src/lib.rules index b260d25a..0c79d992 100644 --- a/src/lib.rules +++ b/src/lib.rules @@ -15,6 +15,10 @@ ifeq ($(V), 1) Q= E=true endif +ifeq ($(QUIET), 1) +Q=@ +E=true +endif %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 7d4a03c5..fc610819 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -211,29 +211,35 @@ void p2p_clear_timeout(struct p2p_data *p2p) } -void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, - int status) +void p2p_go_neg_failed(struct p2p_data *p2p, int status) { struct p2p_go_neg_results res; - p2p_clear_timeout(p2p); - p2p_set_state(p2p, P2P_IDLE); - if (p2p->go_neg_peer) { - p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; - p2p->go_neg_peer->wps_method = WPS_NOT_READY; - p2p->go_neg_peer->oob_pw_id = 0; + struct p2p_device *peer = p2p->go_neg_peer; + + if (!peer) + return; + + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); + if (p2p->state != P2P_SEARCH) { + /* + * Clear timeouts related to GO Negotiation if no new p2p_find + * has been started. + */ + p2p_clear_timeout(p2p); + p2p_set_state(p2p, P2P_IDLE); } + + peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; + peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; p2p->go_neg_peer = NULL; os_memset(&res, 0, sizeof(res)); res.status = status; - if (peer) { - wpabuf_free(peer->go_neg_conf); - peer->go_neg_conf = NULL; - os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, - ETH_ALEN); - os_memcpy(res.peer_interface_addr, peer->intended_addr, - ETH_ALEN); - } + os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN); + os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); } @@ -348,8 +354,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) static void p2p_device_clear_reported(struct p2p_data *p2p) { struct p2p_device *dev; - dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { dev->flags &= ~P2P_DEV_REPORTED; + dev->sd_reqs = 0; + } } @@ -650,6 +658,24 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, } +static int p2p_compare_wfd_info(struct p2p_device *dev, + const struct p2p_message *msg) +{ + if (dev->info.wfd_subelems && msg->wfd_subelems) { + if (dev->info.wfd_subelems->used != msg->wfd_subelems->used) + return 1; + + return os_memcmp(dev->info.wfd_subelems->buf, + msg->wfd_subelems->buf, + dev->info.wfd_subelems->used); + } + if (dev->info.wfd_subelems || msg->wfd_subelems) + return 1; + + return 0; +} + + /** * p2p_add_device - Add peer entries based on scan results or P2P frames * @p2p: P2P module context from p2p_init() @@ -675,6 +701,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; + int wfd_changed; int i; struct os_reltime time_now; @@ -786,6 +813,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, break; } + wfd_changed = p2p_compare_wfd_info(dev, &msg); + if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); @@ -800,7 +829,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_update_peer_vendor_elems(dev, ies, ies_len); - if (dev->flags & P2P_DEV_REPORTED) + if (dev->flags & P2P_DEV_REPORTED && !wfd_changed) return 0; p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", @@ -848,8 +877,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev) /* * If GO Negotiation is in progress, report that it has failed. */ - p2p_go_neg_failed(p2p, dev, -1); - p2p->go_neg_peer = NULL; + p2p_go_neg_failed(p2p, -1); } if (p2p->invite_peer == dev) p2p->invite_peer = NULL; @@ -956,14 +984,8 @@ static void p2p_search(struct p2p_data *p2p) p2p->num_req_dev_types, p2p->req_dev_types, p2p->find_dev_id, pw_id); if (res < 0) { - p2p_dbg(p2p, "Scan request failed"); + p2p_dbg(p2p, "Scan request schedule failed"); p2p_continue_find(p2p); - } else { - p2p_dbg(p2p, "Running p2p_scan"); - p2p->p2p_scan_running = 1; - eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); - eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, - p2p, NULL); } } @@ -976,6 +998,22 @@ static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) } +void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status) +{ + if (status != 0) { + p2p_dbg(p2p, "Scan request failed"); + /* Do continue find even for the first p2p_find_scan */ + p2p_continue_find(p2p); + } else { + p2p_dbg(p2p, "Running p2p_scan"); + p2p->p2p_scan_running = 1; + eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); + eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, + p2p, NULL); + } +} + + static int p2p_run_after_scan(struct p2p_data *p2p) { struct p2p_device *dev; @@ -1106,17 +1144,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } - if (res == 0) { - p2p_dbg(p2p, "Running p2p_scan"); - p2p->p2p_scan_running = 1; - eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); - eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout, - p2p, NULL); - } else if (p2p->p2p_scan_running) { + if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ res = 0; /* do not report failure */ - } else { + } else if (res != 0) { p2p_dbg(p2p, "Failed to start p2p_scan"); p2p_set_state(p2p, P2P_IDLE); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); @@ -1334,8 +1366,8 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, if (go) p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); else if (!force_freq) - p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels, - &p2p->channels); + p2p_channels_union_inplace(&p2p->channels, + &p2p->cfg->cli_channels); p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels); p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", @@ -1616,8 +1648,6 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) struct p2p_go_neg_results res; int go = peer->go_state == LOCAL_GO; struct p2p_channels intersection; - int freqs; - size_t i, j; p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); @@ -1658,21 +1688,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) p2p_channels_dump(p2p, "intersection after no-GO removal", &intersection); } - freqs = 0; - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c = &intersection.reg_class[i]; - if (freqs + 1 == P2P_MAX_CHANNELS) - break; - for (j = 0; j < c->channels; j++) { - int freq; - if (freqs + 1 == P2P_MAX_CHANNELS) - break; - freq = p2p_channel_to_freq(c->reg_class, c->channel[j]); - if (freq < 0) - continue; - res.freq_list[freqs++] = freq; - } - } + + p2p_channels_to_freqs(&intersection, res.freq_list, + P2P_MAX_CHANNELS); res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; @@ -1713,7 +1731,6 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, rx_freq); break; case P2P_INVITATION_RESP: - p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_process_invitation_resp(p2p, sa, data + 1, len - 1); break; case P2P_PROV_DISC_REQ: @@ -1992,11 +2009,12 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps) attr.num_req_dev_type)) return 1; /* Own Primary Device Type matches */ - for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) + for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) { if (dev_type_list_match(p2p->cfg->sec_dev_type[i], attr.req_dev_type, attr.num_req_dev_type)) - return 1; /* Own Secondary Device Type matches */ + return 1; /* Own Secondary Device Type matches */ + } /* No matching device type found */ return 0; @@ -2541,6 +2559,7 @@ void p2p_deinit(struct p2p_data *p2p) eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p_flush(p2p); p2p_free_req_dev_types(p2p); os_free(p2p->cfg->dev_name); @@ -2583,8 +2602,10 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr)); - if (p2p->go_neg_peer == dev) + if (p2p->go_neg_peer == dev) { + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); p2p->go_neg_peer = NULL; + } dev->wps_method = WPS_NOT_READY; dev->oob_pw_id = 0; @@ -2742,28 +2763,64 @@ int p2p_set_country(struct p2p_data *p2p, const char *country) } +static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) +{ + if (dev->sd_pending_bcast_queries == 0) { + /* Initialize with total number of registered broadcast + * SD queries. */ + dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; + } + + if (p2p_start_sd(p2p, dev) == 0) + return 1; + + if (dev->req_config_methods && + !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { + p2p_dbg(p2p, "Send pending Provision Discovery Request to " + MACSTR " (config methods 0x%x)", + MAC2STR(dev->info.p2p_device_addr), + dev->req_config_methods); + if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) + return 1; + } + + return 0; +} + + void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; + int found; + p2p_set_state(p2p, P2P_SEARCH); + + /* Continue from the device following the last iteration */ + found = 0; dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { - if (dev->sd_pending_bcast_queries == 0) { - /* Initialize with total number of registered broadcast - * SD queries. */ - dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries; + if (dev == p2p->last_p2p_find_oper) { + found = 1; + continue; + } + if (!found) + continue; + if (p2p_pre_find_operation(p2p, dev) > 0) { + p2p->last_p2p_find_oper = dev; + return; } + } - if (p2p_start_sd(p2p, dev) == 0) + /* + * Wrap around to the beginning of the list and continue until the last + * iteration device. + */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (p2p_pre_find_operation(p2p, dev) > 0) { + p2p->last_p2p_find_oper = dev; return; - if (dev->req_config_methods && - !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { - p2p_dbg(p2p, "Send pending Provision Discovery Request to " - MACSTR " (config methods 0x%x)", - MAC2STR(dev->info.p2p_device_addr), - dev->req_config_methods); - if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0) - return; } + if (dev == p2p->last_p2p_find_oper) + break; } p2p_listen_in_find(p2p, 1); @@ -2777,6 +2834,8 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { + if (p2p->sd_peer) + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->sd_peer = NULL; p2p_continue_find(p2p); return; @@ -3057,8 +3116,7 @@ static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, { p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success); if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) { - p2p_go_neg_failed(p2p, p2p->go_neg_peer, - p2p->go_neg_peer->status); + p2p_go_neg_failed(p2p, p2p->go_neg_peer->status); } else if (success) { struct p2p_device *dev; dev = p2p_get_device(p2p, addr); @@ -3086,7 +3144,7 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); if (result == P2P_SEND_ACTION_FAILED) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_go_neg_failed(p2p, -1); return; } @@ -3257,7 +3315,7 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_go_neg_failed(p2p, -1); return 0; } @@ -3308,7 +3366,7 @@ static void p2p_timeout_connect(struct p2p_data *p2p) if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_go_neg_failed(p2p, -1); return; } if (p2p->go_neg_peer && @@ -3339,7 +3397,7 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p) if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_go_neg_failed(p2p, -1); return; } @@ -3365,20 +3423,12 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) { struct p2p_device *dev = p2p->go_neg_peer; - struct os_reltime now; if (dev == NULL) { p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } - os_get_reltime(&now); - if (os_reltime_expired(&now, &dev->go_neg_wait_started, 120)) { - p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation"); - p2p_go_neg_failed(p2p, dev, -1); - return; - } - p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation"); p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); p2p_listen_in_find(p2p, 0); @@ -3489,6 +3539,10 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state)); p2p->in_listen = 0; + if (p2p->drv_in_listen) { + p2p_dbg(p2p, "Driver is still in listen state - stop it"); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + } switch (p2p->state) { case P2P_IDLE: @@ -3704,7 +3758,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "[PD_FOR_JOIN]" : "", dev->status, dev->invitation_reqs); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -3714,7 +3768,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "ext_listen_interval=%u\n", dev->ext_listen_period, dev->ext_listen_interval); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -3724,7 +3778,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, "oper_ssid=%s\n", wpa_ssid_txt(dev->oper_ssid, dev->oper_ssid_len)); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -3732,7 +3786,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, #ifdef CONFIG_WIFI_DISPLAY if (dev->info.wfd_subelems) { res = os_snprintf(pos, end - pos, "wfd_subelems="); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -3741,7 +3795,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, wpabuf_len(dev->info.wfd_subelems)); res = os_snprintf(pos, end - pos, "\n"); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -4859,3 +4913,13 @@ void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem) { p2p->vendor_elem = vendor_elem; } + + +void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct p2p_data *p2p = eloop_ctx; + + p2p_dbg(p2p, + "Timeout on waiting peer to become ready for GO Negotiation"); + p2p_go_neg_failed(p2p, -1); +} diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 076a2ac1..fa886f74 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -949,6 +949,13 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, const u8 *dev_id, unsigned int search_delay); /** + * p2p_notify_scan_trigger_status - Indicate scan trigger status + * @p2p: P2P module context from p2p_init() + * @status: 0 on success, -1 on failure + */ +void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status); + +/** * p2p_stop_find - Stop P2P Find (Device Discovery) * @p2p: P2P module context from p2p_init() */ @@ -1738,6 +1745,9 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); int p2p_channels_includes_freq(const struct p2p_channels *channels, unsigned int freq); +int p2p_channels_to_freqs(const struct p2p_channels *channels, + int *freq_list, unsigned int max_len); + /** * p2p_supported_freq - Check whether channel is supported for P2P * @p2p: P2P module context from p2p_init() @@ -1912,7 +1922,8 @@ int p2p_set_no_go_freq(struct p2p_data *p2p, /** * p2p_in_progress - Check whether a P2P operation is progress * @p2p: P2P module context from p2p_init() - * Returns: 0 if P2P module is idle or 1 if an operation is in progress + * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not + * in search state, or 2 if search state operation is in progress */ int p2p_in_progress(struct p2p_data *p2p); diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 21fae3f2..c654c5a8 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "wps/wps_defs.h" @@ -240,6 +241,7 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) p2p_set_state(p2p, P2P_CONNECT); p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST; p2p->go_neg_peer = dev; + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->connect_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, @@ -621,7 +623,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, * Request frame. */ p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); return; } @@ -645,6 +647,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, p2p_add_dev_info(p2p, sa, dev, &msg); } + if (p2p->go_neg_peer && p2p->go_neg_peer == dev) + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); + if (dev && dev->flags & P2P_DEV_USER_REJECTED) { p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; @@ -789,6 +794,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, dev->dialog_token = msg.dialog_token; os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); p2p->go_neg_peer = dev; + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL); status = P2P_SC_SUCCESS; } @@ -957,7 +963,10 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); dev->flags |= P2P_DEV_NOT_YET_READY; - os_get_reltime(&dev->go_neg_wait_started); + eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, + NULL); + eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout, + p2p, NULL); if (p2p->state == P2P_CONNECT_LISTEN) p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT); else @@ -965,7 +974,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_set_timeout(p2p, 0, 0); } else { p2p_dbg(p2p, "Stop GO Negotiation attempt"); - p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_go_neg_failed(p2p, *msg.status); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); @@ -1147,13 +1156,13 @@ fail: wpabuf_head(dev->go_neg_conf), wpabuf_len(dev->go_neg_conf), 200) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); - p2p_go_neg_failed(p2p, dev, -1); + p2p_go_neg_failed(p2p, -1); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } else dev->go_neg_conf_sent++; if (status != P2P_SC_SUCCESS) { p2p_dbg(p2p, "GO Negotiation failed"); - p2p_go_neg_failed(p2p, dev, status); + p2p_go_neg_failed(p2p, status); } } @@ -1204,7 +1213,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, } if (*msg.status) { p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); - p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); return; } @@ -1216,7 +1225,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, } else if (dev->go_state == REMOTE_GO) { p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); p2p->ssid_len = 0; - p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS); + p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 3b60582b..62711e7c 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -101,10 +101,10 @@ struct p2p_device { unsigned int flags; int status; /* enum p2p_status_code */ - struct os_reltime go_neg_wait_started; unsigned int wait_count; unsigned int connect_reqs; unsigned int invitation_reqs; + unsigned int sd_reqs; u16 ext_listen_period; u16 ext_listen_interval; @@ -260,10 +260,18 @@ struct p2p_data { */ struct p2p_device *invite_peer; + /** + * last_p2p_find_oper - Pointer to last pre-find operation peer + */ + struct p2p_device *last_p2p_find_oper; + const u8 *invite_go_dev_addr; u8 invite_go_dev_addr_buf[ETH_ALEN]; int invite_dev_pw_id; + unsigned int retry_invite_req:1; + unsigned int retry_invite_req_sent:1; + /** * sd_peer - Pointer to Service Discovery peer */ @@ -606,6 +614,8 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel); void p2p_channels_intersect(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); +void p2p_channels_union_inplace(struct p2p_channels *res, + const struct p2p_channels *b); void p2p_channels_union(const struct p2p_channels *a, const struct p2p_channels *b, struct p2p_channels *res); @@ -768,8 +778,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr); struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p, const u8 *addr); -void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer, - int status); +void p2p_go_neg_failed(struct p2p_data *p2p, int status); void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer); int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps); int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[], @@ -783,6 +792,7 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go); +void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx); void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index ef01a668..558c6dd0 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -174,7 +174,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, u8 group_bssid[ETH_ALEN], *bssid; int op_freq = 0; u8 reg_class = 0, channel = 0; - struct p2p_channels intersection, *channels = NULL; + struct p2p_channels all_channels, intersection, *channels = NULL; int persistent; os_memset(group_bssid, 0, sizeof(group_bssid)); @@ -226,7 +226,10 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, persistent = 1; } - if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev, + p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels, + &all_channels); + + if (p2p_peer_channels_check(p2p, &all_channels, dev, msg.channel_list, msg.channel_list_len) < 0) { p2p_dbg(p2p, "No common channels found"); @@ -235,8 +238,9 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, } p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "own client channels", &all_channels); p2p_channels_dump(p2p, "peer channels", &dev->channels); - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + p2p_channels_intersect(&all_channels, &dev->channels, &intersection); p2p_channels_dump(p2p, "intersection", &intersection); @@ -248,6 +252,17 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, msg.dev_password_id_present ? msg.dev_password_id : -1); } + if (go) { + p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, + &intersection); + p2p_channels_dump(p2p, "intersection(GO)", &intersection); + if (intersection.reg_classes == 0) { + p2p_dbg(p2p, "No common channels found (GO)"); + status = P2P_SC_FAIL_NO_COMMON_CHANNELS; + goto fail; + } + } + if (op_freq) { p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", op_freq); @@ -412,25 +427,68 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, if (dev == NULL) { p2p_dbg(p2p, "Ignore Invitation Response from unknown peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } if (dev != p2p->invite_peer) { p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer " MACSTR, MAC2STR(sa)); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; } - if (p2p_parse(data, len, &msg)) + if (p2p_parse(data, len, &msg)) { + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); return; + } if (!msg.status) { p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); p2p_parse_free(&msg); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + return; + } + + /* + * We should not really receive a replayed response twice since + * duplicate frames are supposed to be dropped. However, not all drivers + * do that for pre-association frames. We did not use to verify dialog + * token matches for invitation response frames, but that check can be + * safely used to drop a replayed response to the previous Invitation + * Request in case the suggested operating channel was changed. This + * allows a duplicated reject frame to be dropped with the assumption + * that the real response follows after it. + */ + if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && + p2p->retry_invite_req_sent && + msg.dialog_token != dev->dialog_token) { + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", + msg.dialog_token, dev->dialog_token); + p2p_parse_free(&msg); return; } + if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS && + p2p->retry_invite_req && + p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class, + &p2p->op_channel) == 0) { + p2p->retry_invite_req = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p_set_state(p2p, P2P_INVITE); + p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel", + p2p->op_reg_class, p2p->op_channel); + p2p->retry_invite_req_sent = 1; + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); + p2p_parse_free(&msg); + return; + } + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->retry_invite_req = 0; + if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) { p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from " MACSTR, MAC2STR(sa)); @@ -592,6 +650,9 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, dev_pw_id); } p2p->invite_dev_pw_id = dev_pw_id; + p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO && + persistent_group && !force_freq; + p2p->retry_invite_req_sent = 0; dev = p2p_get_device(p2p, peer); if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index d6144a0e..52ba19e0 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -309,23 +309,27 @@ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg) while (pos < end) { u16 attr_len; - if (pos + 2 >= end) { + u8 id; + + if (end - pos < 3) { wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute"); return -1; } - attr_len = WPA_GET_LE16(pos + 1); + id = *pos++; + attr_len = WPA_GET_LE16(pos); + pos += 2; wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u", - pos[0], attr_len); - if (pos + 3 + attr_len > end) { + id, attr_len); + if (attr_len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Attribute underflow " "(len=%u left=%d)", - attr_len, (int) (end - pos - 3)); + attr_len, (int) (end - pos)); wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos); return -1; } - if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg)) + if (p2p_parse_attribute(id, pos, attr_len, msg)) return -1; - pos += 3 + attr_len; + pos += attr_len; } return 0; @@ -603,7 +607,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, "dev=" MACSTR " iface=" MACSTR, MAC2STR(cli->p2p_device_addr), MAC2STR(cli->p2p_interface_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -614,7 +618,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, wps_dev_type_bin2str(cli->pri_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -623,7 +627,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, wps_dev_type_bin2str( &cli->sec_dev_types[s * 8], devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -638,7 +642,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf, } ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -672,7 +676,7 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) "p2p_dev_capab=0x%x\n" "p2p_group_capab=0x%x\n", msg.capability[0], msg.capability[1]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -684,14 +688,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) wps_dev_type_bin2str(msg.pri_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n", msg.device_name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -699,14 +703,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR "\n", MAC2STR(msg.p2p_device_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n", msg.config_methods); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index 13119c20..1a2af04b 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -75,16 +75,25 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, return NULL; /* query number that needs to be send to the device */ if (count == dev->sd_pending_bcast_queries - 1) - return q; + goto found; count++; } if (!q->for_all_peers && os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) == 0) - return q; + goto found; } return NULL; + +found: + if (dev->sd_reqs > 100) { + p2p_dbg(p2p, "Too many SD request attempts to " MACSTR + " - skip remaining queries", + MAC2STR(dev->info.p2p_device_addr)); + return NULL; + } + return q; } @@ -287,6 +296,7 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) if (req == NULL) return -1; + dev->sd_reqs++; p2p->sd_peer = dev; p2p->sd_query = query; p2p->pending_action_state = P2P_PENDING_SD; diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index 23acce76..f32751d7 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "common/ieee802_11_common.h" #include "p2p_i.h" @@ -54,56 +55,7 @@ int p2p_random(char *buf, size_t len) */ int p2p_channel_to_freq(int op_class, int channel) { - /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */ - /* TODO: more operating classes */ - switch (op_class) { - case 81: - /* channels 1..13 */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 82: - /* channel 14 */ - if (channel != 14) - return -1; - return 2414 + 5 * channel; - case 83: /* channels 1..9; 40 MHz */ - case 84: /* channels 5..13; 40 MHz */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 115: /* channels 36,40,44,48; indoor only */ - case 118: /* channels 52,56,60,64; dfs */ - if (channel < 36 || channel > 64) - return -1; - return 5000 + 5 * channel; - case 124: /* channels 149,153,157,161 */ - case 125: /* channels 149,153,157,161,165,169 */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 116: /* channels 36,44; 40 MHz; indoor only */ - case 117: /* channels 40,48; 40 MHz; indoor only */ - case 119: /* channels 52,60; 40 MHz; dfs */ - case 120: /* channels 56,64; 40 MHz; dfs */ - if (channel < 36 || channel > 64) - return -1; - return 5000 + 5 * channel; - case 126: /* channels 149,157; 40 MHz */ - case 127: /* channels 153,161; 40 MHz */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */ - if (channel < 36 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 180: /* 60 GHz band, channels 1..4 */ - if (channel < 1 || channel > 4) - return -1; - return 56160 + 2160 * channel; - } - return -1; + return ieee80211_chan_to_freq(NULL, op_class, channel); } @@ -241,20 +193,15 @@ static void p2p_op_class_union(struct p2p_reg_class *cl, /** - * p2p_channels_union - Union of channel lists - * @a: First set of channels + * p2p_channels_union_inplace - Inplace union of channel lists + * @res: Input data and place for returning union of the channel sets * @b: Second set of channels - * @res: Data structure for returning the union of channels */ -void p2p_channels_union(const struct p2p_channels *a, - const struct p2p_channels *b, - struct p2p_channels *res) +void p2p_channels_union_inplace(struct p2p_channels *res, + const struct p2p_channels *b) { size_t i, j; - if (a != res) - os_memcpy(res, a, sizeof(*res)); - for (i = 0; i < res->reg_classes; i++) { struct p2p_reg_class *cl = &res->reg_class[i]; for (j = 0; j < b->reg_classes; j++) { @@ -284,6 +231,21 @@ void p2p_channels_union(const struct p2p_channels *a, } +/** + * p2p_channels_union - Union of channel lists + * @a: First set of channels + * @b: Second set of channels + * @res: Data structure for returning the union of channels + */ +void p2p_channels_union(const struct p2p_channels *a, + const struct p2p_channels *b, + struct p2p_channels *res) +{ + os_memcpy(res, a, sizeof(*res)); + p2p_channels_union_inplace(res, b); +} + + void p2p_channels_remove_freqs(struct p2p_channels *chan, const struct wpa_freq_range_list *list) { @@ -428,7 +390,7 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, const struct p2p_reg_class *c; c = &chan->reg_class[i]; ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -436,7 +398,7 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, ret = os_snprintf(pos, end - pos, "%s%u", j == 0 ? "" : ",", c->channel[j]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -517,3 +479,35 @@ int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, return 0; } + + +int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list, + unsigned int max_len) +{ + unsigned int i, idx; + + if (!channels || max_len == 0) + return 0; + + for (i = 0, idx = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *c = &channels->reg_class[i]; + unsigned int j; + + if (idx + 1 == max_len) + break; + for (j = 0; j < c->channels; j++) { + int freq; + if (idx + 1 == max_len) + break; + freq = p2p_channel_to_freq(c->reg_class, + c->channel[j]); + if (freq < 0) + continue; + freq_list[idx++] = freq; + } + } + + freq_list[idx] = 0; + + return idx; +} diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index b1cf32dd..ef744304 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -729,7 +729,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, /* If the peer's MI is my MI, I will choose new MI */ if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) { - os_get_random(participant->mi, sizeof(participant->mi)); + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + return NULL; participant->mn = 0; } @@ -1003,8 +1004,10 @@ static int ieee802_1x_mka_decode_live_peer_body( if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ if (peer_mn > participant->mn) { - os_get_random(participant->mi, - sizeof(participant->mi)); + if (os_get_random(participant->mi, + sizeof(participant->mi)) < 0) + wpa_printf(MSG_DEBUG, + "KaY: Could not update mi"); participant->mn = 0; } continue; @@ -1054,8 +1057,10 @@ ieee802_1x_mka_decode_potential_peer_body( if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) { /* My message id is used by other participant */ if (peer_mn > participant->mn) { - os_get_random(participant->mi, - sizeof(participant->mi)); + if (os_get_random(participant->mi, + sizeof(participant->mi)) < 0) + wpa_printf(MSG_DEBUG, + "KaY: Could not update mi"); participant->mn = 0; } continue; @@ -1998,7 +2003,12 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) return -1; } ctx_offset = 0; - os_get_random(context + ctx_offset, conf->key_len); + if (os_get_random(context + ctx_offset, conf->key_len) < 0) { + os_free(context); + os_free(conf->key); + os_free(conf); + return -1; + } ctx_offset += conf->key_len; dl_list_for_each(peer, &participant->live_peers, struct ieee802_1x_kay_peer, list) { @@ -3159,7 +3169,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; kay->macsec_desired = FALSE; kay->macsec_protect = FALSE; - kay->macsec_validate = FALSE; + kay->macsec_validate = Disabled; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; @@ -3167,7 +3177,7 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50; kay->macsec_desired = TRUE; kay->macsec_protect = TRUE; - kay->macsec_validate = TRUE; + kay->macsec_validate = Strict; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; @@ -3325,7 +3335,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, participant->retry_count = 0; participant->kay = kay; - os_get_random(participant->mi, sizeof(participant->mi)); + if (os_get_random(participant->mi, sizeof(participant->mi)) < 0) + goto fail; participant->mn = 0; participant->lrx = FALSE; @@ -3340,6 +3351,9 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, dl_list_init(&participant->rxsc_list); participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci, kay->sc_ch); + secy_cp_control_protect_frames(kay, kay->macsec_protect); + secy_cp_control_replay(kay, kay->macsec_replay_protect, + kay->macsec_replay_window); secy_create_transmit_sc(kay, participant->txsc); /* to derive KEK from CAK and CKN */ diff --git a/src/radius/radius.c b/src/radius/radius.c index f3b645dc..6eba2eb6 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -233,6 +233,17 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_LOCATION_INFO, "Location-Information", + RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES, + "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES, + "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info", + RADIUS_ATTR_INT32 }, { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, @@ -945,7 +956,6 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, vhdr = (struct radius_attr_vendor *) pos; if (vhdr->vendor_length > left || vhdr->vendor_length < sizeof(*vhdr)) { - left = 0; break; } if (vhdr->vendor_type != subtype) { diff --git a/src/radius/radius.h b/src/radius/radius.h index 62faae10..5977339e 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -92,6 +92,13 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_NAS_IPV6_ADDRESS = 95, RADIUS_ATTR_ERROR_CAUSE = 101, RADIUS_ATTR_EAP_KEY_NAME = 102, + RADIUS_ATTR_OPERATOR_NAME = 126, + RADIUS_ATTR_LOCATION_INFO = 127, + RADIUS_ATTR_LOCATION_DATA = 128, + RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129, + RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130, + RADIUS_ATTR_LOCATION_CAPABLE = 131, + RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132, RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177, RADIUS_ATTR_WLAN_HESSID = 181, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186, diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index e2766e2f..1382c53b 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -1039,6 +1039,13 @@ radius_change_server(struct radius_client_data *radius, return -1; } + if (sel_sock < 0) { + wpa_printf(MSG_INFO, + "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d", + nserv->addr.af, sock, sock6, auth); + return -1; + } + if (conf->force_client_addr) { switch (conf->client_addr.af) { case AF_INET: diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 00394b49..85a485e9 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -252,6 +252,20 @@ struct radius_server_data { const char *server_id; /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + * + * This controls whether the authentication server derives ERP key + * hierarchy (rRK and rIK) from full EAP authentication and allows + * these keys to be used to perform ERP to derive rMSK instead of full + * EAP authentication to derive MSK. + */ + int erp; + + const char *erp_domain; + + struct dl_list erp_keys; /* struct eap_server_erp_key */ + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -673,6 +687,7 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.pwd_group = data->pwd_group; eap_conf.server_id = (const u8 *) data->server_id; eap_conf.server_id_len = os_strlen(data->server_id); + eap_conf.erp = data->erp; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); @@ -1687,6 +1702,7 @@ radius_server_init(struct radius_server_conf *conf) if (data == NULL) return NULL; + dl_list_init(&data->erp_keys); os_get_reltime(&data->start_time); data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; @@ -1725,6 +1741,8 @@ radius_server_init(struct radius_server_conf *conf) data->eap_req_id_text_len = conf->eap_req_id_text_len; } } + data->erp = conf->erp; + data->erp_domain = conf->erp_domain; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -1802,6 +1820,24 @@ radius_server_init(struct radius_server_conf *conf) /** + * radius_server_erp_flush - Flush all ERP keys + * @data: RADIUS server context from radius_server_init() + */ +void radius_server_erp_flush(struct radius_server_data *data) +{ + struct eap_server_erp_key *erp; + + if (data == NULL) + return; + while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key, + list)) != NULL) { + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); + } +} + + +/** * radius_server_deinit - Deinitialize RADIUS server * @data: RADIUS server context from radius_server_init() */ @@ -1836,6 +1872,8 @@ void radius_server_deinit(struct radius_server_data *data) sqlite3_close(data->db); #endif /* CONFIG_SQLITE */ + radius_server_erp_flush(data); + os_free(data); } @@ -1874,7 +1912,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, "radiusAuthServResetTime=0\n" "radiusAuthServConfigReset=4\n", uptime); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -1913,7 +1951,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, data->counters.malformed_acct_requests, data->counters.acct_bad_authenticators, data->counters.unknown_acct_types); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -1971,7 +2009,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, cli->counters.malformed_acct_requests, cli->counters.acct_bad_authenticators, cli->counters.unknown_acct_types); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { *pos = '\0'; return pos - buf; } @@ -2017,11 +2055,57 @@ static void radius_server_log_msg(void *ctx, const char *msg) } +#ifdef CONFIG_ERP + +static const char * radius_server_get_erp_domain(void *ctx) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + + return data->erp_domain; +} + + +static struct eap_server_erp_key * +radius_server_erp_get_key(void *ctx, const char *keyname) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} + + +static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct radius_session *sess = ctx; + struct radius_server_data *data = sess->server; + + dl_list_add(&data->erp_keys, &erp->list); + return 0; +} + +#endif /* CONFIG_ERP */ + + static struct eapol_callbacks radius_server_eapol_cb = { .get_eap_user = radius_server_get_eap_user, .get_eap_req_id_text = radius_server_get_eap_req_id_text, .log_msg = radius_server_log_msg, +#ifdef CONFIG_ERP + .get_erp_send_reauth_start = NULL, + .get_erp_domain = radius_server_get_erp_domain, + .erp_get_key = radius_server_erp_get_key, + .erp_add_key = radius_server_erp_add_key, +#endif /* CONFIG_ERP */ }; diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 46ac3127..ca4e38c1 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -159,6 +159,18 @@ struct radius_server_conf { const char *server_id; /** + * erp - Whether EAP Re-authentication Protocol (ERP) is enabled + * + * This controls whether the authentication server derives ERP key + * hierarchy (rRK and rIK) from full EAP authentication and allows + * these keys to be used to perform ERP to derive rMSK instead of full + * EAP authentication to derive MSK. + */ + int erp; + + const char *erp_domain; + + /** * wps - Wi-Fi Protected Setup context * * If WPS is used with an external RADIUS server (which is quite @@ -223,6 +235,7 @@ struct radius_server_conf { struct radius_server_data * radius_server_init(struct radius_server_conf *conf); +void radius_server_erp_flush(struct radius_server_data *data); void radius_server_deinit(struct radius_server_data *data); int radius_server_get_mib(struct radius_server_data *data, char *buf, diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c index aab8b7e6..aca8f540 100644 --- a/src/rsn_supp/peerkey.c +++ b/src/rsn_supp/peerkey.c @@ -242,7 +242,8 @@ static int wpa_supplicant_process_smk_m2( peerkey->cipher = cipher; #ifdef CONFIG_IEEE80211W if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256)) + WPA_KEY_MGMT_PSK_SHA256 | + WPA_KEY_MGMT_IEEE8021X_SUITE_B)) peerkey->use_sha256 = 1; #endif /* CONFIG_IEEE80211W */ @@ -927,8 +928,8 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, os_memcpy(mic, key->key_mic, 16); if (peerkey->tstk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, - key->key_mic); + wpa_eapol_key_mic(peerkey->tstk.kck, sm->key_mgmt, ver, buf, + len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " "when using TSTK - ignoring TSTK"); @@ -943,7 +944,7 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, if (!ok && peerkey->stk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, + wpa_eapol_key_mic(peerkey->stk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index 885291a2..8af04d0f 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -109,6 +109,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @pmk: The new pairwise master key * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes * @aa: Authenticator address * @spa: Supplicant address * @network_ctx: Network configuration context for this PMK @@ -122,6 +124,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) */ struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { struct rsn_pmksa_cache_entry *entry, *pos, *prev; @@ -130,13 +133,19 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, if (pmk_len > PMK_LEN) return NULL; + if (wpa_key_mgmt_suite_b(akmp) && !kck) + return NULL; + entry = os_zalloc(sizeof(*entry)); if (entry == NULL) return NULL; os_memcpy(entry->pmk, pmk, pmk_len); entry->pmk_len = pmk_len; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + if (wpa_key_mgmt_suite_b(akmp)) + rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); + else + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * @@ -333,6 +342,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *new_entry; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, + NULL, 0, aa, pmksa->sm->own_addr, old_entry->network_ctx, old_entry->akmp); if (new_entry == NULL) @@ -472,7 +482,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; i = 0; @@ -481,7 +491,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", i, MAC2STR(entry->aa)); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, @@ -489,7 +499,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, " %d %d\n", (int) (entry->expiration - now.sec), entry->opportunistic); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; entry = entry->next; diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index 6cbf89aa..f8e040e0 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -57,6 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); @@ -104,6 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, + const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp) { return NULL; diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index 915f85e7..af0e1085 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -94,6 +94,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, pmk, pmk_len); sm->pmk_len = pmk_len; pmksa_cache_add(sm->pmksa, pmk, pmk_len, + NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, WPA_KEY_MGMT_IEEE8021X); @@ -298,7 +299,8 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) sm->proto != WPA_PROTO_RSN || wpa_sm_get_state(sm) != WPA_COMPLETED || (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) { + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " "state for new pre-authentication"); return; /* invalid state for new pre-auth */ @@ -391,6 +393,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, dl_list_for_each(pos, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { if (cand->priority <= pos->priority) { + if (!pos->list.prev) { + /* + * This cannot really happen in pracrice since + * pos was fetched from the list and the prev + * pointer must be set. It looks like clang + * static analyzer gets confused with the + * dl_list_del(&cand->list) call above and ends + * up assuming pos->list.prev could be NULL. + */ + os_free(cand); + return; + } dl_list_add(pos->list.prev, &cand->list); cand = NULL; break; @@ -487,7 +501,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, if (sm->preauth_eapol) { ret = os_snprintf(pos, end - pos, "Pre-authentication " "EAPOL state machines:\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; res = eapol_sm_get_status(sm->preauth_eapol, diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 8cb19a25..4baeb3b0 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -148,6 +148,9 @@ struct wpa_tdls_peer { size_t supp_oper_classes_len; u8 wmm_capable; + + /* channel switch currently enabled */ + int chan_switch_enabled; }; @@ -687,6 +690,7 @@ static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) peer->qos_info = 0; peer->wmm_capable = 0; peer->tpk_set = peer->tpk_success = 0; + peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); os_memset(peer->rnonce, 0, WPA_NONCE_LEN); @@ -742,6 +746,13 @@ static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, return 0; } + /* Cancel active channel switch before teardown */ + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR + " to base channel", MAC2STR(addr)); + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + } + dialog_token = peer->dtoken; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, @@ -858,9 +869,11 @@ void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr) if (wpa_tdls_is_external_setup(sm)) { /* - * Disable the link, send a teardown packet through the - * AP, and then reset link data. + * Get us on the base channel, disable the link, send a + * teardown packet through the AP, and then reset link data. */ + if (peer->chan_switch_enabled) + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); wpa_tdls_send_teardown(sm, addr, WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE); @@ -2742,7 +2755,8 @@ int wpa_tdls_init(struct wpa_sm *sm) * are assumed to perform everything internally */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, - &sm->tdls_external_setup) < 0) { + &sm->tdls_external_setup, + &sm->tdls_chan_switch) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; } @@ -2751,6 +2765,8 @@ int wpa_tdls_init(struct wpa_sm *sm) "driver", sm->tdls_supported ? "" : " not"); wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", sm->tdls_external_setup ? "external" : "internal"); + wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching", + sm->tdls_chan_switch ? "supports" : "does not support"); return 0; } @@ -2830,39 +2846,61 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(const u8 *ies, size_t len) +static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) { - struct wpa_eapol_ie_parse elems; - - if (ies == NULL) - return 0; - - if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) - return 0; + /* bit 38 - TDLS Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x40); +} - if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) - return 0; - /* bit 38 - TDLS Prohibited */ - return !!(elems.ext_capab[2 + 4] & 0x40); +static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +{ + /* bit 39 - TDLS Channel Switch Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x80); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + struct wpa_eapol_ie_parse elems; + + sm->tdls_prohibited = 0; + sm->tdls_chan_switch_prohibited = 0; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + sm->tdls_prohibited = wpa_tdls_prohibited(&elems); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); + sm->tdls_chan_switch_prohibited = + wpa_tdls_chan_switch_prohibited(&elems); + wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS", + sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + struct wpa_eapol_ie_parse elems; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_prohibited = 1; } + + if (!sm->tdls_chan_switch_prohibited && + wpa_tdls_chan_switch_prohibited(&elems)) { + wpa_printf(MSG_DEBUG, + "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs"); + sm->tdls_chan_switch_prohibited = 1; + } } @@ -2877,3 +2915,78 @@ int wpa_tdls_is_external_setup(struct wpa_sm *sm) { return sm->tdls_external_setup; } + + +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params) +{ + struct wpa_tdls_peer *peer; + int ret; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + if (!sm->tdls_chan_switch) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching not supported by the driver"); + return -1; + } + + if (sm->tdls_chan_switch_prohibited) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel"); + return -1; + } + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) { + wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR + " not found for channel switching", MAC2STR(addr)); + return -1; + } + + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " already has channel switching enabled", + MAC2STR(addr)); + return 0; + } + + ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr, + oper_class, freq_params); + if (!ret) + peer->chan_switch_enabled = 1; + + return ret; +} + + +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (!peer || !peer->chan_switch_enabled) { + wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for " + MACSTR, MAC2STR(addr)); + return -1; + } + + /* ignore the return value */ + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + + peer->chan_switch_enabled = 0; + return 0; +} diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index 8f561b9a..8ea54bba 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -56,10 +56,10 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, } } if (key_mic && - wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { + wpa_eapol_key_mic(kck, sm->key_mgmt, ver, msg, msg_len, key_mic)) { wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, - "WPA: Failed to generate EAPOL-Key " - "version %d MIC", ver); + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); goto out; } wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16); @@ -89,7 +89,8 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) int key_info, ver; u8 bssid[ETH_ALEN], *rbuf; - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN) + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) @@ -138,6 +139,24 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) } +static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) +{ +#ifdef CONFIG_IEEE80211R + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set low order 256 bits of MSK for key management offload"); + } else { +#endif /* CONFIG_IEEE80211R */ + if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set PMK for key management offload"); +#ifdef CONFIG_IEEE80211R + } +#endif /* CONFIG_IEEE80211R */ +} + + static int wpa_supplicant_get_pmk(struct wpa_sm *sm, const unsigned char *src_addr, const u8 *pmkid) @@ -198,10 +217,13 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; + wpa_supplicant_key_mgmt_set_pmk(sm); if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt)) { sa = pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, + NULL, 0, src_addr, sm->own_addr, sm->network_ctx, sm->key_mgmt); @@ -235,6 +257,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { /* Send EAPOL-Start to trigger full EAP authentication. */ @@ -586,6 +609,10 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, return -1; } + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk1, 0, sizeof(sm->ptk.tk1)); + os_memset(sm->ptk.u.tk2, 0, sizeof(sm->ptk.u.tk2)); + if (sm->wpa_ptk_rekey) { eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, @@ -1178,6 +1205,17 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.gtk) wpa_sm_set_rekey_offload(sm); + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { + struct rsn_pmksa_cache_entry *sa; + + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->ptk.kck, sizeof(sm->ptk.kck), + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } + return; failed: @@ -1242,8 +1280,9 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; + u16 gtk_len; - gd->gtk_len = WPA_GET_BE16(key->key_length); + gtk_len = WPA_GET_BE16(key->key_length); maxkeylen = key_data_len; if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (maxkeylen < 8) { @@ -1255,11 +1294,13 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, - gd->gtk_len, maxkeylen, + if (gtk_len > maxkeylen || + wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, maxkeylen, &gd->key_rsc_len, &gd->alg)) return -1; + gd->gtk_len = gtk_len; gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >> WPA_KEY_INFO_KEY_INDEX_SHIFT; if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) { @@ -1385,6 +1426,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) goto failed; + os_memset(&gd, 0, sizeof(gd)); if (rekey) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " @@ -1403,6 +1445,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, return; failed: + os_memset(&gd, 0, sizeof(gd)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1418,7 +1461,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, os_memcpy(mic, key->key_mic, 16); if (sm->tptk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, + wpa_eapol_key_mic(sm->tptk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1435,7 +1478,7 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, if (!ok && sm->ptk_set) { os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, + wpa_eapol_key_mic(sm->ptk.kck, sm->key_mgmt, ver, buf, len, key->key_mic); if (os_memcmp_const(mic, key->key_mic, 16) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1489,7 +1532,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, os_memset(ek, 0, sizeof(ek)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || - sm->key_mgmt == WPA_KEY_MGMT_OSEN) { + sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) { u8 *buf; if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -1687,6 +1731,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", @@ -1702,6 +1747,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } + if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", + ver); + goto out; + } + #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ @@ -1715,7 +1768,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && - sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + sm->key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); @@ -1724,6 +1778,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " @@ -1743,6 +1798,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " @@ -1870,7 +1926,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 1; out: - os_free(tmp); + bin_clear_free(tmp, data_len); return ret; } @@ -1906,6 +1962,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) WPA_AUTH_KEY_MGMT_CCKM); case WPA_KEY_MGMT_WPA_NONE: return WPA_AUTH_KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; default: return 0; } @@ -1963,7 +2021,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; len = ret; @@ -1990,7 +2048,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); - if (ret >= 0 && (size_t) ret < buflen) + if (!os_snprintf_error(buflen - len, ret)) len += ret; return (int) len; @@ -2088,6 +2146,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->assoc_wpa_ie); os_free(sm->ap_wpa_ie); os_free(sm->ap_rsn_ie); + wpa_sm_drop_sa(sm); os_free(sm->ctx); peerkey_deinit(sm); #ifdef CONFIG_IEEE80211R @@ -2176,6 +2235,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ + + /* Keys are not needed in the WPA state machine anymore */ + wpa_sm_drop_sa(sm); } @@ -2184,10 +2246,12 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) * @sm: Pointer to WPA state machine data from wpa_sm_init() * @pmk: The new PMK * @pmk_len: The length of the new PMK in bytes + * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK * * Configure the PMK for WPA state machine. */ -void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid) { if (sm == NULL) return; @@ -2200,6 +2264,12 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) sm->xxkey_len = pmk_len; os_memcpy(sm->xxkey, pmk, pmk_len); #endif /* CONFIG_IEEE80211R */ + + if (bssid) { + pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, + bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } } @@ -2424,7 +2494,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2437,7 +2507,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, ret = os_snprintf(pos, end - pos, "pmf=%d\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -2640,7 +2710,6 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) } -#ifdef CONFIG_TESTING_OPTIONS void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); @@ -2649,8 +2718,12 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); +#ifdef CONFIG_IEEE80211R + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); +#endif /* CONFIG_IEEE80211R */ } -#endif /* CONFIG_TESTING_OPTIONS */ int wpa_sm_has_ptk(struct wpa_sm *sm) @@ -2783,3 +2856,30 @@ int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) } #endif /* CONFIG_P2P */ + + +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) +{ + if (rx_replay_counter == NULL) + return; + + os_memcpy(sm->rx_replay_counter, rx_replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + wpa_printf(MSG_DEBUG, "Updated key replay counter"); +} + + +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, + const u8 *ptk_kek) +{ + if (ptk_kck) { + os_memcpy(sm->ptk.kck, ptk_kck, 16); + wpa_printf(MSG_DEBUG, "Updated PTK KCK"); + } + if (ptk_kek) { + os_memcpy(sm->ptk.kek, ptk_kek, 16); + wpa_printf(MSG_DEBUG, "Updated PTK KEK"); + } + sm->ptk_set = 1; +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 63032b02..cc128935 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -17,6 +17,7 @@ struct wpa_sm; struct eapol_sm; struct wpa_config_blob; +struct hostapd_freq_params; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ @@ -51,7 +52,7 @@ struct wpa_sm_ctx { int (*mark_authenticated)(void *ctx, const u8 *target_ap); #ifdef CONFIG_TDLS int (*tdls_get_capa)(void *ctx, int *tdls_supported, - int *tdls_ext_setup); + int *tdls_ext_setup, int *tdls_chan_switch); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capab, @@ -67,9 +68,14 @@ struct wpa_sm_ctx { size_t supp_channels_len, const u8 *supp_oper_classes, size_t supp_oper_classes_len); + int (*tdls_enable_channel_switch)( + void *ctx, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params); + int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr); #endif /* CONFIG_TDLS */ void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck, const u8 *replay_ctr); + int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); }; @@ -105,7 +111,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); -void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid); void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth); void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx); @@ -147,6 +154,10 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, + const u8 *ptk_kek); + #else /* CONFIG_NO_WPA */ static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) @@ -301,6 +312,16 @@ static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, { } +static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, + const u8 *rx_replay_counter) +{ +} + +static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, + const u8 *ptk_kek) +{ +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY @@ -388,6 +409,10 @@ void wpa_tdls_enable(struct wpa_sm *sm, int enabled); void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr); const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); int wpa_tdls_is_external_setup(struct wpa_sm *sm); +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params); +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 839b545b..07f3692c 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -92,6 +92,7 @@ struct wpa_sm { #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; + int tdls_chan_switch_prohibited; int tdls_disabled; /* The driver supports TDLS */ @@ -102,6 +103,9 @@ struct wpa_sm { * to it via tdls_mgmt. */ int tdls_external_setup; + + /* The driver supports TDLS channel switching */ + int tdls_chan_switch; #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R @@ -257,11 +261,12 @@ static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) #ifdef CONFIG_TDLS static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, int *tdls_supported, - int *tdls_ext_setup) + int *tdls_ext_setup, + int *tdls_chan_switch) { if (sm->ctx->tdls_get_capa) return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, - tdls_ext_setup); + tdls_ext_setup, tdls_chan_switch); return -1; } @@ -310,8 +315,38 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, supp_oper_classes_len); return -1; } + +static inline int +wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + const struct hostapd_freq_params *freq_params) +{ + if (sm->ctx->tdls_enable_channel_switch) + return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr, + oper_class, + freq_params); + return -1; +} + +static inline int +wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->ctx->tdls_disable_channel_switch) + return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr); + return -1; +} #endif /* CONFIG_TDLS */ +static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, + const u8 *pmk, size_t pmk_len) +{ + if (!sm->proactive_key_caching) + return 0; + if (!sm->ctx->key_mgmt_set_pmk) + return -1; + return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); +} + void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index 93e8cf69..51876eda 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -173,6 +173,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); diff --git a/src/tls/asn1.c b/src/tls/asn1.c index 97462fac..cec10929 100644 --- a/src/tls/asn1.c +++ b/src/tls/asn1.c @@ -166,7 +166,7 @@ void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, "%s%lu", i == 0 ? "" : ".", oid->oid[i]); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) break; pos += ret; } diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 4a4f0b69..facdd659 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -570,8 +570,26 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; - case TLS_DH_anon_WITH_AES_128_CBC_SHA256: - cipher = "ADH-AES-128-SHA256"; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; @@ -579,15 +597,30 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf, case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_256_CBC_SHA256: - cipher = "AES-256-SHA256"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; break; case TLS_RSA_WITH_AES_128_CBC_SHA256: cipher = "AES-128-SHA256"; break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; + break; default: return -1; } diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 4f08e0f9..9ce96803 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -440,6 +440,7 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, const u8 *pos, *end, *server_params, *server_params_end; u8 alert; unsigned int bits; + u16 val; tlsv1_client_free_dh(conn); @@ -449,13 +450,13 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; server_params = pos; - conn->dh_p_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) { - wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu", - (unsigned long) conn->dh_p_len); + if (val == 0 || val > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val); goto fail; } + conn->dh_p_len = val; bits = count_bits(pos, conn->dh_p_len); if (bits < 768) { wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)", @@ -474,10 +475,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_g_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; + conn->dh_g_len = val; conn->dh_g = os_malloc(conn->dh_g_len); if (conn->dh_g == NULL) goto fail; @@ -490,10 +492,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (end - pos < 3) goto fail; - conn->dh_ys_len = WPA_GET_BE16(pos); + val = WPA_GET_BE16(pos); pos += 2; - if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len) + if (val == 0 || val > (size_t) (end - pos)) goto fail; + conn->dh_ys_len = val; conn->dh_ys = os_malloc(conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index 23d0b815..93ae4888 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -516,14 +516,56 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf, case TLS_RSA_WITH_3DES_EDE_CBC_SHA: cipher = "DES-CBC3-SHA"; break; + case TLS_DHE_RSA_WITH_DES_CBC_SHA: + cipher = "DHE-RSA-DES-CBC-SHA"; + break; + case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: + cipher = "DHE-RSA-DES-CBC3-SHA"; + break; + case TLS_DH_anon_WITH_RC4_128_MD5: + cipher = "ADH-RC4-MD5"; + break; + case TLS_DH_anon_WITH_DES_CBC_SHA: + cipher = "ADH-DES-SHA"; + break; + case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA: + cipher = "ADH-DES-CBC3-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA: + cipher = "AES-128-SHA"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: + cipher = "DHE-RSA-AES-128-SHA"; + break; case TLS_DH_anon_WITH_AES_128_CBC_SHA: cipher = "ADH-AES-128-SHA"; break; case TLS_RSA_WITH_AES_256_CBC_SHA: cipher = "AES-256-SHA"; break; - case TLS_RSA_WITH_AES_128_CBC_SHA: - cipher = "AES-128-SHA"; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: + cipher = "DHE-RSA-AES-256-SHA"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA: + cipher = "ADH-AES-256-SHA"; + break; + case TLS_RSA_WITH_AES_128_CBC_SHA256: + cipher = "AES-128-SHA256"; + break; + case TLS_RSA_WITH_AES_256_CBC_SHA256: + cipher = "AES-256-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: + cipher = "DHE-RSA-AES-128-SHA256"; + break; + case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: + cipher = "DHE-RSA-AES-256-SHA256"; + break; + case TLS_DH_anon_WITH_AES_128_CBC_SHA256: + cipher = "ADH-AES-128-SHA256"; + break; + case TLS_DH_anon_WITH_AES_256_CBC_SHA256: + cipher = "ADH-AES-256-SHA256"; break; default: return -1; diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c index 728e1372..310966c2 100644 --- a/src/tls/tlsv1_server_read.c +++ b/src/tls/tlsv1_server_read.c @@ -626,7 +626,7 @@ static int tls_process_client_key_exchange_dh( dh_yc_len = WPA_GET_BE16(pos); dh_yc = pos + 2; - if (dh_yc + dh_yc_len > end) { + if (dh_yc_len > end - dh_yc) { tlsv1_server_log(conn, "Client public value overflow (length %d)", dh_yc_len); tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index e1e4df8d..742af328 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -512,7 +512,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) ret = os_snprintf(pos, end - pos, "%s=%s, ", x509_name_attr_str(name->attr[i].type), name->attr[i].value); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } @@ -527,7 +527,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) if (name->email) { ret = os_snprintf(pos, end - pos, "/emailAddress=%s", name->email); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) goto done; pos += ret; } diff --git a/src/utils/base64.c b/src/utils/base64.c index af1307fc..d44f290e 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -48,9 +48,11 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, pos = out; line_len = 0; while (end - in >= 3) { - *pos++ = base64_table[in[0] >> 2]; - *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; - *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; + *pos++ = base64_table[(in[0] >> 2) & 0x3f]; + *pos++ = base64_table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = base64_table[(((in[1] & 0x0f) << 2) | + (in[2] >> 6)) & 0x3f]; *pos++ = base64_table[in[2] & 0x3f]; in += 3; line_len += 4; @@ -61,14 +63,14 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, } if (end - in) { - *pos++ = base64_table[in[0] >> 2]; + *pos++ = base64_table[(in[0] >> 2) & 0x3f]; if (end - in == 1) { - *pos++ = base64_table[(in[0] & 0x03) << 4]; + *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; *pos++ = '='; } else { - *pos++ = base64_table[((in[0] & 0x03) << 4) | - (in[1] >> 4)]; - *pos++ = base64_table[(in[1] & 0x0f) << 2]; + *pos++ = base64_table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; } *pos++ = '='; line_len += 4; diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c index d5ff5b5c..9ce1a5cb 100644 --- a/src/utils/browser-android.c +++ b/src/utils/browser-android.c @@ -64,24 +64,15 @@ static void http_req(void *ctx, struct http_request *req) int hs20_web_browser(const char *url) { - char cmd[2000]; - int ret; struct http_server *http; struct in_addr addr; struct browser_data data; + pid_t pid; wpa_printf(MSG_INFO, "Launching Android browser to %s", url); os_memset(&data, 0, sizeof(data)); - ret = os_snprintf(cmd, sizeof(cmd), - "start -a android.intent.action.VIEW -d %s " - "-n com.android.browser/.BrowserActivity", url); - if (ret < 0 || (size_t) ret >= sizeof(cmd)) { - wpa_printf(MSG_ERROR, "Too long URL"); - return -1; - } - if (eloop_init() < 0) { wpa_printf(MSG_ERROR, "eloop_init failed"); return -1; @@ -94,14 +85,34 @@ int hs20_web_browser(const char *url) return -1; } - if (os_exec("/system/bin/am", cmd, 1) != 0) { - wpa_printf(MSG_INFO, "Failed to launch Android browser"); - eloop_cancel_timeout(browser_timeout, NULL, NULL); + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); http_server_deinit(http); eloop_destroy(); return -1; } + if (pid == 0) { + /* run the external command in the child process */ + char *argv[9]; + + argv[0] = "browser-android"; + argv[1] = "start"; + argv[2] = "-a"; + argv[3] = "android.intent.action.VIEW"; + argv[4] = "-d"; + argv[5] = (void *) url; + argv[6] = "-n"; + argv[7] = "com.android.browser/.BrowserActivity"; + argv[8] = NULL; + + execv("/system/bin/am", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + eloop_register_timeout(30, 0, browser_timeout, &data, NULL); eloop_run(); eloop_cancel_timeout(browser_timeout, &data, NULL); @@ -109,7 +120,7 @@ int hs20_web_browser(const char *url) eloop_destroy(); wpa_printf(MSG_INFO, "Closing Android browser"); - if (os_exec("/system/bin/input", "keyevent 3", 1) != 0) { + if (system("/system/bin/input keyevent KEYCODE_HOME") != 0) { wpa_printf(MSG_INFO, "Failed to inject keyevent"); } diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c index a080e2cb..aed39706 100644 --- a/src/utils/browser-system.c +++ b/src/utils/browser-system.c @@ -64,22 +64,15 @@ static void http_req(void *ctx, struct http_request *req) int hs20_web_browser(const char *url) { - char cmd[2000]; - int ret; struct http_server *http; struct in_addr addr; struct browser_data data; + pid_t pid; - wpa_printf(MSG_INFO, "Launching Android browser to %s", url); + wpa_printf(MSG_INFO, "Launching system browser to %s", url); os_memset(&data, 0, sizeof(data)); - ret = os_snprintf(cmd, sizeof(cmd), "x-www-browser '%s' &", url); - if (ret < 0 || (size_t) ret >= sizeof(cmd)) { - wpa_printf(MSG_ERROR, "Too long URL"); - return -1; - } - if (eloop_init() < 0) { wpa_printf(MSG_ERROR, "eloop_init failed"); return -1; @@ -92,14 +85,28 @@ int hs20_web_browser(const char *url) return -1; } - if (os_exec("/usr/bin/x-www-browser", url, 0) != 0) { - wpa_printf(MSG_INFO, "Failed to launch browser"); - eloop_cancel_timeout(browser_timeout, NULL, NULL); + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); http_server_deinit(http); eloop_destroy(); return -1; } + if (pid == 0) { + /* run the external command in the child process */ + char *argv[3]; + + argv[0] = "browser-system"; + argv[1] = (void *) url; + argv[2] = NULL; + + execv("/usr/bin/x-www-browser", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + eloop_register_timeout(120, 0, browser_timeout, &data, NULL); eloop_run(); eloop_cancel_timeout(browser_timeout, &data, NULL); diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c index ce3054bb..5fc40fac 100644 --- a/src/utils/browser-wpadebug.c +++ b/src/utils/browser-wpadebug.c @@ -65,26 +65,15 @@ static void http_req(void *ctx, struct http_request *req) int hs20_web_browser(const char *url) { - char cmd[2000]; - int ret; struct http_server *http; struct in_addr addr; struct browser_data data; + pid_t pid; wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url); os_memset(&data, 0, sizeof(data)); - ret = os_snprintf(cmd, sizeof(cmd), - "start -a android.action.MAIN " - "-c android.intent.category.LAUNCHER " - "-n w1.fi.wpadebug/.WpaWebViewActivity " - "-e w1.fi.wpadebug.URL '%s'", url); - if (ret < 0 || (size_t) ret >= sizeof(cmd)) { - wpa_printf(MSG_ERROR, "Too long URL"); - return -1; - } - if (eloop_init() < 0) { wpa_printf(MSG_ERROR, "eloop_init failed"); return -1; @@ -97,14 +86,37 @@ int hs20_web_browser(const char *url) return -1; } - if (os_exec("/system/bin/am", cmd, 1) != 0) { - wpa_printf(MSG_INFO, "Failed to launch wpadebug browser"); - eloop_cancel_timeout(browser_timeout, NULL, NULL); + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); http_server_deinit(http); eloop_destroy(); return -1; } + if (pid == 0) { + /* run the external command in the child process */ + char *argv[12]; + + argv[0] = "browser-wpadebug"; + argv[1] = "start"; + argv[2] = "-a"; + argv[3] = "android.action.MAIN"; + argv[4] = "-c"; + argv[5] = "android.intent.category.LAUNCHER"; + argv[6] = "-n"; + argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity"; + argv[8] = "-e"; + argv[9] = "w1.fi.wpadebug.URL"; + argv[10] = (void *) url; + argv[11] = NULL; + + execv("/system/bin/am", argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + exit(0); + return -1; + } + eloop_register_timeout(300, 0, browser_timeout, &data, NULL); eloop_run(); eloop_cancel_timeout(browser_timeout, &data, NULL); diff --git a/src/utils/common.c b/src/utils/common.c index 99020049..182c6a8a 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -183,6 +183,35 @@ void wpa_get_ntp_timestamp(u8 *buf) os_memcpy(buf + 4, (u8 *) &tmp, 4); } +/** + * wpa_scnprintf - Simpler-to-use snprintf function + * @buf: Output buffer + * @size: Buffer size + * @fmt: format + * + * Simpler snprintf version that doesn't require further error checks - the + * return value only indicates how many bytes were actually written, excluding + * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough). + */ +int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list ap; + int ret; + + if (!size) + return 0; + + va_start(ap, fmt); + ret = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (ret < 0) + return 0; + if ((size_t) ret >= size) + return size - 1; + + return ret; +} static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len, int uppercase) @@ -195,7 +224,7 @@ static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, for (i = 0; i < len; i++) { ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", data[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return pos - buf; } @@ -578,21 +607,6 @@ int is_hex(const u8 *data, size_t len) } -int find_first_bit(u32 value) -{ - int pos = 0; - - while (value) { - if (value & 0x1) - return pos; - value >>= 1; - pos++; - } - - return -1; -} - - size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len) @@ -726,7 +740,7 @@ char * freq_range_list_str(const struct wpa_freq_range_list *list) res = os_snprintf(pos, end - pos, "%s%u-%u", i == 0 ? "" : ",", range->min, range->max); - if (res < 0 || res > end - pos) { + if (os_snprintf_error(end - pos, res)) { os_free(buf); return NULL; } @@ -866,3 +880,35 @@ int random_mac_addr_keep_oui(u8 *addr) addr[0] |= 0x02; /* locally administered */ return 0; } + + +/** + * str_token - Get next token from a string + * @buf: String to tokenize. Note that the string might be modified. + * @delim: String of delimiters + * @context: Pointer to save our context. Should be initialized with + * NULL on the first call, and passed for any further call. + * Returns: The next token, NULL if there are no more valid tokens. + */ +char * str_token(char *str, const char *delim, char **context) +{ + char *end, *pos = str; + + if (*context) + pos = *context; + + while (*pos && os_strchr(delim, *pos)) + pos++; + if (!*pos) + return NULL; + + end = pos + 1; + while (*end && !os_strchr(delim, *end)) + end++; + + if (*end) + *end++ = '\0'; + + *context = end; + return pos; +} diff --git a/src/utils/common.h b/src/utils/common.h index 14d9ad1e..7eca4095 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -329,6 +329,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val) #ifndef ETH_ALEN #define ETH_ALEN 6 #endif +#ifndef ETH_HLEN +#define ETH_HLEN 14 +#endif #ifndef IFNAMSIZ #define IFNAMSIZ 16 #endif @@ -474,6 +477,7 @@ int hex2byte(const char *hex); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); +int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...); int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len); int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, size_t len); @@ -493,7 +497,6 @@ const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len); char * wpa_config_parse_string(const char *value, size_t *len); int is_hex(const u8 *data, size_t len); -int find_first_bit(u32 value); size_t merge_byte_arrays(u8 *res, size_t res_len, const u8 *src1, size_t src1_len, const u8 *src2, size_t src2_len); @@ -534,13 +537,14 @@ void int_array_add_unique(int **res, int a); #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - void str_clear_free(char *str); void bin_clear_free(void *bin, size_t len); int random_mac_addr(u8 *addr); int random_mac_addr_keep_oui(u8 *addr); +char * str_token(char *str, const char *delim, char **context); + /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/os.h b/src/utils/os.h index b9247d89..77250d63 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -549,6 +549,12 @@ char * os_strdup(const char *s); #endif /* OS_NO_C_LIB_DEFINES */ +static inline int os_snprintf_error(size_t size, int res) +{ + return res < 0 || (unsigned int) res >= size; +} + + static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) { if (size && nmemb > (~(size_t) 0) / size) diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c index 90b6688a..77733ad9 100644 --- a/src/utils/os_internal.c +++ b/src/utils/os_internal.c @@ -17,9 +17,11 @@ */ #include "includes.h" +#include <time.h> +#include <sys/wait.h> #undef OS_REJECT_C_LIB_FUNCTIONS -#include "os.h" +#include "common.h" void os_sleep(os_time_t sec, os_time_t usec) { @@ -96,7 +98,7 @@ int os_gmtime(os_time_t t, struct os_tm *tm) int os_daemonize(const char *pid_file) { if (daemon(0, 0)) { - perror("daemon"); + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); return -1; } @@ -167,8 +169,8 @@ char * os_rel2abs_path(const char *rel_path) } } - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; ret = os_malloc(ret_len); if (ret) { @@ -506,3 +508,57 @@ int os_snprintf(char *str, size_t size, const char *format, ...) str[size - 1] = '\0'; return ret; } + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + pid_t pid; + int pid_status; + + pid = fork(); + if (pid < 0) { + wpa_printf(MSG_ERROR, "fork: %s", strerror(errno)); + return -1; + } + + if (pid == 0) { + /* run the external command in the child process */ + const int MAX_ARG = 30; + char *_program, *_arg, *pos; + char *argv[MAX_ARG + 1]; + int i; + + _program = os_strdup(program); + _arg = os_strdup(arg); + + argv[0] = _program; + + i = 1; + pos = _arg; + while (i < MAX_ARG && pos && *pos) { + while (*pos == ' ') + pos++; + if (*pos == '\0') + break; + argv[i++] = pos; + pos = os_strchr(pos, ' '); + if (pos) + *pos++ = '\0'; + } + argv[i] = NULL; + + execv(program, argv); + wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + os_free(_program); + os_free(_arg); + exit(0); + return -1; + } + + if (wait_completion) { + /* wait for the child process to complete in the parent */ + waitpid(pid, &pid_status, 0); + } + + return 0; +} diff --git a/src/utils/os_none.c b/src/utils/os_none.c index 26491115..83fe0251 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -234,3 +234,9 @@ int os_snprintf(char *str, size_t size, const char *format, ...) return 0; } #endif /* OS_NO_C_LIB_DEFINES */ + + +int os_exec(const char *program, const char *arg, int wait_completion) +{ + return -1; +} diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c index d955dc4e..6f5ea939 100644 --- a/src/utils/pcsc_funcs.c +++ b/src/utils/pcsc_funcs.c @@ -281,77 +281,82 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len, wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template", pos, end - pos); - while (pos + 1 < end) { + while (end - pos >= 2) { + unsigned char type, len; + + type = pos[0]; + len = pos[1]; wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d", - pos[0], pos[1]); - if (pos + 2 + pos[1] > end) + type, len); + pos += 2; + + if (len > (unsigned int) (end - pos)) break; - switch (pos[0]) { + switch (type) { case USIM_TLV_FILE_DESC: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_DF_NAME: wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_PROPR_INFO: wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary " - "information TLV", pos + 2, pos[1]); + "information TLV", pos, len); break; case USIM_TLV_LIFE_CYCLE_STATUS: wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status " - "Integer TLV", pos + 2, pos[1]); + "Integer TLV", pos, len); break; case USIM_TLV_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV", - pos + 2, pos[1]); - if ((pos[1] == 1 || pos[1] == 2) && file_len) { - if (pos[1] == 1) - *file_len = (int) pos[2]; + pos, len); + if ((len == 1 || len == 2) && file_len) { + if (len == 1) + *file_len = (int) pos[0]; else - *file_len = ((int) pos[2] << 8) | - (int) pos[3]; + *file_len = WPA_GET_BE16(pos); wpa_printf(MSG_DEBUG, "SCARD: file_size=%d", *file_len); } break; case USIM_TLV_TOTAL_FILE_SIZE: wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV", - pos + 2, pos[1]); + pos, len); break; case USIM_TLV_PIN_STATUS_TEMPLATE: wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template " - "DO TLV", pos + 2, pos[1]); - if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG && - pos[3] >= 1 && ps_do) { + "DO TLV", pos, len); + if (len >= 2 && pos[0] == USIM_PS_DO_TAG && + pos[1] >= 1 && ps_do) { wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x", - pos[4]); - *ps_do = (int) pos[4]; + pos[2]); + *ps_do = (int) pos[2]; } break; case USIM_TLV_SHORT_FILE_ID: wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File " - "Identifier (SFI) TLV", pos + 2, pos[1]); + "Identifier (SFI) TLV", pos, len); break; case USIM_TLV_SECURITY_ATTR_8B: case USIM_TLV_SECURITY_ATTR_8C: case USIM_TLV_SECURITY_ATTR_AB: wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute " - "TLV", pos + 2, pos[1]); + "TLV", pos, len); break; default: wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV", - pos, 2 + pos[1]); + pos, len); break; } - pos += 2 + pos[1]; + pos += len; if (pos == end) return 0; @@ -397,10 +402,12 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, unsigned char rid[5]; unsigned char appl_code[2]; /* 0x1002 for 3G USIM */ } *efdir; - unsigned char buf[127]; + unsigned char buf[127], *aid_pos; size_t blen; + unsigned int aid_len = 0; efdir = (struct efdir *) buf; + aid_pos = &buf[4]; blen = sizeof(buf); if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) { wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR"); @@ -449,14 +456,15 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, continue; } - if (efdir->aid_len < 1 || efdir->aid_len > 16) { - wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d", - efdir->aid_len); + aid_len = efdir->aid_len; + if (aid_len < 1 || aid_len > 16) { + wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u", + aid_len); continue; } wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record", - efdir->rid, efdir->aid_len); + aid_pos, aid_len); if (efdir->appl_code[0] == 0x10 && efdir->appl_code[1] == 0x02) { @@ -472,14 +480,14 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid, return -1; } - if (efdir->aid_len > maxlen) { + if (aid_len > maxlen) { wpa_printf(MSG_DEBUG, "SCARD: Too long AID"); return -1; } - os_memcpy(aid, efdir->rid, efdir->aid_len); + os_memcpy(aid, aid_pos, aid_len); - return efdir->aid_len; + return aid_len; } @@ -1096,7 +1104,7 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len) } if (scard->sim_type == SCARD_GSM_SIM) { - blen = (buf[2] << 8) | buf[3]; + blen = WPA_GET_BE16(&buf[2]); } else { int file_size; if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) @@ -1170,7 +1178,7 @@ int scard_get_mnc_len(struct scard_data *scard) } if (scard->sim_type == SCARD_GSM_SIM) { - file_size = (buf[2] << 8) | buf[3]; + file_size = WPA_GET_BE16(&buf[2]); } else { if (scard_parse_fsp_templ(buf, blen, NULL, &file_size)) return -3; diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c index 197a4af9..f8f815a8 100644 --- a/src/utils/radiotap.c +++ b/src/utils/radiotap.c @@ -109,6 +109,7 @@ int ieee80211_radiotap_iterator_init( iterator->_arg_index = 0; iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); + iterator->_next_ns_data = NULL; iterator->_reset_on_ext = 0; iterator->_next_bitmap = &radiotap_header->it_present; iterator->_next_bitmap++; @@ -154,6 +155,8 @@ int ieee80211_radiotap_iterator_init( } iterator->this_arg = iterator->_arg; + iterator->this_arg_index = 0; + iterator->this_arg_size = 0; /* we are all initialized happily */ diff --git a/src/utils/trace.c b/src/utils/trace.c index 6044f5f7..7403c08f 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -33,7 +33,7 @@ static void get_prg_fname(void) os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); len = readlink(exe, fname, sizeof(fname) - 1); if (len < 0 || len >= (int) sizeof(fname)) { - perror("readlink"); + wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno)); return; } fname[len] = '\0'; diff --git a/src/utils/uuid.c b/src/utils/uuid.c index 2aa4bcb5..0f224f97 100644 --- a/src/utils/uuid.c +++ b/src/utils/uuid.c @@ -55,7 +55,7 @@ int uuid_bin2str(const u8 *bin, char *str, size_t max_len) bin[4], bin[5], bin[6], bin[7], bin[8], bin[9], bin[10], bin[11], bin[12], bin[13], bin[14], bin[15]); - if (len < 0 || (size_t) len >= max_len) + if (os_snprintf_error(max_len, len)) return -1; return 0; } diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index 68cbace6..0d119051 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -555,6 +555,8 @@ int wpa_debug_open_file(const char *path) #ifndef _WIN32 setvbuf(out_file, NULL, _IOLBF, 0); #endif /* _WIN32 */ +#else /* CONFIG_DEBUG_FILE */ + (void)path; #endif /* CONFIG_DEBUG_FILE */ return 0; } @@ -572,6 +574,14 @@ void wpa_debug_close_file(void) #endif /* CONFIG_DEBUG_FILE */ } + +void wpa_debug_setup_stdout(void) +{ +#ifndef _WIN32 + setvbuf(stdout, NULL, _IOLBF, 0); +#endif /* _WIN32 */ +} + #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -617,7 +627,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...) if (ifname) { int res = os_snprintf(prefix, sizeof(prefix), "%s: ", ifname); - if (res < 0 || res >= (int) sizeof(prefix)) + if (os_snprintf_error(sizeof(prefix), res)) prefix[0] = '\0'; } } diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 391f1975..400bea9e 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -34,6 +34,7 @@ enum { #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0) #define wpa_debug_open_file(p) do { } while (0) #define wpa_debug_close_file() do { } while (0) +#define wpa_debug_setup_stdout() do { } while (0) #define wpa_dbg(args...) do { } while (0) static inline int wpa_debug_reopen_file(void) @@ -46,6 +47,7 @@ static inline int wpa_debug_reopen_file(void) int wpa_debug_open_file(const char *path); int wpa_debug_reopen_file(void); void wpa_debug_close_file(void); +void wpa_debug_setup_stdout(void); /** * wpa_debug_printf_timestamp - Print timestamp for debug output diff --git a/src/wps/wps.c b/src/wps/wps.c index b0f6887c..2c68be8c 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -560,7 +560,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) "wps_state=configured\n"); else ret = 0; - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -568,7 +568,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (attr.ap_setup_locked && *attr.ap_setup_locked) { ret = os_snprintf(pos, end - pos, "wps_ap_setup_locked=1\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -576,7 +576,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) if (attr.selected_registrar && *attr.selected_registrar) { ret = os_snprintf(pos, end - pos, "wps_selected_registrar=1\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -585,7 +585,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "wps_device_password_id=%u\n", WPA_GET_BE16(attr.dev_password_id)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -595,7 +595,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) "wps_selected_registrar_config_methods=" "0x%04x\n", WPA_GET_BE16(attr.sel_reg_config_methods)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -607,7 +607,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) wps_dev_type_bin2str(attr.primary_dev_type, devtype, sizeof(devtype))); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -626,7 +626,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) str[i] = '\0'; ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str); os_free(str); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -635,7 +635,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end) ret = os_snprintf(pos, end - pos, "wps_config_methods=0x%04x\n", WPA_GET_BE16(attr.config_methods)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } diff --git a/src/wps/wps.h b/src/wps/wps.h index 192d2835..0a7f65df 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -819,6 +819,7 @@ int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg, int wps_registrar_add_nfc_password_token(struct wps_registrar *reg, const u8 *oob_dev_pw, size_t oob_dev_pw_len); +void wps_registrar_flush(struct wps_registrar *reg); int wps_build_credential_wrap(struct wpabuf *msg, const struct wps_credential *cred); diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index a282348e..222d4855 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -489,7 +489,7 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, ret = os_snprintf(buf, buf_len, "%u-%08X-%u", WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]), WPA_GET_BE16(&dev_type[6])); - if (ret < 0 || (unsigned int) ret >= buf_len) + if (os_snprintf_error(buf_len, ret)) return NULL; return buf; diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h index f483e2e8..da005a41 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -279,30 +279,71 @@ enum wps_dev_categ { WPS_DEV_DISPLAY = 7, WPS_DEV_MULTIMEDIA = 8, WPS_DEV_GAMING = 9, - WPS_DEV_PHONE = 10 + WPS_DEV_PHONE = 10, + WPS_DEV_AUDIO = 11, }; enum wps_dev_subcateg { WPS_DEV_COMPUTER_PC = 1, WPS_DEV_COMPUTER_SERVER = 2, WPS_DEV_COMPUTER_MEDIA_CENTER = 3, + WPS_DEV_COMPUTER_ULTRA_MOBILE = 4, + WPS_DEV_COMPUTER_NOTEBOOK = 5, + WPS_DEV_COMPUTER_DESKTOP = 6, + WPS_DEV_COMPUTER_MID = 7, + WPS_DEV_COMPUTER_NETBOOK = 8, + WPS_DEV_COMPUTER_TABLET = 9, + WPS_DEV_INPUT_KEYBOARD = 1, + WPS_DEV_INPUT_MOUSE = 2, + WPS_DEV_INPUT_JOYSTICK = 3, + WPS_DEV_INPUT_TRACKBALL = 4, + WPS_DEV_INPUT_GAMING = 5, + WPS_DEV_INPUT_REMOTE = 6, + WPS_DEV_INPUT_TOUCHSCREEN = 7, + WPS_DEV_INPUT_BIOMETRIC_READER = 8, + WPS_DEV_INPUT_BARCODE_READER = 9, WPS_DEV_PRINTER_PRINTER = 1, WPS_DEV_PRINTER_SCANNER = 2, + WPS_DEV_PRINTER_FAX = 3, + WPS_DEV_PRINTER_COPIER = 4, + WPS_DEV_PRINTER_ALL_IN_ONE = 5, WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1, + WPS_DEV_CAMERA_VIDEO = 2, + WPS_DEV_CAMERA_WEB = 3, + WPS_DEV_CAMERA_SECURITY = 4, WPS_DEV_STORAGE_NAS = 1, WPS_DEV_NETWORK_INFRA_AP = 1, WPS_DEV_NETWORK_INFRA_ROUTER = 2, WPS_DEV_NETWORK_INFRA_SWITCH = 3, + WPS_DEV_NETWORK_INFRA_GATEWAY = 4, + WPS_DEV_NETWORK_INFRA_BRIDGE = 5, WPS_DEV_DISPLAY_TV = 1, WPS_DEV_DISPLAY_PICTURE_FRAME = 2, WPS_DEV_DISPLAY_PROJECTOR = 3, + WPS_DEV_DISPLAY_MONITOR = 4, WPS_DEV_MULTIMEDIA_DAR = 1, WPS_DEV_MULTIMEDIA_PVR = 2, WPS_DEV_MULTIMEDIA_MCX = 3, + WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4, + WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5, + WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6, WPS_DEV_GAMING_XBOX = 1, WPS_DEV_GAMING_XBOX360 = 2, WPS_DEV_GAMING_PLAYSTATION = 3, - WPS_DEV_PHONE_WINDOWS_MOBILE = 1 + WPS_DEV_GAMING_GAME_CONSOLE = 4, + WPS_DEV_GAMING_PORTABLE_DEVICE = 5, + WPS_DEV_PHONE_WINDOWS_MOBILE = 1, + WPS_DEV_PHONE_SINGLE_MODE = 2, + WPS_DEV_PHONE_DUAL_MODE = 3, + WPS_DEV_PHONE_SP_SINGLE_MODE = 4, + WPS_DEV_PHONE_SP_DUAL_MODE = 5, + WPS_DEV_AUDIO_TUNER_RECV = 1, + WPS_DEV_AUDIO_SPEAKERS = 2, + WPS_DEV_AUDIO_PMP = 3, + WPS_DEV_AUDIO_HEADSET = 4, + WPS_DEV_AUDIO_HEADPHONES = 5, + WPS_DEV_AUDIO_MICROPHONE = 6, + WPS_DEV_AUDIO_HOME_THEATRE = 7, }; diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index b90cc25e..8ee1ea98 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -676,6 +676,22 @@ wps_registrar_init(struct wps_context *wps, } +void wps_registrar_flush(struct wps_registrar *reg) +{ + if (reg == NULL) + return; + wps_free_pins(®->pins); + wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); + wps_free_pbc_sessions(reg->pbc_sessions); + reg->pbc_sessions = NULL; + wps_free_devices(reg->devices); + reg->devices = NULL; +#ifdef WPS_WORKAROUNDS + reg->pbc_ignore_start.sec = 0; +#endif /* WPS_WORKAROUNDS */ +} + + /** * wps_registrar_deinit - Deinitialize WPS Registrar data * @reg: Registrar data from wps_registrar_init() @@ -686,11 +702,8 @@ void wps_registrar_deinit(struct wps_registrar *reg) return; eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); - wps_free_pins(®->pins); - wps_free_nfc_pw_tokens(®->nfc_pw_tokens, 0); - wps_free_pbc_sessions(reg->pbc_sessions); + wps_registrar_flush(reg); wpabuf_free(reg->extra_cred); - wps_free_devices(reg->devices); os_free(reg); } @@ -3495,7 +3508,7 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, d->dev.model_name ? d->dev.model_name : "", d->dev.model_number ? d->dev.model_number : "", d->dev.serial_number ? d->dev.serial_number : ""); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index ae94a9f6..933d7340 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -251,13 +251,16 @@ void format_date(struct wpabuf *buf) * use for constructing UUIDs for subscriptions. Presumably any method from * rfc4122 is good enough; I've chosen random number method. */ -static void uuid_make(u8 uuid[UUID_LEN]) +static int uuid_make(u8 uuid[UUID_LEN]) { - os_get_random(uuid, UUID_LEN); + if (os_get_random(uuid, UUID_LEN) < 0) + return -1; /* Replace certain bits as specified in rfc4122 or X.667 */ uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */ uuid[8] &= 0x3f; uuid[8] |= 0x80; + + return 0; } @@ -700,10 +703,12 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { s = dl_list_first(&sm->subscriptions, struct subscription, list); - wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " - "trashing oldest"); - dl_list_del(&s->list); - subscription_destroy(s); + if (s) { + wpa_printf(MSG_INFO, + "WPS UPnP: Too many subscriptions, trashing oldest"); + dl_list_del(&s->list); + subscription_destroy(s); + } } s = os_zalloc(sizeof(*s)); @@ -714,7 +719,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, s->sm = sm; s->timeout_time = expire; - uuid_make(s->uuid); + if (uuid_make(s->uuid) < 0) { + subscription_destroy(s); + return NULL; + } subscr_addr_list_create(s, callback_urls); if (dl_list_empty(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in " diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c index 098571ce..26a740d2 100644 --- a/src/wps/wps_upnp_ssdp.c +++ b/src/wps/wps_upnp_ssdp.c @@ -317,7 +317,8 @@ static void advertisement_state_machine_handler(void *eloop_data, * (see notes above) */ next_timeout_msec = 0; - os_get_random((void *) &r, sizeof(r)); + if (os_get_random((void *) &r, sizeof(r)) < 0) + r = 32768; next_timeout_sec = UPNP_CACHE_SEC / 4 + (((UPNP_CACHE_SEC / 4) * r) >> 16); sm->advertise_count++; diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 43c3eed2..7d7f1b6c 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -10,8 +10,6 @@ PKG_CONFIG ?= pkg-config ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),) CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y -else - CONFIG_DRIVER_TEST := y endif include $(LOCAL_PATH)/android.config @@ -85,6 +83,7 @@ OBJS += eap_register.c OBJS += src/utils/common.c OBJS += src/utils/wpa_debug.c OBJS += src/utils/wpabuf.c +OBJS += wmm_ac.c OBJS_p = wpa_passphrase.c OBJS_p += src/utils/common.c OBJS_p += src/utils/wpa_debug.c @@ -184,6 +183,12 @@ ifdef CONFIG_NO_SCAN_PROCESSING L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING endif +ifdef CONFIG_SUITEB +L_CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + ifdef CONFIG_IEEE80211W L_CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -193,9 +198,22 @@ endif ifdef CONFIG_IEEE80211R L_CFLAGS += -DCONFIG_IEEE80211R OBJS += src/rsn_supp/wpa_ft.c +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_MESH NEED_80211_COMMON=y NEED_SHA256=y +NEED_AES_SIV=y NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +L_CFLAGS += -DCONFIG_MESH +OBJS += mesh.c +OBJS += mesh_mpm.c +OBJS += mesh_rsn.c endif ifdef CONFIG_SAE @@ -263,7 +281,6 @@ OBJS += src/utils/bitfield.c L_CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y -NEED_80211_COMMON=y CONFIG_WPS=y CONFIG_AP=y ifdef CONFIG_P2P_STRICT @@ -329,6 +346,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd) LIBS += -lpcap endif +ifdef CONFIG_ERP +L_CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + ifdef CONFIG_EAP_TLS # EAP-TLS ifeq ($(CONFIG_EAP_TLS), dyn) @@ -635,7 +658,6 @@ CONFIG_IEEE8021X_EAPOL=y NEED_DH_GROUPS=y NEED_SHA256=y NEED_BASE64=y -NEED_80211_COMMON=y NEED_AES_CBC=y NEED_MODEXP=y @@ -744,7 +766,6 @@ endif endif ifdef CONFIG_AP -NEED_80211_COMMON=y NEED_EAP_COMMON=y NEED_RSN_AUTHENTICATOR=y L_CFLAGS += -DCONFIG_AP @@ -768,6 +789,7 @@ OBJS += src/ap/ieee802_11_shared.c OBJS += src/ap/drv_callbacks.c OBJS += src/ap/ap_drv_ops.c OBJS += src/ap/beacon.c +OBJS += src/ap/bss_load.c OBJS += src/ap/eap_user_db.c ifdef CONFIG_IEEE80211N OBJS += src/ap/ieee802_11_ht.c @@ -1149,6 +1171,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += src/crypto/aes-internal-enc.c endif endif +ifdef NEED_AES_SIV +AESOBJS += src/crypto/aes-siv.c +endif ifdef NEED_AES OBJS += $(AESOBJS) endif @@ -1224,6 +1249,9 @@ endif ifdef NEED_TLS_PRF_SHA256 SHA256OBJS += src/crypto/sha256-tlsprf.c endif +ifdef NEED_HMAC_SHA256_KDF +SHA256OBJS += src/crypto/sha256-kdf.c +endif OBJS += $(SHA256OBJS) endif @@ -1368,14 +1396,11 @@ OBJS += src/utils/base64.c endif ifdef NEED_SME -NEED_80211_COMMON=y OBJS += sme.c L_CFLAGS += -DCONFIG_SME endif -ifdef NEED_80211_COMMON OBJS += src/common/ieee802_11_common.c -endif ifdef NEED_EAP_COMMON OBJS += src/eap_common/eap_common.c @@ -1500,26 +1525,6 @@ OBJS_priv += wpa_priv.c ifdef CONFIG_DRIVER_NL80211 OBJS_priv += src/common/ieee802_11_common.c endif -ifdef CONFIG_DRIVER_TEST -OBJS_priv += $(SHA1OBJS) -OBJS_priv += $(MD5OBJS) -ifeq ($(CONFIG_TLS), openssl) -OBJS_priv += src/crypto/crypto_openssl.c -endif -ifeq ($(CONFIG_TLS), gnutls) -OBJS_priv += src/crypto/crypto_gnutls.c -endif -ifeq ($(CONFIG_TLS), nss) -OBJS_priv += src/crypto/crypto_nss.c -endif -ifeq ($(CONFIG_TLS), internal) -ifeq ($(CONFIG_CRYPTO), libtomcrypt) -OBJS_priv += src/crypto/crypto_libtomcrypt.c -else -OBJS_priv += src/crypto/crypto_internal.c -endif -endif -endif # CONFIG_DRIVER_TEST OBJS += src/l2_packet/l2_packet_privsep.c OBJS += src/drivers/driver_privsep.c EXTRA_progs += wpa_priv diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 8f7c23f6..06ba18fd 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -80,6 +80,7 @@ OBJS_p += ../src/utils/wpabuf.o OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o OBJS_c += ../src/utils/wpa_debug.o OBJS_c += ../src/utils/common.o +OBJS += wmm_ac.o ifndef CONFIG_OS ifdef CONFIG_NATIVE_WINDOWS @@ -185,6 +186,12 @@ ifdef CONFIG_NO_SCAN_PROCESSING CFLAGS += -DCONFIG_NO_SCAN_PROCESSING endif +ifdef CONFIG_SUITEB +CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + ifdef CONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -194,9 +201,22 @@ endif ifdef CONFIG_IEEE80211R CFLAGS += -DCONFIG_IEEE80211R OBJS += ../src/rsn_supp/wpa_ft.o +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_MESH NEED_80211_COMMON=y NEED_SHA256=y +NEED_AES_SIV=y NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +CFLAGS += -DCONFIG_MESH +OBJS += mesh.o +OBJS += mesh_mpm.o +OBJS += mesh_rsn.o endif ifdef CONFIG_SAE @@ -264,7 +284,6 @@ OBJS += ../src/utils/bitfield.o CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y -NEED_80211_COMMON=y CONFIG_WPS=y CONFIG_AP=y ifdef CONFIG_P2P_STRICT @@ -329,6 +348,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd) LIBS += -lpcap endif +ifdef CONFIG_ERP +CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + ifdef CONFIG_EAP_TLS # EAP-TLS ifeq ($(CONFIG_EAP_TLS), dyn) @@ -635,7 +660,6 @@ CONFIG_IEEE8021X_EAPOL=y NEED_DH_GROUPS=y NEED_SHA256=y NEED_BASE64=y -NEED_80211_COMMON=y NEED_AES_CBC=y NEED_MODEXP=y @@ -757,7 +781,6 @@ OBJS += ../src/pae/ieee802_1x_secy_ops.o endif ifdef CONFIG_AP -NEED_80211_COMMON=y NEED_EAP_COMMON=y NEED_RSN_AUTHENTICATOR=y CFLAGS += -DCONFIG_AP @@ -781,6 +804,7 @@ OBJS += ../src/ap/ieee802_11_shared.o OBJS += ../src/ap/drv_callbacks.o OBJS += ../src/ap/ap_drv_ops.o OBJS += ../src/ap/beacon.o +OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o @@ -1149,6 +1173,9 @@ else AESOBJS += ../src/crypto/aes-omac1.o endif endif +ifdef NEED_AES_SIV +AESOBJS += ../src/crypto/aes-siv.o +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-wrap.o @@ -1235,6 +1262,9 @@ endif ifdef NEED_TLS_PRF_SHA256 SHA256OBJS += ../src/crypto/sha256-tlsprf.o endif +ifdef NEED_HMAC_SHA256_KDF +OBJS += ../src/crypto/sha256-kdf.o +endif OBJS += $(SHA256OBJS) endif @@ -1386,14 +1416,11 @@ OBJS += ../src/utils/base64.o endif ifdef NEED_SME -NEED_80211_COMMON=y OBJS += sme.o CFLAGS += -DCONFIG_SME endif -ifdef NEED_80211_COMMON OBJS += ../src/common/ieee802_11_common.o -endif ifdef NEED_EAP_COMMON OBJS += ../src/eap_common/eap_common.o @@ -1539,26 +1566,6 @@ OBJS_priv += wpa_priv.o ifdef CONFIG_DRIVER_NL80211 OBJS_priv += ../src/common/ieee802_11_common.o endif -ifdef CONFIG_DRIVER_TEST -OBJS_priv += $(SHA1OBJS) -OBJS_priv += $(MD5OBJS) -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), nss) -OBJS_priv += ../src/crypto/crypto_nss.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 @@ -1588,6 +1595,10 @@ ifeq ($(V), 1) Q= E=true endif +ifeq ($(QUIET), 1) +Q=@ +E=true +endif dynamic_eap_methods: $(EAPDYN) @@ -1680,10 +1691,12 @@ else endif %.service: %.service.in - sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + @$(E) " sed" $< %@.service: %.service.arg.in - sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + @$(E) " sed" $< wpa_supplicant.exe: wpa_supplicant mv -f $< $@ diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config index 3ed734da..8d27bb29 100644 --- a/wpa_supplicant/android.config +++ b/wpa_supplicant/android.config @@ -55,9 +55,6 @@ CONFIG_LIBNL20=y # wpa_supplicant. # CONFIG_USE_NDISUIO=y -# Driver interface for development testing -#CONFIG_DRIVER_TEST=y - # Driver interface for wired Ethernet drivers #CONFIG_DRIVER_WIRED=y diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 7c934988..2ebc7f62 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -75,24 +75,10 @@ no_vht: #endif /* CONFIG_IEEE80211N */ -static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, - struct hostapd_config *conf) +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) { - struct hostapd_bss_config *bss = conf->bss[0]; - - conf->driver = wpa_s->driver; - - os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); - - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, - &conf->channel); - if (conf->hw_mode == NUM_HOSTAPD_MODES) { - wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", - ssid->frequency); - return -1; - } - /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ @@ -155,6 +141,28 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } } #endif /* CONFIG_IEEE80211N */ +} + + +static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) +{ + struct hostapd_bss_config *bss = conf->bss[0]; + + conf->driver = wpa_s->driver; + + os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); + + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); #ifdef CONFIG_P2P if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && @@ -217,7 +225,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; if (ssid->psk_set) { - os_free(bss->ssid.wpa_psk); + bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk)); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) return -1; @@ -317,7 +325,8 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->ssid.security_policy != SECURITY_PLAINTEXT) goto no_wps; if (bss->ssid.security_policy == SECURITY_WPA_PSK && - (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2))) + (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) || + !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ bss->eap_server = 1; @@ -555,6 +564,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; hapd_iface->owner = wpa_s; hapd_iface->drv_flags = wpa_s->drv_flags; + hapd_iface->smps_modes = wpa_s->drv_smps_modes; hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads; hapd_iface->extended_capa = wpa_s->extended_capa; hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask; @@ -629,6 +639,10 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; +#ifdef CONFIG_TESTING_OPTIONS + hapd_iface->bss[i]->ext_eapol_frame_io = + wpa_s->ext_eapol_frame_io; +#endif /* CONFIG_TESTING_OPTIONS */ } os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN); @@ -798,9 +812,14 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, if (pin == NULL) { unsigned int rpin = wps_generate_pin(); ret_len = os_snprintf(buf, buflen, "%08d", rpin); + if (os_snprintf_error(buflen, ret_len)) + return -1; pin = buf; - } else + } else if (buf) { ret_len = os_snprintf(buf, buflen, "%s", pin); + if (os_snprintf_error(buflen, ret_len)) + return -1; + } ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, timeout); @@ -890,7 +909,7 @@ int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, return -1; hapd = wpa_s->ap_iface->bss[0]; ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); - if (ret < 0 || ret >= (int) sizeof(pin_txt)) + if (os_snprintf_error(sizeof(pin_txt), ret)) return -1; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); @@ -975,30 +994,45 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0], - buf, buflen); + return hostapd_ctrl_iface_sta_first(hapd, buf, buflen); } int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr, - buf, buflen); + return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen); } int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr, - buf, buflen); + return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen); } @@ -1044,7 +1078,7 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, wpa_cipher_txt(conf->wpa_group), wpa_key_mgmt_txt(conf->wpa_key_mgmt, conf->wpa)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; return pos - buf; diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 8aa5ffa2..4d80c7a7 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -75,4 +75,9 @@ int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, const struct wpabuf *pw, const u8 *pubkey_hash); +struct hostapd_config; +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf); + #endif /* AP_H */ diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index 6a92b735..a320cc43 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -294,7 +294,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) int ret; ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d", freqs[i]); - if (ret < 0 || ret >= msg + sizeof(msg) - pos) + if (os_snprintf_error(msg + sizeof(msg) - pos, ret)) break; pos += ret; } diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 1de51e5e..17984396 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -306,8 +306,9 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { return bss == wpa_s->current_bss || - os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; + (!is_zero_ether_addr(bss->bssid) && + (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0)); } @@ -620,7 +621,7 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, struct wpa_scan_res *res, struct os_reltime *fetch_time) { - const u8 *ssid, *p2p; + const u8 *ssid, *p2p, *mesh; struct wpa_bss *bss; if (wpa_s->conf->ignore_old_scan_res) { @@ -670,6 +671,11 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, /* TODO: add option for ignoring BSSes we are not interested in * (to save memory) */ + + mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID); + if (mesh && mesh[1] <= 32) + ssid = mesh; + bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); if (bss == NULL) bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index b7f259b6..4ebf6843 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -225,7 +225,7 @@ static char * wpa_config_write_int(const struct parse_data *data, if (value == NULL) return NULL; res = os_snprintf(value, 20, "%d", *src); - if (res < 0 || res >= 20) { + if (os_snprintf_error(20, res)) { os_free(value); return NULL; } @@ -270,7 +270,7 @@ static char * wpa_config_write_bssid(const struct parse_data *data, if (value == NULL) return NULL; res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid)); - if (res < 0 || res >= 20) { + if (os_snprintf_error(20, res)) { os_free(value); return NULL; } @@ -358,9 +358,15 @@ static char * wpa_config_write_psk(const struct parse_data *data, if (ssid->ext_psk) { size_t len = 4 + os_strlen(ssid->ext_psk) + 1; char *buf = os_malloc(len); + int res; + if (buf == NULL) return NULL; - os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + if (os_snprintf_error(len, res)) { + os_free(buf); + buf = NULL; + } return buf; } #endif /* CONFIG_EXT_PASSWORD */ @@ -446,7 +452,7 @@ static char * wpa_config_write_proto(const struct parse_data *data, if (ssid->proto & WPA_PROTO_WPA) { ret = os_snprintf(pos, end - pos, "%sWPA", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -454,7 +460,7 @@ static char * wpa_config_write_proto(const struct parse_data *data, if (ssid->proto & WPA_PROTO_RSN) { ret = os_snprintf(pos, end - pos, "%sRSN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -462,7 +468,7 @@ static char * wpa_config_write_proto(const struct parse_data *data, if (ssid->proto & WPA_PROTO_OSEN) { ret = os_snprintf(pos, end - pos, "%sOSEN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -535,6 +541,8 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "OSEN") == 0) val |= WPA_KEY_MGMT_OSEN; #endif /* CONFIG_HS20 */ + else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -574,7 +582,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sWPA-PSK", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -584,7 +592,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sWPA-EAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -594,7 +602,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { ret = os_snprintf(pos, end - pos, "%sIEEE8021X", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -604,7 +612,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -614,7 +622,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, "%sWPA-NONE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -625,7 +633,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "%sFT-PSK", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -635,7 +643,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sFT-EAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -647,7 +655,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -657,7 +665,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -669,7 +677,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { ret = os_snprintf(pos, end - pos, "%sWPS", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -677,6 +685,50 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } #endif /* CONFIG_WPS */ +#ifdef CONFIG_SAE + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "%sSAE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "%sFT-SAE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_HS20 + if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) { + ret = os_snprintf(pos, end - pos, "%sOSEN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_HS20 */ + + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + if (pos == buf) { os_free(buf); buf = NULL; @@ -846,7 +898,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) { ret = os_snprintf(pos, end - pos, "%sOPEN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -856,7 +908,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) { ret = os_snprintf(pos, end - pos, "%sSHARED", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -866,7 +918,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) { ret = os_snprintf(pos, end - pos, "%sLEAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -987,7 +1039,7 @@ static char * wpa_config_write_freqs(const struct parse_data *data, for (i = 0; freqs[i]; i++) { ret = os_snprintf(pos, end - pos, "%s%u", i == 0 ? "" : " ", freqs[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -1110,7 +1162,7 @@ static char * wpa_config_write_eap(const struct parse_data *data, if (name) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -1264,7 +1316,7 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, os_memcpy(key, buf, *len); str_clear_free(buf); res = os_snprintf(title, sizeof(title), "wep_key%d", idx); - if (res >= 0 && (size_t) res < sizeof(title)) + if (!os_snprintf_error(sizeof(title), res)) wpa_hexdump_key(MSG_MSGDUMP, title, key, *len); return 0; } @@ -1387,7 +1439,7 @@ static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data, if (value == NULL) return NULL; res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr)); - if (res < 0 || res >= 20) { + if (os_snprintf_error(20, res)) { os_free(value); return NULL; } @@ -1472,7 +1524,7 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, res = os_snprintf(pos, end - pos, MACSTR " ", MAC2STR(ssid->p2p_client_list + (i - 1) * ETH_ALEN)); - if (res < 0 || res >= end - pos) { + if (os_snprintf_error(end - pos, res)) { os_free(value); return NULL; } @@ -1542,6 +1594,97 @@ static char * wpa_config_write_psk_list(const struct parse_data *data, #endif /* CONFIG_P2P */ + +#ifdef CONFIG_MESH + +static int wpa_config_parse_mesh_ht_mode(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int htval = 0; + + if (os_strcmp(value, "NOHT") == 0) + htval = CHAN_NO_HT; + else if (os_strcmp(value, "HT20") == 0) + htval = CHAN_HT20; + else if (os_strcmp(value, "HT40-") == 0) + htval = CHAN_HT40MINUS; + else if (os_strcmp(value, "HT40+") == 0) + htval = CHAN_HT40PLUS; + else { + wpa_printf(MSG_ERROR, + "Line %d: no ht_mode configured.", line); + return -1; + } + + wpa_printf(MSG_MSGDUMP, "mesh_ht_mode: 0x%x", htval); + ssid->mesh_ht_mode = htval; + return 0; +} + + +static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *rates = wpa_config_parse_int_array(value); + + if (rates == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'", + line, value); + return -1; + } + if (rates[0] == 0) { + os_free(rates); + rates = NULL; + } + + os_free(ssid->mesh_basic_rates); + ssid->mesh_basic_rates = rates; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE + +static char * wpa_config_write_mesh_ht_mode(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *val; + + switch (ssid->mesh_ht_mode) { + default: + val = NULL; + break; + case CHAN_NO_HT: + val = "NOHT"; + break; + case CHAN_HT20: + val = "HT20"; + break; + case CHAN_HT40MINUS: + val = "HT40-"; + break; + case CHAN_HT40PLUS: + val = "HT40+"; + break; + } + return val ? os_strdup(val) : NULL; +} + + +static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->mesh_basic_rates); +} + +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_MESH */ + + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1682,6 +1825,8 @@ static const struct parse_data ssid_fields[] = { { INTe(engine2) }, { INT(eapol_flags) }, { INTe(sim_num) }, + { STRe(openssl_ciphers) }, + { INTe(erp) }, #endif /* IEEE8021X_EAPOL */ { FUNC_KEY(wep_key0) }, { FUNC_KEY(wep_key1) }, @@ -1695,7 +1840,12 @@ static const struct parse_data ssid_fields[] = { { INTe(fragment_size) }, { INTe(ocsp) }, #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + { INT_RANGE(mode, 0, 5) }, + { INT_RANGE(no_auto_peer, 0, 1) }, +#else /* CONFIG_MESH */ { INT_RANGE(mode, 0, 4) }, +#endif /* CONFIG_MESH */ { INT_RANGE(proactive_key_caching, 0, 1) }, { INT_RANGE(disabled, 0, 2) }, { STR(id_str) }, @@ -1705,6 +1855,14 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, +#ifdef CONFIG_MESH + { FUNC(mesh_ht_mode) }, + { FUNC(mesh_basic_rates) }, + { INT(dot11MeshMaxRetries) }, + { INT(dot11MeshRetryTimeout) }, + { INT(dot11MeshConfirmTimeout) }, + { INT(dot11MeshHoldingTimeout) }, +#endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, @@ -1903,6 +2061,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->pac_file); bin_clear_free(eap->new_password, eap->new_password_len); str_clear_free(eap->external_sim_resp); + os_free(eap->openssl_ciphers); } #endif /* IEEE8021X_EAPOL */ @@ -1919,7 +2078,6 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) struct psk_list_entry *psk; os_free(ssid->ssid); - os_memset(ssid->psk, 0, sizeof(ssid->psk)); str_clear_free(ssid->passphrase); os_free(ssid->ext_psk); #ifdef IEEE8021X_EAPOL @@ -1933,12 +2091,15 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) #ifdef CONFIG_HT_OVERRIDES os_free(ssid->ht_mcs); #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_MESH + os_free(ssid->mesh_basic_rates); +#endif /* CONFIG_MESH */ while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry, list))) { dl_list_del(&psk->list); - os_free(psk); + bin_clear_free(psk, sizeof(*psk)); } - os_free(ssid); + bin_clear_free(ssid, sizeof(*ssid)); } @@ -2000,6 +2161,7 @@ void wpa_config_free(struct wpa_config *config) { struct wpa_ssid *ssid, *prev = NULL; struct wpa_cred *cred, *cprev; + int i; ssid = config->ssid; while (ssid) { @@ -2018,11 +2180,14 @@ void wpa_config_free(struct wpa_config *config) wpa_config_flush_blobs(config); wpabuf_free(config->wps_vendor_ext_m1); + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) + wpabuf_free(config->wps_vendor_ext[i]); os_free(config->ctrl_interface); os_free(config->ctrl_interface_group); os_free(config->opensc_engine_path); os_free(config->pkcs11_engine_path); os_free(config->pkcs11_module_path); + os_free(config->openssl_ciphers); os_free(config->pcsc_reader); str_clear_free(config->pcsc_pin); os_free(config->driver_param); @@ -2181,6 +2346,13 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM; #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + ssid->mesh_ht_mode = DEFAULT_MESH_HT_MODE; + ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES; + ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT; + ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT; + ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT; +#endif /* CONFIG_MESH */ #ifdef CONFIG_HT_OVERRIDES ssid->disable_ht = DEFAULT_DISABLE_HT; ssid->disable_ht40 = DEFAULT_DISABLE_HT40; @@ -2831,12 +3003,18 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, static char * alloc_int_str(int val) { + const unsigned int bufsize = 20; char *buf; + int res; - buf = os_malloc(20); + buf = os_malloc(bufsize); if (buf == NULL) return NULL; - os_snprintf(buf, 20, "%d", val); + res = os_snprintf(buf, bufsize, "%d", val); + if (os_snprintf_error(bufsize, res)) { + os_free(buf); + buf = NULL; + } return buf; } @@ -2907,7 +3085,7 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) ret = os_snprintf(pos, end - pos, "%s%u", i > 0 ? "\n" : "", cred->req_conn_capab_proto[i]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; @@ -2919,7 +3097,7 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) "%s%d", j > 0 ? "," : ":", ports[j]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -2988,7 +3166,7 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) for (i = 0; i < cred->num_domain; i++) { ret = os_snprintf(pos, end - pos, "%s%s", i > 0 ? "\n" : "", cred->domain[i]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -3053,7 +3231,7 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) ret = os_snprintf(pos, end - pos, "%s%s", i > 0 ? "\n" : "", wpa_ssid_txt(e->ssid, e->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -3083,7 +3261,7 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) i > 0 ? "\n" : "", p->fqdn, p->exact_match, p->priority, p->country); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; } @@ -3273,6 +3451,8 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, return NULL; config->eapol_version = DEFAULT_EAPOL_VERSION; config->ap_scan = DEFAULT_AP_SCAN; + config->user_mpm = DEFAULT_USER_MPM; + config->max_peer_links = DEFAULT_MAX_PEER_LINKS; config->fast_reauth = DEFAULT_FAST_REAUTH; config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; @@ -3290,6 +3470,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->wmm_ac_params[3] = ac_vo; config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY; config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; + config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -3818,11 +3999,16 @@ static const struct global_parse_data global_fields[] = { #endif /* CONFIG_MACSEC */ { INT(ap_scan), 0 }, { FUNC(bgscan), 0 }, +#ifdef CONFIG_MESH + { INT(user_mpm), 0 }, + { INT_RANGE(max_peer_links, 0, 255), 0 }, +#endif /* CONFIG_MESH */ { INT(disable_scan_offload), 0 }, { INT(fast_reauth), 0 }, { STR(opensc_engine_path), 0 }, { STR(pkcs11_engine_path), 0 }, { STR(pkcs11_module_path), 0 }, + { STR(openssl_ciphers), 0 }, { STR(pcsc_reader), 0 }, { STR(pcsc_pin), 0 }, { INT(external_sim), 0 }, @@ -3915,6 +4101,7 @@ static const struct global_parse_data global_fields[] = { { INT(mac_addr), 0 }, { INT(rand_addr_lifetime), 0 }, { INT(preassoc_mac_addr), 0 }, + { INT(key_mgmt_offload), 0}, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 3fd4192c..b3f7eef7 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -15,6 +15,8 @@ #else /* CONFIG_NO_SCAN_PROCESSING */ #define DEFAULT_AP_SCAN 1 #endif /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_USER_MPM 1 +#define DEFAULT_MAX_PEER_LINKS 99 #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 @@ -28,6 +30,7 @@ #define DEFAULT_SCAN_CUR_FREQ 0 #define DEFAULT_P2P_SEARCH_DELAY 500 #define DEFAULT_RAND_ADDR_LIFETIME 60 +#define DEFAULT_KEY_MGMT_OFFLOAD 1 #include "config_ssid.h" #include "wps/wps.h" @@ -517,6 +520,15 @@ struct wpa_config { char *pkcs11_module_path; /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + char *openssl_ciphers; + + /** * pcsc_reader - PC/SC reader name prefix * * If not %NULL, PC/SC reader with a name that matches this prefix is @@ -1079,6 +1091,34 @@ struct wpa_config { * 2 = like 1, but maintain OUI (with local admin bit set) */ int preassoc_mac_addr; + + /** + * key_mgmt_offload - Use key management offload + * + * Key management offload should be used if the device supports it. + * Key management offload is the capability of a device operating as + * a station to do the exchange necessary to establish temporal keys + * during initial RSN connection, after roaming, or during a PTK + * rekeying operation. + */ + int key_mgmt_offload; + + /** + * user_mpm - MPM residency + * + * 0: MPM lives in driver. + * 1: wpa_supplicant handles peering and station allocation. + * + * If AMPE or SAE is enabled, the MPM is always in userspace. + */ + int user_mpm; + + /** + * max_peer_links - Maximum number of peer links + * + * Maximum number of mesh peering currently maintained by the STA. + */ + int max_peer_links; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 5c8f0450..5c8b24b0 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -143,6 +143,15 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) ssid->group_cipher &= ~WPA_CIPHER_CCMP; } + if (ssid->mode == WPAS_MODE_MESH && + (ssid->key_mgmt != WPA_KEY_MGMT_NONE && + ssid->key_mgmt != WPA_KEY_MGMT_SAE)) { + wpa_printf(MSG_ERROR, + "Line %d: key_mgmt for mesh network should be open or SAE", + line); + errors++; + } + return errors; } @@ -599,7 +608,7 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) int res; res = os_snprintf(field, sizeof(field), "wep_key%d", idx); - if (res < 0 || (size_t) res >= sizeof(field)) + if (os_snprintf_error(sizeof(field), res)) return; value = wpa_config_get(ssid, field); if (value) { @@ -707,6 +716,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INTe(engine); INTe(engine2); INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); + INTe(erp); #endif /* IEEE8021X_EAPOL */ for (i = 0; i < 4; i++) write_wep_key(f, i, ssid); @@ -743,6 +753,14 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(update_identifier); #endif /* CONFIG_HS20 */ write_int(f, "mac_addr", ssid->mac_addr, -1); +#ifdef CONFIG_MESH + STR(mesh_ht_mode); + STR(mesh_basic_rates); + INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES); + INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT); + INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT); + INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT); +#endif /* CONFIG_MESH */ #undef STR #undef INT @@ -938,6 +956,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->pkcs11_module_path) fprintf(f, "pkcs11_module_path=%s\n", config->pkcs11_module_path); + if (config->openssl_ciphers) + fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers); if (config->pcsc_reader) fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader); if (config->pcsc_pin) @@ -1190,6 +1210,15 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->preassoc_mac_addr) fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr); + + if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD) + fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload); + + if (config->user_mpm != DEFAULT_USER_MPM) + fprintf(f, "user_mpm=%d\n", config->user_mpm); + + if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS) + fprintf(f, "max_peer_links=%d\n", config->max_peer_links); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index f50b2d41..c5cd6e71 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -27,6 +27,11 @@ #define DEFAULT_FRAGMENT_SIZE 1398 #define DEFAULT_BG_SCAN_PERIOD -1 +#define DEFAULT_MESH_HT_MODE CHAN_UNDEFINED /* undefined */ +#define DEFAULT_MESH_MAX_RETRIES 2 +#define DEFAULT_MESH_RETRY_TIMEOUT 40 +#define DEFAULT_MESH_CONFIRM_TIMEOUT 40 +#define DEFAULT_MESH_HOLDING_TIMEOUT 40 #define DEFAULT_DISABLE_HT 0 #define DEFAULT_DISABLE_HT40 0 #define DEFAULT_DISABLE_SGI 0 @@ -317,6 +322,8 @@ struct wpa_ssid { * 4 = P2P Group Formation (used internally; not in configuration * files) * + * 5 = Mesh + * * Note: IBSS can only be used with key_mgmt NONE (plaintext and static * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE * (fixed group key TKIP/CCMP) is available for backwards compatibility, @@ -331,6 +338,7 @@ struct wpa_ssid { WPAS_MODE_AP = 2, WPAS_MODE_P2P_GO = 3, WPAS_MODE_P2P_GROUP_FORMATION = 4, + WPAS_MODE_MESH = 5, } mode; /** @@ -400,6 +408,29 @@ struct wpa_ssid { */ int frequency; + /** + * mesh_ht_mode - definition of HT mode in mesh mode + * + * Use the given HT mode for mesh networks. The driver will + * adapt to other stations if neccesary, but advertise the + * configured HT mode (HT20/HT40-/HT40+/NOHT). + */ + int mesh_ht_mode; + + /** + * mesh_basic_rates - BSS Basic rate set for mesh network + * + */ + int *mesh_basic_rates; + + /** + * Mesh network plink parameters + */ + int dot11MeshMaxRetries; + int dot11MeshRetryTimeout; /* msec */ + int dot11MeshConfirmTimeout; /* msec */ + int dot11MeshHoldingTimeout; /* msec */ + int ht40; int vht; @@ -666,6 +697,14 @@ struct wpa_ssid { * followed). */ int mac_addr; + + /** + * no_auto_peer - Do not automatically peer with compatible mesh peers + * + * When unset, the reception of a beacon from a another mesh peer in + * this MBSS will trigger a peering attempt. + */ + int no_auto_peer; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index a4c26e45..acdc90d7 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -7,6 +7,10 @@ */ #include "utils/includes.h" +#ifdef CONFIG_TESTING_OPTIONS +#include <net/ethernet.h> +#include <netinet/ip.h> +#endif /* CONFIG_TESTING_OPTIONS */ #include "utils/common.h" #include "utils/eloop.h" @@ -15,6 +19,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "ap/hostapd.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" @@ -42,6 +47,7 @@ #include "wnm_sta.h" #include "offchannel.h" #include "drivers/driver.h" +#include "mesh.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -420,11 +426,30 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { wpa_s->ext_mgmt_frame_handling = !!atoi(value); + } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { + wpa_s->ext_eapol_frame_io = !!atoi(value); +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_s->ap_iface->bss[0]->ext_eapol_frame_io = + wpa_s->ext_eapol_frame_io; + } +#endif /* CONFIG_AP */ + } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) { + wpa_s->extra_roc_dur = atoi(value); #endif /* CONFIG_TESTING_OPTIONS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { ret = wpas_ctrl_set_blob(wpa_s, value); #endif /* CONFIG_NO_CONFIG_BLOBS */ + } else if (os_strcasecmp(cmd, "setband") == 0) { + if (os_strcmp(value, "AUTO") == 0) + wpa_s->setband = WPA_SETBAND_AUTO; + else if (os_strcmp(value, "5G") == 0) + wpa_s->setband = WPA_SETBAND_5G; + else if (os_strcmp(value, "2G") == 0) + wpa_s->setband = WPA_SETBAND_2G; + else + ret = -1; } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -459,9 +484,6 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, else enabled = wpa_s->global->wifi_display; res = os_snprintf(buf, buflen, "%d", enabled); - if (res < 0 || (unsigned int) res >= buflen) - return -1; - return res; #endif /* CONFIG_WIFI_DISPLAY */ #ifdef CONFIG_TESTING_GET_GTK } else if (os_strcmp(cmd, "gtk") == 0) { @@ -473,7 +495,7 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, #endif /* CONFIG_TESTING_GET_GTK */ } - if (res < 0 || (unsigned int) res >= buflen) + if (os_snprintf_error(buflen, res)) return -1; return res; } @@ -626,14 +648,162 @@ static int ctrl_iface_get_capability_tdls( (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ? "EXTERNAL" : "INTERNAL") : "UNSUPPORTED"); - if (ret < 0 || (size_t) ret > buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } + +static int wpa_supplicant_ctrl_iface_tdls_chan_switch( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 peer[ETH_ALEN]; + struct hostapd_freq_params freq_params; + u8 oper_class; + char *pos, *end; + + if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Only supported with external setup"); + return -1; + } + + os_memset(&freq_params, 0, sizeof(freq_params)); + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + oper_class = strtol(pos, &end, 10); + if (pos == end) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Invalid op class provided"); + return -1; + } + + pos = end; + freq_params.freq = atoi(pos); + if (freq_params.freq == 0) { + wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided"); + return -1; + } + +#define SET_FREQ_SETTING(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + freq_params.str = atoi(pos2); \ + } \ + } while (0) + + SET_FREQ_SETTING(center_freq1); + SET_FREQ_SETTING(center_freq2); + SET_FREQ_SETTING(bandwidth); + SET_FREQ_SETTING(sec_channel_offset); +#undef SET_FREQ_SETTING + + freq_params.ht_enabled = !!os_strstr(pos, " ht"); + freq_params.vht_enabled = !!os_strstr(pos, " vht"); + + if (hwaddr_aton(cmd, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'", + cmd); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR + " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s", + MAC2STR(peer), oper_class, freq_params.freq, + freq_params.center_freq1, freq_params.center_freq2, + freq_params.bandwidth, freq_params.sec_channel_offset, + freq_params.ht_enabled ? " HT" : "", + freq_params.vht_enabled ? " VHT" : ""); + + return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class, + &freq_params); +} + + +static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 peer[ETH_ALEN]; + + if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Only supported with external setup"); + return -1; + } + + if (hwaddr_aton(cmd, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'", + cmd); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR, + MAC2STR(peer)); + + return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); +} + #endif /* CONFIG_TDLS */ +static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + struct wmm_ac_ts_setup_params params = { + .tsid = 0xff, + .direction = 0xff, + }; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "tsid=%i", ¶ms.tsid) == 1 || + sscanf(token, "up=%i", ¶ms.user_priority) == 1 || + sscanf(token, "nominal_msdu_size=%i", + ¶ms.nominal_msdu_size) == 1 || + sscanf(token, "mean_data_rate=%i", + ¶ms.mean_data_rate) == 1 || + sscanf(token, "min_phy_rate=%i", + ¶ms.minimum_phy_rate) == 1 || + sscanf(token, "sba=%i", + ¶ms.surplus_bandwidth_allowance) == 1) + continue; + + if (os_strcasecmp(token, "downlink") == 0) { + params.direction = WMM_TSPEC_DIRECTION_DOWNLINK; + } else if (os_strcasecmp(token, "uplink") == 0) { + params.direction = WMM_TSPEC_DIRECTION_UPLINK; + } else if (os_strcasecmp(token, "bidi") == 0) { + params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL; + } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) { + params.fixed_nominal_msdu = 1; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'", + token); + return -1; + } + + } + + return wpas_wmm_ac_addts(wpa_s, ¶ms); +} + + +static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 tsid = atoi(cmd); + + return wpas_wmm_ac_delts(wpa_s, tsid); +} + + #ifdef CONFIG_IEEE80211R static int wpa_supplicant_ctrl_iface_ft_ds( struct wpa_supplicant *wpa_s, char *addr) @@ -747,7 +917,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, if (ret < 0) return -1; ret = os_snprintf(buf, buflen, "%s", pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -759,7 +929,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, done: /* Return the generated PIN */ ret = os_snprintf(buf, buflen, "%08d", ret); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -796,14 +966,14 @@ static int wpa_supplicant_ctrl_iface_wps_check_pin( if (!wps_pin_valid(pin_val)) { wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; @@ -1537,12 +1707,12 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid = wpa_s->current_ssid; ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(wpa_s->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ret = os_snprintf(pos, end - pos, "freq=%u\n", wpa_s->assoc_freq); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (ssid) { @@ -1560,7 +1730,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", wpa_ssid_txt(_ssid, ssid_len), ssid->id); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1571,7 +1741,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "passphrase=%s\n", ssid->passphrase); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1579,7 +1749,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "id_str=%s\n", ssid->id_str); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1610,7 +1780,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = 0; break; } - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1632,21 +1802,21 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, wpa_s->sme.sae.state == SAE_ACCEPTED) { ret = os_snprintf(pos, end - pos, "sae_group=%d\n", wpa_s->sme.sae.group); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } #endif /* CONFIG_SAE */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wpa_s->l2 && l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1655,7 +1825,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, if (wpa_s->global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR "\n", MAC2STR(wpa_s->global->p2p_dev_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1663,7 +1833,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", MAC2STR(wpa_s->own_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1679,7 +1849,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, release = rel_num + 1; } ret = os_snprintf(pos, end - pos, "hs20=%d\n", release); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1698,7 +1868,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "provisioning_sp=%s\n", cred->provisioning_sp); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1721,7 +1891,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, } ret = os_snprintf(pos, end - pos, "home_sp=%s\n", cred->domain[i]); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1741,7 +1911,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, type = "unknown"; ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1767,7 +1937,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, char uuid_str[100]; uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str)); ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1855,7 +2025,7 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, while (e) { ret = os_snprintf(pos, end - pos, MACSTR "\n", MAC2STR(e->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; e = e->next; @@ -1937,10 +2107,6 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *pos, *end, *stamp; int ret; - if (cmd == NULL) { - return -1; - } - /* cmd: "LOG_LEVEL [<level>]" */ if (*cmd == '\0') { pos = buf; @@ -1949,7 +2115,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, "Timestamp: %d\n", debug_level_str(wpa_debug_level), wpa_debug_timestamp); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) ret = 0; return ret; @@ -1992,7 +2158,7 @@ static int wpa_supplicant_ctrl_iface_list_networks( end = buf + buflen; ret = os_snprintf(pos, end - pos, "network id / ssid / bssid / flags\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2013,7 +2179,7 @@ static int wpa_supplicant_ctrl_iface_list_networks( ret = os_snprintf(pos, end - pos, "%d\t%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; if (ssid->bssid_set) { @@ -2022,7 +2188,7 @@ static int wpa_supplicant_ctrl_iface_list_networks( } else { ret = os_snprintf(pos, end - pos, "\tany"); } - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", @@ -2033,11 +2199,11 @@ static int wpa_supplicant_ctrl_iface_list_networks( "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return prev - buf; pos += ret; @@ -2052,7 +2218,7 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) { int ret; ret = os_snprintf(pos, end - pos, "-"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; ret = wpa_write_ciphers(pos, end, cipher, "+"); @@ -2071,13 +2237,13 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, int ret; ret = os_snprintf(pos, end - pos, "[%s-", proto); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { ret = os_snprintf(pos, end - pos, "?]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; @@ -2087,21 +2253,28 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sEAP", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sPSK", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, "%sNone", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "%sSAE", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } @@ -2109,14 +2282,21 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sFT/EAP", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "%sFT/PSK", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "%sFT/SAE", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } @@ -2125,30 +2305,38 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", pos == start ? "" : "+"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } #endif /* CONFIG_IEEE80211W */ + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { ret = os_snprintf(pos, end - pos, "-preauth"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } ret = os_snprintf(pos, end - pos, "]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; @@ -2176,7 +2364,7 @@ static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, txt = "[WPS]"; ret = os_snprintf(pos, end - pos, "%s", txt); - if (ret >= 0 && ret < end - pos) + if (!os_snprintf_error(end - pos, ret)) pos += ret; wpabuf_free(wps_ie); return pos; @@ -2205,8 +2393,9 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *p2p; + const u8 *ie, *ie2, *p2p, *mesh; + mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (!p2p) p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE); @@ -2220,26 +2409,34 @@ static int wpa_supplicant_ctrl_iface_scan_result( ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t", MAC2STR(bss->bssid), bss->freq, bss->level); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie2) - pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + if (ie2) { + pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", + ie2, 2 + ie2[1]); + } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (mesh) { + ret = os_snprintf(pos, end - pos, "[MESH]"); + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { @@ -2257,33 +2454,33 @@ static int wpa_supplicant_ctrl_iface_scan_result( break; } ret = os_snprintf(pos, end - pos, "%s", s); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } } if (p2p) { ret = os_snprintf(pos, end - pos, "[P2P]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { ret = os_snprintf(pos, end - pos, "[HS20]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -2291,12 +2488,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; @@ -2315,7 +2512,7 @@ static int wpa_supplicant_ctrl_iface_scan_results( end = buf + buflen; ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " "flags / ssid\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2331,6 +2528,116 @@ static int wpa_supplicant_ctrl_iface_scan_results( } +#ifdef CONFIG_MESH + +static int wpa_supplicant_ctrl_iface_mesh_interface_add( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + char *pos, ifname[IFNAMSIZ + 1]; + + ifname[0] = '\0'; + + pos = os_strstr(cmd, "ifname="); + if (pos) { + pos += 7; + os_strlcpy(ifname, pos, sizeof(ifname)); + } + + if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0) + return -1; + + os_strlcpy(reply, ifname, max_len); + return os_strlen(ifname); +} + + +static int wpa_supplicant_ctrl_iface_mesh_group_add( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find network id=%d", id); + return -1; + } + if (ssid->mode != WPAS_MODE_MESH) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network"); + return -1; + } + if (ssid->key_mgmt != WPA_KEY_MGMT_NONE && + ssid->key_mgmt != WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: key_mgmt for mesh network should be open or SAE"); + return -1; + } + + /* + * TODO: If necessary write our own group_add function, + * for now we can reuse select_network + */ + wpa_supplicant_select_network(wpa_s, ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_mesh_group_remove( + struct wpa_supplicant *wpa_s, char *cmd) +{ + struct wpa_supplicant *orig; + struct wpa_global *global; + int found = 0; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); + + global = wpa_s->global; + orig = wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, cmd) == 0) { + found = 1; + break; + } + } + if (!found) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found", + cmd); + return -1; + } + if (wpa_s->mesh_if_created && wpa_s == orig) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself"); + return -1; + } + + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + + /* + * TODO: If necessary write our own group_remove function, + * for now we can reuse deauthenticate + */ + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + if (wpa_s->mesh_if_created) + wpa_supplicant_remove_iface(global, wpa_s, 0); + + return 0; +} + +#endif /* CONFIG_MESH */ + + static int wpa_supplicant_ctrl_iface_select_network( struct wpa_supplicant *wpa_s, char *cmd) { @@ -2463,7 +2770,7 @@ static int wpa_supplicant_ctrl_iface_add_network( wpa_config_set_network_defaults(ssid); ret = os_snprintf(buf, buflen, "%d\n", ssid->id); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -2586,6 +2893,8 @@ static int wpa_supplicant_ctrl_iface_update_network( wpa_config_update_psk(ssid); else if (os_strcmp(name, "priority") == 0) wpa_config_update_prio_list(wpa_s->conf); + else if (os_strcmp(name, "no_auto_peer") == 0) + ssid->no_auto_peer = atoi(value); return 0; } @@ -2712,7 +3021,7 @@ static int wpa_supplicant_ctrl_iface_dup_network( ssid_d = wpa_config_get_network(wpa_s->conf, id_d); if (ssid_d == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " - "network id=%d", id_s); + "network id=%d", id_d); return -1; } @@ -2743,7 +3052,7 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, end = buf + buflen; ret = os_snprintf(pos, end - pos, "cred id / realm / username / domain / imsi\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2754,7 +3063,7 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, cred->username ? cred->username : "", cred->domain ? cred->domain[0] : "", cred->imsi ? cred->imsi : ""); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2780,7 +3089,7 @@ static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id); ret = os_snprintf(buf, buflen, "%d\n", cred->id); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -2810,9 +3119,13 @@ static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->parent_cred == cred) { + int res; + wpa_printf(MSG_DEBUG, "Remove network id %d since it " "used the removed credential", ssid->id); - os_snprintf(str, sizeof(str), "%d", ssid->id); + res = os_snprintf(str, sizeof(str), "%d", ssid->id); + if (os_snprintf_error(sizeof(str), res)) + str[sizeof(str) - 1] = '\0'; ssid = ssid->next; wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); } else @@ -3042,7 +3355,7 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict, ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3078,7 +3391,7 @@ static int ctrl_iface_get_capability_group(int res, char *strict, ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", ciphers[i].name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3110,14 +3423,14 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, } ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { ret = os_snprintf(pos, end - pos, " WPA-EAP"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3125,14 +3438,14 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, " WPA-PSK"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, " WPA-NONE"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3165,7 +3478,7 @@ static int ctrl_iface_get_capability_proto(int res, char *strict, WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, "%sRSN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3174,7 +3487,7 @@ static int ctrl_iface_get_capability_proto(int res, char *strict, WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { ret = os_snprintf(pos, end - pos, "%sWPA", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3206,7 +3519,7 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict, if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { ret = os_snprintf(pos, end - pos, "%sOPEN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3214,7 +3527,7 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict, if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { ret = os_snprintf(pos, end - pos, "%sSHARED", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3222,7 +3535,7 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict, if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { ret = os_snprintf(pos, end - pos, "%sLEAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3254,7 +3567,7 @@ static int ctrl_iface_get_capability_modes(int res, char *strict, if (capa->flags & WPA_DRIVER_FLAGS_IBSS) { ret = os_snprintf(pos, end - pos, "%sIBSS", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3262,7 +3575,7 @@ static int ctrl_iface_get_capability_modes(int res, char *strict, if (capa->flags & WPA_DRIVER_FLAGS_AP) { ret = os_snprintf(pos, end - pos, "%sAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3299,7 +3612,7 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; @@ -3307,12 +3620,12 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3350,7 +3663,7 @@ static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n", hmode); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; @@ -3359,17 +3672,17 @@ static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, continue; ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n", chnl[i].chan, chnl[i].freq, - chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ? - " (NO_IBSS)" : "", + chnl[i].flag & HOSTAPD_CHAN_NO_IR ? + " (NO_IR)" : "", chnl[i].flag & HOSTAPD_CHAN_RADAR ? " (DFS)" : ""); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -3443,6 +3756,15 @@ static int wpa_supplicant_ctrl_iface_get_capability( return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_ERP + if (os_strcmp(field, "erp") == 0) { + res = os_snprintf(buf, buflen, "ERP"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_EPR */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -3463,20 +3785,20 @@ static char * anqp_add_hex(char *pos, char *end, const char *title, return start; ret = os_snprintf(pos, end - pos, "%s=", title); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return start; pos += ret; d = wpabuf_head_u8(data); for (i = 0; i < wpabuf_len(data); i++) { ret = os_snprintf(pos, end - pos, "%02x", *d++); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return start; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return start; pos += ret; @@ -3498,7 +3820,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_ID) { ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3506,14 +3828,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_BSSID) { ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(bss->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FREQ) { ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3521,7 +3843,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_BEACON_INT) { ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", bss->beacon_int); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3529,28 +3851,28 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_CAPABILITIES) { ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", bss->caps); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_QUAL) { ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_NOISE) { ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_LEVEL) { ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3558,7 +3880,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_TSF) { ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", (unsigned long long) bss->tsf); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3569,34 +3891,34 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, os_get_reltime(&now); ret = os_snprintf(pos, end - pos, "age=%d\n", (int) (now.sec - bss->last_update.sec)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_IE) { ret = os_snprintf(pos, end - pos, "ie="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = (const u8 *) (bss + 1); for (i = 0; i < bss->ie_len; i++) { ret = os_snprintf(pos, end - pos, "%02x", *ie++); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FLAGS) { ret = os_snprintf(pos, end - pos, "flags="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; @@ -3611,14 +3933,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; switch (bss->caps & IEEE80211_CAP_DMG_MASK) { @@ -3636,19 +3958,19 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, break; } ret = os_snprintf(pos, end - pos, "%s", s); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } else { if (bss->caps & IEEE80211_CAP_IBSS) { ret = os_snprintf(pos, end - pos, "[IBSS]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (bss->caps & IEEE80211_CAP_ESS) { ret = os_snprintf(pos, end - pos, "[ESS]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3656,21 +3978,21 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[P2P]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[HS20]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #endif /* CONFIG_HS20 */ ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3678,7 +4000,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_SSID) { ret = os_snprintf(pos, end - pos, "ssid=%s\n", wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3711,7 +4033,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, WFD_IE_VENDOR_TYPE); if (wfd) { ret = os_snprintf(pos, end - pos, "wfd_subelems="); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { wpabuf_free(wfd); return 0; } @@ -3723,7 +4045,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, wpabuf_free(wfd); ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3761,9 +4083,19 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_MESH + if (mask & WPA_BSS_MASK_MESH_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_MESH */ + if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3783,7 +4115,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, struct dl_list *next; int ret = 0; int len; - char *ctmp; + char *ctmp, *end = buf + buflen; unsigned long mask = WPA_BSS_MASK_ALL; if (os_strncmp(cmd, "RANGE=", 6) == 0) { @@ -3892,8 +4224,16 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, if (bss == bsslast) { if ((mask & WPA_BSS_MASK_DELIM) && len && (bss == dl_list_last(&wpa_s->bss_id, - struct wpa_bss, list_id))) - os_snprintf(buf - 5, 5, "####\n"); + struct wpa_bss, list_id))) { + int res; + + res = os_snprintf(buf - 5, end - buf + 5, + "####\n"); + if (os_snprintf_error(end - buf + 5, res)) { + wpa_printf(MSG_DEBUG, + "Could not add end delim"); + } + } break; } next = bss->list_id.next; @@ -3938,7 +4278,7 @@ static int wpa_supplicant_ctrl_iface_bss_expire_count( } -static int wpa_supplicant_ctrl_iface_bss_flush( +static void wpa_supplicant_ctrl_iface_bss_flush( struct wpa_supplicant *wpa_s, char *cmd) { int flush_age = atoi(cmd); @@ -3947,7 +4287,6 @@ static int wpa_supplicant_ctrl_iface_bss_flush( wpa_bss_flush(wpa_s); else wpa_bss_flush_by_age(wpa_s, flush_age); - return 0; } @@ -4173,7 +4512,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, return -1; if (wps_method == WPS_PIN_DISPLAY && pin == NULL) { ret = os_snprintf(buf, buflen, "%08d", new_pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -4288,7 +4627,7 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, if (ref == 0) return -1; res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); - if (res < 0 || (unsigned) res >= buflen) + if (os_snprintf_error(buflen, res)) return -1; return res; } @@ -4724,7 +5063,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, info->dev_capab, info->group_capab, info->level); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -4735,7 +5074,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", wps_dev_type_bin2str(t, devtype, sizeof(devtype))); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -4743,7 +5082,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0); if (ssid) { res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -4755,7 +5094,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, if (info->vendor_elems) { res = os_snprintf(pos, end - pos, "vendor_elems="); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -4764,7 +5103,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, wpabuf_len(info->vendor_elems)); res = os_snprintf(pos, end - pos, "\n"); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -5007,6 +5346,7 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) { os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; + wpas_p2p_stop_find(wpa_s); if (wpa_s->global->p2p) p2p_flush(wpa_s->global->p2p); } @@ -5180,6 +5520,8 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) if (used < 0) return -1; pos = dst + used; + if (*pos == ' ') + pos++; while (num_id < MAX_ANQP_INFO_ID) { if (os_strncmp(pos, "hs20:", 5) == 0) { #ifdef CONFIG_HS20 @@ -5359,6 +5701,8 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) if (used < 0) return -1; pos = dst + used; + if (*pos == ' ') + pos++; for (;;) { int num = atoi(pos); if (num <= 0 || num > 31) @@ -5471,14 +5815,6 @@ static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) #endif /* CONFIG_HS20 */ -static int wpa_supplicant_ctrl_iface_sta_autoconnect( - struct wpa_supplicant *wpa_s, char *cmd) -{ - wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; - return 0; -} - - #ifdef CONFIG_AUTOSCAN static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, @@ -5594,14 +5930,14 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, "NOISE=%d\nFREQUENCY=%u\n", si.current_signal, si.current_txrate / 1000, si.current_noise, si.frequency); - if (ret < 0 || ret > end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", channel_width_to_string(si.chanwidth)); - if (ret < 0 || ret > end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -5610,7 +5946,7 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n", si.center_frq1, si.center_frq2); - if (ret < 0 || ret > end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -5618,7 +5954,7 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, if (si.avg_signal) { ret = os_snprintf(pos, end - pos, "AVG_RSSI=%d\n", si.avg_signal); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -5639,7 +5975,7 @@ static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); - if (ret < 0 || (size_t) ret > buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -5664,6 +6000,8 @@ static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, } } ret = os_snprintf(buf, buflen, "%s\n", "OK"); + if (os_snprintf_error(buflen, ret)) + ret = -1; } return ret; } @@ -5753,6 +6091,7 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS wpa_s->wps_fragment_size = 0; wpas_wps_cancel(wpa_s); + wps_registrar_flush(wpa_s->wps->registrar); #endif /* CONFIG_WPS */ wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; @@ -5792,12 +6131,16 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->conf->auto_interworking = 0; wpa_s->conf->okc = 0; + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); + rsn_preauth_deinit(wpa_s->wpa); + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200); wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70); wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); eapol_sm_notify_logoff(wpa_s->eapol, FALSE); radio_remove_works(wpa_s, NULL, 1); + wpa_s->ext_work_in_progress = 0; wpa_s->next_ssid = NULL; @@ -5806,6 +6149,14 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #endif /* CONFIG_INTERWORKING */ wpa_s->ext_mgmt_frame_handling = 0; + wpa_s->ext_eapol_frame_io = 0; +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->extra_roc_dur = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_s->disconnected = 0; + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; } @@ -5829,7 +6180,7 @@ static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n", work->type, work->wpa_s->ifname, work->freq, work->started, diff.sec, diff.usec); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -5847,6 +6198,7 @@ static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx) "Timing out external radio work %u (%s)", ework->id, work->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id); + work->wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); } @@ -5868,6 +6220,7 @@ static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)", ework->id, ework->type); wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id); + work->wpa_s->ext_work_in_progress = 1; if (!ework->timeout) ework->timeout = 10; eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout, @@ -5923,7 +6276,7 @@ static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, } ret = os_snprintf(buf, buflen, "%u", ework->id); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -5947,6 +6300,7 @@ static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd) "Completed external radio work %u (%s)", ework->id, ework->type); eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); + wpa_s->ext_work_in_progress = 0; radio_work_done(work); os_free(ework); return 3; /* "OK\n" */ @@ -6003,31 +6357,17 @@ static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) } -static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val) -{ - int *freqs = NULL; - - freqs = freq_range_to_channel_list(wpa_s, val); - if (freqs == NULL) - return -1; - - os_free(wpa_s->manual_scan_freqs); - wpa_s->manual_scan_freqs = freqs; - - return 0; -} - - -static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value) +static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value, + unsigned int *scan_id_count, int scan_id[]) { const char *pos = value; while (pos) { if (*pos == ' ' || *pos == '\0') break; - if (wpa_s->scan_id_count == MAX_SCAN_ID) + if (*scan_id_count == MAX_SCAN_ID) return -1; - wpa_s->scan_id[wpa_s->scan_id_count++] = atoi(pos); + scan_id[(*scan_id_count)++] = atoi(pos); pos = os_strchr(pos, ','); if (pos) pos++; @@ -6041,54 +6381,82 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, char *reply, int reply_size, int *reply_len) { char *pos; + unsigned int manual_scan_passive = 0; + unsigned int manual_scan_use_id = 0; + unsigned int manual_scan_only_new = 0; + unsigned int scan_only = 0; + unsigned int scan_id_count = 0; + int scan_id[MAX_SCAN_ID]; + void (*scan_res_handler)(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + int *manual_scan_freqs = NULL; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { *reply_len = -1; return; } - wpa_s->manual_scan_passive = 0; - wpa_s->manual_scan_use_id = 0; - wpa_s->manual_scan_only_new = 0; - wpa_s->scan_id_count = 0; + if (radio_work_pending(wpa_s, "scan")) { + wpa_printf(MSG_DEBUG, + "Pending scan scheduled - reject new request"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + return; + } if (params) { if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) - wpa_s->scan_res_handler = scan_only_handler; + scan_only = 1; pos = os_strstr(params, "freq="); - if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) { - *reply_len = -1; - return; + if (pos) { + manual_scan_freqs = freq_range_to_channel_list(wpa_s, + pos + 5); + if (manual_scan_freqs == NULL) { + *reply_len = -1; + goto done; + } } pos = os_strstr(params, "passive="); if (pos) - wpa_s->manual_scan_passive = !!atoi(pos + 8); + manual_scan_passive = !!atoi(pos + 8); pos = os_strstr(params, "use_id="); if (pos) - wpa_s->manual_scan_use_id = atoi(pos + 7); + manual_scan_use_id = atoi(pos + 7); pos = os_strstr(params, "only_new=1"); if (pos) - wpa_s->manual_scan_only_new = 1; + manual_scan_only_new = 1; pos = os_strstr(params, "scan_id="); - if (pos && scan_id_list_parse(wpa_s, pos + 8) < 0) { + if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count, + scan_id) < 0) { *reply_len = -1; - return; + goto done; } - } else { - os_free(wpa_s->manual_scan_freqs); - wpa_s->manual_scan_freqs = NULL; - if (wpa_s->scan_res_handler == scan_only_handler) - wpa_s->scan_res_handler = NULL; } + if (scan_only) + scan_res_handler = scan_only_handler; + else if (wpa_s->scan_res_handler == scan_only_handler) + scan_res_handler = NULL; + else + scan_res_handler = wpa_s->scan_res_handler; + if (!wpa_s->sched_scanning && !wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->manual_scan_passive = manual_scan_passive; + wpa_s->manual_scan_use_id = manual_scan_use_id; + wpa_s->manual_scan_only_new = manual_scan_only_new; + wpa_s->scan_id_count = scan_id_count; + os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); + wpa_s->scan_res_handler = scan_res_handler; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = manual_scan_freqs; + manual_scan_freqs = NULL; + wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->after_wps = 0; @@ -6102,6 +6470,16 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, wpa_s->manual_scan_id); } } else if (wpa_s->sched_scanning) { + wpa_s->manual_scan_passive = manual_scan_passive; + wpa_s->manual_scan_use_id = manual_scan_use_id; + wpa_s->manual_scan_only_new = manual_scan_only_new; + wpa_s->scan_id_count = scan_id_count; + os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); + wpa_s->scan_res_handler = scan_res_handler; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = manual_scan_freqs; + manual_scan_freqs = NULL; + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_s->scan_req = MANUAL_SCAN_REQ; @@ -6117,6 +6495,9 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request"); *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); } + +done: + os_free(manual_scan_freqs); } @@ -6256,6 +6637,228 @@ static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) return 0; } + +static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 src[ETH_ALEN], *buf; + int used; + size_t len; + + wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + wpa_supplicant_rx_eapol(wpa_s, src, buf, len); + os_free(buf); + + return 0; +} + + +static u16 ipv4_hdr_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len / 2; i++) + sum += *pos++; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return sum ^ 0xffff; +} + + +#define HWSIM_PACKETLEN 1500 +#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct ether_header *eth; + const struct iphdr *ip; + const u8 *pos; + unsigned int i; + + if (len != HWSIM_PACKETLEN) + return; + + eth = (const struct ether_header *) buf; + ip = (const struct iphdr *) (eth + 1); + pos = (const u8 *) (ip + 1); + + if (ip->ihl != 5 || ip->version != 4 || + ntohs(ip->tot_len) != HWSIM_IP_LEN) + return; + + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + if (*pos != (u8) i) + return; + pos++; + } + + wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); +} + + +static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int enabled = atoi(cmd); + + if (!enabled) { + if (wpa_s->l2_test) { + l2_packet_deinit(wpa_s->l2_test); + wpa_s->l2_test = NULL; + wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled"); + } + return 0; + } + + if (wpa_s->l2_test) + return 0; + + wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, + ETHERTYPE_IP, wpas_data_test_rx, + wpa_s, 1); + if (wpa_s->l2_test == NULL) + return -1; + + wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled"); + + return 0; +} + + +static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst[ETH_ALEN], src[ETH_ALEN]; + char *pos; + int used; + long int val; + u8 tos; + u8 buf[HWSIM_PACKETLEN]; + struct ether_header *eth; + struct iphdr *ip; + u8 *dpos; + unsigned int i; + + if (wpa_s->l2_test == NULL) + return -1; + + /* format: <dst> <src> <tos> */ + + pos = cmd; + used = hwaddr_aton2(pos, dst); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + + val = strtol(pos, NULL, 0); + if (val < 0 || val > 0xff) + return -1; + tos = val; + + eth = (struct ether_header *) buf; + os_memcpy(eth->ether_dhost, dst, ETH_ALEN); + os_memcpy(eth->ether_shost, src, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IP); + ip = (struct iphdr *) (eth + 1); + os_memset(ip, 0, sizeof(*ip)); + ip->ihl = 5; + ip->version = 4; + ip->ttl = 64; + ip->tos = tos; + ip->tot_len = htons(HWSIM_IP_LEN); + ip->protocol = 1; + ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); + dpos = (u8 *) (ip + 1); + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + *dpos++ = i; + + if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf, + HWSIM_PACKETLEN) < 0) + return -1; + + wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR + " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); + + return 0; +} + + +static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 *buf; + struct ether_header *eth; + struct l2_packet_data *l2 = NULL; + size_t len; + u16 ethertype; + int res = -1; + + len = os_strlen(cmd); + if (len & 1 || len < ETH_HLEN * 2) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, buf, len) < 0) + goto done; + + eth = (struct ether_header *) buf; + ethertype = ntohs(eth->ether_type); + + l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype, + wpas_data_test_rx, wpa_s, 1); + if (l2 == NULL) + goto done; + + res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); + wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res); +done: + if (l2) + l2_packet_deinit(l2); + os_free(buf); + + return res < 0 ? -1 : 0; +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -6268,8 +6871,13 @@ static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s) for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { if (wpa_s->vendor_elem[i]) { - os_snprintf(buf, sizeof(buf), "frame[%u]", i); - wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]); + int res; + + res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); + if (!os_snprintf_error(sizeof(buf), res)) { + wpa_hexdump_buf(MSG_DEBUG, buf, + wpa_s->vendor_elem[i]); + } } } @@ -6463,6 +7071,171 @@ static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) } +static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (neighbor_rep) { + wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED + "length=%u", + (unsigned int) wpabuf_len(neighbor_rep)); + wpabuf_free(neighbor_rep); + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); + } +} + + +static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, + char *cmd) +{ + struct wpa_ssid ssid; + struct wpa_ssid *ssid_p = NULL; + int ret = 0; + + if (os_strncmp(cmd, " ssid=", 6) == 0) { + ssid.ssid_len = os_strlen(cmd + 6); + if (ssid.ssid_len > 32) + return -1; + ssid.ssid = (u8 *) (cmd + 6); + ssid_p = &ssid; + } + + ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, + wpas_ctrl_neighbor_rep_cb, + wpa_s); + + return ret; +} + + +static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s) +{ + eapol_sm_erp_flush(wpa_s->eapol); + return 0; +} + + +static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *token, *context = NULL; + unsigned int enable = ~0, type = 0; + u8 _addr[ETH_ALEN], _mask[ETH_ALEN]; + u8 *addr = NULL, *mask = NULL; + + while ((token = str_token(cmd, " ", &context))) { + if (os_strcasecmp(token, "scan") == 0) { + type |= MAC_ADDR_RAND_SCAN; + } else if (os_strcasecmp(token, "sched") == 0) { + type |= MAC_ADDR_RAND_SCHED_SCAN; + } else if (os_strcasecmp(token, "pno") == 0) { + type |= MAC_ADDR_RAND_PNO; + } else if (os_strcasecmp(token, "all") == 0) { + type = wpa_s->mac_addr_rand_supported; + } else if (os_strncasecmp(token, "enable=", 7) == 0) { + enable = atoi(token + 7); + } else if (os_strncasecmp(token, "addr=", 5) == 0) { + addr = _addr; + if (hwaddr_aton(token + 5, addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC address: %s", + token); + return -1; + } + } else if (os_strncasecmp(token, "mask=", 5) == 0) { + mask = _mask; + if (hwaddr_aton(token + 5, mask)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC address mask: %s", + token); + return -1; + } + } else { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC_RAND_SCAN parameter: %s", + token); + return -1; + } + } + + if (!type) { + wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified"); + return -1; + } + + if ((wpa_s->mac_addr_rand_supported & type) != type) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN types=%u != supported=%u", + type, wpa_s->mac_addr_rand_supported); + return -1; + } + + if (enable > 1) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN enable=<0/1> not specified"); + return -1; + } + + if (!enable) { + wpas_mac_addr_rand_scan_clear(wpa_s, type); + if (wpa_s->pno) { + if (type & MAC_ADDR_RAND_PNO) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } else if (wpa_s->sched_scanning && + (type & MAC_ADDR_RAND_SCHED_SCAN)) { + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } + return 0; + } + + if ((addr && !mask) || (!addr && mask)) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN invalid addr/mask combination"); + return -1; + } + + if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN cannot allow multicast address"); + return -1; + } + + if (type & MAC_ADDR_RAND_SCAN) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, + addr, mask); + } + + if (type & MAC_ADDR_RAND_SCHED_SCAN) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, + addr, mask); + + if (wpa_s->sched_scanning && !wpa_s->pno) { + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } + } + + if (type & MAC_ADDR_RAND_PNO) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, + addr, mask); + if (wpa_s->pno) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } + + return 0; +} + + char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len) { @@ -6515,13 +7288,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "MIB") == 0) { reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size); if (reply_len >= 0) { - int res; - res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len, - reply_size - reply_len); - if (res < 0) - reply_len = -1; - else - reply_len += res; + reply_len += eapol_sm_get_mib(wpa_s->eapol, + reply + reply_len, + reply_size - reply_len); } } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( @@ -6642,8 +7411,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpas_wps_er_start(wpa_s, buf + 13)) reply_len = -1; } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { - if (wpas_wps_er_stop(wpa_s)) - reply_len = -1; + wpas_wps_er_stop(wpa_s); } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) reply_len = -1; @@ -6682,6 +7450,21 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_IBSS_RSN */ +#ifdef CONFIG_MESH + } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, buf + 19, reply, reply_size); + } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, "", reply, reply_size); + } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, + buf + 18)) + reply_len = -1; +#endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { if (p2p_ctrl_find(wpa_s, buf + 9)) @@ -6965,8 +7748,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5)) reply_len = -1; } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) { - if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16)) - reply_len = -1; + wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0; } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) { if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15)) reply_len = -1; @@ -6975,8 +7757,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { - if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10)) - reply_len = -1; + wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10); #ifdef CONFIG_TDLS } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) @@ -6987,7 +7768,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s, + buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, + buf + 24)) + reply_len = -1; #endif /* CONFIG_TDLS */ + } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { + reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) { + if (wmm_ac_ctrl_addts(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) { + if (wmm_ac_ctrl_delts(wpa_s, buf + 13)) + reply_len = -1; } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); @@ -7032,6 +7829,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { + if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) { + if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { + if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { + if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) @@ -7042,6 +7851,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) { if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) reply_len = -1; + } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { + if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20)) + reply_len = -1; + } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { + wpas_ctrl_iface_erp_flush(wpa_s); + } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { + if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -7192,7 +8009,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", tmp->drv_name, tmp->ifname, tmp->desc ? tmp->desc : ""); - if (res < 0 || res >= end - pos) { + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } @@ -7218,7 +8035,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, while (wpa_s) { res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); - if (res < 0 || res >= end - pos) { + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } @@ -7433,12 +8250,12 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global, "p2p_state=%s\n", MAC2STR(global->p2p_dev_addr), p2p_get_state_txt(global->p2p)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } else if (global->p2p) { ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -7447,7 +8264,7 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global, #ifdef CONFIG_WIFI_DISPLAY ret = os_snprintf(pos, end - pos, "wifi_display=%d\n", !!global->wifi_display); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #endif /* CONFIG_WIFI_DISPLAY */ @@ -7456,7 +8273,7 @@ static int wpas_global_ctrl_iface_status(struct wpa_global *global, ret = os_snprintf(pos, end - pos, "ifname=%s\n" "address=" MACSTR "\n", wpa_s->ifname, MAC2STR(wpa_s->own_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -7550,6 +8367,9 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, if (wpas_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c index 9d0674de..bf6a3df6 100644 --- a/wpa_supplicant/ctrl_iface_udp.c +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -218,7 +218,8 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } @@ -356,7 +357,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) priv->sock = socket(domain, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_INET)"); + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); goto fail; } @@ -386,7 +387,7 @@ try_again: port--; if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) goto try_again; - perror("bind(AF_INET)"); + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; } @@ -482,7 +483,9 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, if (sendto(priv->sock, sbuf, llen + len, 0, (struct sockaddr *) &dst->addr, sizeof(dst->addr)) < 0) { - perror("sendto(CTRL_IFACE monitor)"); + wpa_printf(MSG_ERROR, + "sendto(CTRL_IFACE monitor): %s", + strerror(errno)); dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( @@ -551,7 +554,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } @@ -634,7 +638,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) priv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_INET)"); + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); goto fail; } @@ -652,7 +656,7 @@ try_again: if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) goto try_again; - perror("bind(AF_INET)"); + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; } diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 40082e24..2c1c6a05 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -47,6 +47,7 @@ struct ctrl_iface_priv { struct wpa_supplicant *wpa_s; int sock; struct dl_list ctrl_dst; + int android_control_socket; }; @@ -54,6 +55,7 @@ struct ctrl_iface_global_priv { struct wpa_global *global; int sock; struct dl_list ctrl_dst; + int android_control_socket; }; @@ -270,7 +272,7 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname); - if (res < 0 || (size_t) res >= len) { + if (os_snprintf_error(len, res)) { os_free(pbuf); os_free(buf); return NULL; @@ -340,8 +342,10 @@ static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s, os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s", wpa_s->conf->ctrl_interface); priv->sock = android_get_control_socket(addr.sun_path); - if (priv->sock >= 0) + if (priv->sock >= 0) { + priv->android_control_socket = 1; goto havesock; + } #endif /* ANDROID */ if (os_strncmp(buf, "DIR=", 4) == 0) { dir = buf + 4; @@ -556,6 +560,16 @@ static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, if (priv->sock <= 0) return -1; + /* + * On Android, the control socket being used may be the socket + * that is created when wpa_supplicant is started as a /init.*.rc + * service. Such a socket is maintained as a key-value pair in + * Android's environment. Closing this control socket would leave us + * in a bad state with an invalid socket descriptor. + */ + if (priv->android_control_socket) + return priv->sock; + eloop_unregister_read_sock(priv->sock); close(priv->sock); priv->sock = -1; @@ -657,7 +671,7 @@ static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, return; res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); - if (res < 0 || (size_t) res >= sizeof(levelstr)) + if (os_snprintf_error(sizeof(levelstr), res)) return; idx = 0; if (ifname) { @@ -870,6 +884,7 @@ static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, } wpa_printf(MSG_DEBUG, "Using Android control socket '%s'", ctrl + 9); + priv->android_control_socket = 1; goto havesock; } @@ -884,6 +899,7 @@ static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, wpa_printf(MSG_DEBUG, "Using Android control socket '%s'", ctrl); + priv->android_control_socket = 1; goto havesock; } } @@ -1064,6 +1080,16 @@ static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, if (priv->sock <= 0) return -1; + /* + * On Android, the control socket being used may be the socket + * that is created when wpa_supplicant is started as a /init.*.rc + * service. Such a socket is maintained as a key-value pair in + * Android's environment. Closing this control socket would leave us + * in a bad state with an invalid socket descriptor. + */ + if (priv->android_control_socket) + return priv->sock; + eloop_unregister_read_sock(priv->sock); close(priv->sock); priv->sock = -1; diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c index 5cc15059..7ef6cad6 100644 --- a/wpa_supplicant/dbus/dbus_common.c +++ b/wpa_supplicant/dbus/dbus_common.c @@ -165,6 +165,7 @@ static void process_timeout(void *eloop_ctx, void *sock_ctx) static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { struct wpas_dbus_priv *priv = data; + if (!dbus_timeout_get_enabled(timeout)) return TRUE; @@ -180,6 +181,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) static void remove_timeout(DBusTimeout *timeout, void *data) { struct wpas_dbus_priv *priv = data; + eloop_cancel_timeout(process_timeout, priv, timeout); dbus_timeout_set_data(timeout, NULL, NULL); } @@ -244,8 +246,7 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv) remove_timeout, timeout_toggled, priv, NULL)) { - wpa_printf(MSG_ERROR, "dbus: Failed to set callback " - "functions"); + wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions"); return -1; } @@ -259,12 +260,12 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv) static DBusHandlerResult disconnect_filter(DBusConnection *conn, - DBusMessage *message, void *data) + DBusMessage *message, void *data) { struct wpas_dbus_priv *priv = data; if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, - "Disconnected")) { + "Disconnected")) { wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating"); dbus_connection_set_exit_on_disconnect(conn, FALSE); wpa_supplicant_terminate_proc(priv->global); @@ -284,10 +285,11 @@ static int wpas_dbus_init_common(struct wpas_dbus_priv *priv) priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); if (priv->con) { dbus_connection_add_filter(priv->con, disconnect_filter, priv, - NULL); + NULL); } else { - wpa_printf(MSG_ERROR, "dbus: Could not acquire the system " - "bus: %s - %s", error.name, error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not acquire the system bus: %s - %s", + error.name, error.message); ret = -1; } dbus_error_free(&error); @@ -309,7 +311,7 @@ static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv) * FIXME: is there a better solution to this problem? */ eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, - priv->con, NULL); + priv->con, NULL); return 0; } @@ -345,26 +347,14 @@ struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) return NULL; priv->global = global; - if (wpas_dbus_init_common(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } - + if (wpas_dbus_init_common(priv) < 0 || #ifdef CONFIG_CTRL_IFACE_DBUS_NEW - if (wpas_dbus_ctrl_iface_init(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } + wpas_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ - #ifdef CONFIG_CTRL_IFACE_DBUS - if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } + wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS */ - - if (wpas_dbus_init_common_finish(priv) < 0) { + wpas_dbus_init_common_finish(priv) < 0) { wpas_dbus_deinit(priv); return NULL; } diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c index 949ce7c9..317661a9 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -66,7 +66,7 @@ dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, const char * wpa_dbus_type_as_string(const int type) { - switch(type) { + switch (type) { case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING; case DBUS_TYPE_BOOLEAN: @@ -106,11 +106,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_start( iter_dict_entry)) return FALSE; - if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, - &key)) - return FALSE; - - return TRUE; + return dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, + &key); } @@ -120,10 +117,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_end( { if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val)) return FALSE; - if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry)) - return FALSE; - return TRUE; + return dbus_message_iter_close_container(iter_dict, iter_dict_entry); } @@ -143,22 +138,15 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict, return FALSE; if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, - key, value_type)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_entry, + key, value_type) || + !dbus_message_iter_open_container(&iter_dict_entry, DBUS_TYPE_VARIANT, - type_as_string, &iter_dict_val)) + type_as_string, &iter_dict_val) || + !dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) return FALSE; - if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) - return FALSE; - - if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, - &iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val); } @@ -170,17 +158,13 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( dbus_uint32_t i; if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, - key, DBUS_TYPE_ARRAY)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_entry, + key, DBUS_TYPE_ARRAY) || + !dbus_message_iter_open_container(&iter_dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, - &iter_dict_val)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, + &iter_dict_val) || + !dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &iter_array)) return FALSE; @@ -195,11 +179,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array)) return FALSE; - if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, - &iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val); } @@ -428,9 +409,7 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, const char *value, const dbus_uint32_t value_len) { - if (!key) - return FALSE; - if (!value && (value_len != 0)) + if (!key || (!value && value_len != 0)) return FALSE; return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value, value_len); @@ -465,27 +444,20 @@ dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict, err = os_snprintf(array_type, sizeof(array_type), DBUS_TYPE_ARRAY_AS_STRING "%s", type); - if (err < 0 || err > (int) sizeof(array_type)) - return FALSE; - - if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) - return FALSE; - - if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, - key, DBUS_TYPE_ARRAY)) + if (os_snprintf_error(sizeof(array_type), err)) return FALSE; - if (!dbus_message_iter_open_container(iter_dict_entry, + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array || + !_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, + key, DBUS_TYPE_ARRAY) || + !dbus_message_iter_open_container(iter_dict_entry, DBUS_TYPE_VARIANT, array_type, iter_dict_val)) return FALSE; - if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, - type, iter_array)) - return FALSE; - - return TRUE; + return dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, + type, iter_array); } @@ -542,10 +514,8 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, DBusMessageIter iter_bytes; size_t i; - if (!iter_array || !value) - return FALSE; - - if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY, + if (!iter_array || !value || + !dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &iter_bytes)) return FALSE; @@ -557,10 +527,7 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, return FALSE; } - if (!dbus_message_iter_close_container(iter_array, &iter_bytes)) - return FALSE; - - return TRUE; + return dbus_message_iter_close_container(iter_array, &iter_bytes); } @@ -586,17 +553,12 @@ dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_val, DBusMessageIter *iter_array) { - if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array || + !dbus_message_iter_close_container(iter_dict_val, iter_array)) return FALSE; - if (!dbus_message_iter_close_container(iter_dict_val, iter_array)) - return FALSE; - - if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, - iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, + iter_dict_val); } @@ -619,12 +581,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; dbus_uint32_t i; - if (!key) - return FALSE; - if (!items && (num_items != 0)) - return FALSE; - - if (!wpa_dbus_dict_begin_string_array(iter_dict, key, + if (!key || (!items && num_items != 0) || + !wpa_dbus_dict_begin_string_array(iter_dict, key, &iter_dict_entry, &iter_dict_val, &iter_array)) return FALSE; @@ -635,11 +593,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, return FALSE; } - if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, - &iter_dict_val, &iter_array)) - return FALSE; - - return TRUE; + return wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array); } @@ -662,12 +617,9 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, DBusMessageIter iter_dict_entry, iter_dict_val, iter_array; dbus_uint32_t i; - if (!key) - return FALSE; - if (!items && (num_items != 0)) - return FALSE; - - if (!wpa_dbus_dict_begin_array(iter_dict, key, + if (!key || + (!items && num_items != 0) || + !wpa_dbus_dict_begin_array(iter_dict, key, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &iter_dict_entry, &iter_dict_val, @@ -681,11 +633,8 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, return FALSE; } - if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry, - &iter_dict_val, &iter_array)) - return FALSE; - - return TRUE; + return wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array); } @@ -707,16 +656,25 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, DBusMessageIter *iter_dict, DBusError *error) { + int type; + + wpa_printf(MSG_MSGDUMP, "%s: start reading a dict entry", __func__); if (!iter || !iter_dict) { dbus_set_error_const(error, DBUS_ERROR_FAILED, - "[internal] missing message iterators"); + "[internal] missing message iterators"); return FALSE; } - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + type = dbus_message_iter_get_arg_type(iter); + if (type != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { + wpa_printf(MSG_DEBUG, + "%s: unexpected message argument types (arg=%c element=%c)", + __func__, type, + type != DBUS_TYPE_ARRAY ? '?' : + dbus_message_iter_get_element_type(iter)); dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, - "unexpected message argument types"); + "unexpected message argument types"); return FALSE; } @@ -753,10 +711,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( BYTE_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" - "entry_get_byte_array out of " - "memory trying to retrieve the " - "string array"); + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to retrieve the string array", + __func__); goto done; } buffer = nbuffer; @@ -768,6 +725,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( entry->array_len = ++count; dbus_message_iter_next(iter); } + wpa_hexdump_key(MSG_MSGDUMP, "dbus: byte array contents", + entry->bytearray_value, entry->array_len); /* Zero-length arrays are valid. */ if (entry->array_len == 0) { @@ -812,10 +771,9 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( STR_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" - "entry_get_string_array out of " - "memory trying to retrieve the " - "string array"); + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to retrieve the string array", + __func__); goto done; } buffer = nbuffer; @@ -823,17 +781,21 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( entry->strarray_value = buffer; dbus_message_iter_get_basic(iter, &value); + wpa_printf(MSG_MSGDUMP, "%s: string_array value: %s", + __func__, wpa_debug_show_keys ? value : "[omitted]"); str = os_strdup(value); if (str == NULL) { - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_" - "string_array out of memory trying to " - "duplicate the string array"); + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to duplicate the string array", + __func__); goto done; } entry->strarray_value[count] = str; entry->array_len = ++count; dbus_message_iter_next(iter); } + wpa_printf(MSG_MSGDUMP, "%s: string_array length %u", + __func__, entry->array_len); /* Zero-length arrays are valid. */ if (entry->array_len == 0) { @@ -856,15 +818,31 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( { struct wpa_dbus_dict_entry tmpentry; size_t buflen = 0; - int i; - - if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE) - return FALSE; + int i, type; entry->array_type = WPAS_DBUS_TYPE_BINARRAY; entry->array_len = 0; entry->binarray_value = NULL; + type = dbus_message_iter_get_arg_type(iter); + wpa_printf(MSG_MSGDUMP, "%s: parsing binarray type %c", __func__, type); + if (type == DBUS_TYPE_INVALID) { + /* Likely an empty array of arrays */ + return TRUE; + } + if (type != DBUS_TYPE_ARRAY) { + wpa_printf(MSG_DEBUG, "%s: not an array type: %c", + __func__, type); + return FALSE; + } + + type = dbus_message_iter_get_element_type(iter); + if (type != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, "%s: unexpected element type %c", + __func__, type); + return FALSE; + } + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) { DBusMessageIter iter_array; @@ -884,7 +862,7 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( os_memset(&tmpentry, 0, sizeof(tmpentry)); tmpentry.type = DBUS_TYPE_ARRAY; if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry) - == FALSE) + == FALSE) goto cleanup; entry->binarray_value[entry->array_len] = @@ -897,6 +875,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( entry->array_len++; dbus_message_iter_next(iter); } + wpa_printf(MSG_MSGDUMP, "%s: binarray length %u", + __func__, entry->array_len); return TRUE; @@ -917,12 +897,11 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( dbus_bool_t success = FALSE; DBusMessageIter iter_array; - if (!entry) - return FALSE; + wpa_printf(MSG_MSGDUMP, "%s: array_type %c", __func__, array_type); dbus_message_iter_recurse(iter_dict_val, &iter_array); - switch (array_type) { + switch (array_type) { case DBUS_TYPE_BYTE: success = _wpa_dbus_dict_entry_get_byte_array(&iter_array, entry); @@ -936,6 +915,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry); break; default: + wpa_printf(MSG_MSGDUMP, "%s: unsupported array type %c", + __func__, array_type); break; } @@ -950,42 +931,72 @@ static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant( switch (entry->type) { case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(iter, &v); + wpa_printf(MSG_MSGDUMP, "%s: object path value: %s", + __func__, v); + entry->str_value = os_strdup(v); + if (entry->str_value == NULL) + return FALSE; + break; case DBUS_TYPE_STRING: dbus_message_iter_get_basic(iter, &v); + wpa_printf(MSG_MSGDUMP, "%s: string value: %s", + __func__, wpa_debug_show_keys ? v : "[omitted]"); entry->str_value = os_strdup(v); if (entry->str_value == NULL) return FALSE; break; case DBUS_TYPE_BOOLEAN: dbus_message_iter_get_basic(iter, &entry->bool_value); + wpa_printf(MSG_MSGDUMP, "%s: boolean value: %d", + __func__, entry->bool_value); break; case DBUS_TYPE_BYTE: dbus_message_iter_get_basic(iter, &entry->byte_value); + wpa_printf(MSG_MSGDUMP, "%s: byte value: %d", + __func__, entry->byte_value); break; case DBUS_TYPE_INT16: dbus_message_iter_get_basic(iter, &entry->int16_value); + wpa_printf(MSG_MSGDUMP, "%s: int16 value: %d", + __func__, entry->int16_value); break; case DBUS_TYPE_UINT16: dbus_message_iter_get_basic(iter, &entry->uint16_value); + wpa_printf(MSG_MSGDUMP, "%s: uint16 value: %d", + __func__, entry->uint16_value); break; case DBUS_TYPE_INT32: dbus_message_iter_get_basic(iter, &entry->int32_value); + wpa_printf(MSG_MSGDUMP, "%s: int32 value: %d", + __func__, entry->int32_value); break; case DBUS_TYPE_UINT32: dbus_message_iter_get_basic(iter, &entry->uint32_value); + wpa_printf(MSG_MSGDUMP, "%s: uint32 value: %d", + __func__, entry->uint32_value); break; case DBUS_TYPE_INT64: dbus_message_iter_get_basic(iter, &entry->int64_value); + wpa_printf(MSG_MSGDUMP, "%s: int64 value: %lld", + __func__, (long long int) entry->int64_value); break; case DBUS_TYPE_UINT64: dbus_message_iter_get_basic(iter, &entry->uint64_value); + wpa_printf(MSG_MSGDUMP, "%s: uint64 value: %llu", + __func__, + (unsigned long long int) entry->uint64_value); break; case DBUS_TYPE_DOUBLE: dbus_message_iter_get_basic(iter, &entry->double_value); + wpa_printf(MSG_MSGDUMP, "%s: double value: %f", + __func__, entry->double_value); break; case DBUS_TYPE_ARRAY: return _wpa_dbus_dict_entry_get_array(iter, entry); default: + wpa_printf(MSG_MSGDUMP, "%s: unsupported type %c", + __func__, entry->type); return FALSE; } @@ -1016,26 +1027,40 @@ dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, int type; const char *key; - if (!iter_dict || !entry) - goto error; - - if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) + if (!iter_dict || !entry || + dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) { + wpa_printf(MSG_DEBUG, "%s: not a dict entry", __func__); goto error; + } dbus_message_iter_recurse(iter_dict, &iter_dict_entry); dbus_message_iter_get_basic(&iter_dict_entry, &key); + wpa_printf(MSG_MSGDUMP, "%s: dict entry key: %s", __func__, key); entry->key = key; - if (!dbus_message_iter_next(&iter_dict_entry)) + if (!dbus_message_iter_next(&iter_dict_entry)) { + wpa_printf(MSG_DEBUG, "%s: no variant in dict entry", __func__); goto error; + } type = dbus_message_iter_get_arg_type(&iter_dict_entry); - if (type != DBUS_TYPE_VARIANT) + if (type != DBUS_TYPE_VARIANT) { + wpa_printf(MSG_DEBUG, + "%s: unexpected dict entry variant type: %c", + __func__, type); goto error; + } dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val); entry->type = dbus_message_iter_get_arg_type(&iter_dict_val); - if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) + wpa_printf(MSG_MSGDUMP, "%s: dict entry variant content type: %c", + __func__, entry->type); + entry->array_type = DBUS_TYPE_INVALID; + if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) { + wpa_printf(MSG_DEBUG, + "%s: failed to fetch dict values from variant", + __func__); goto error; + } dbus_message_iter_next(iter_dict); return TRUE; diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h index 96663494..b068431a 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -72,28 +72,28 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, /* Manual construction and addition of array elements */ dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict, - const char *key, const char *type, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array); + const char *key, const char *type, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict, - const char *key, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array); + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, - const char *elem); + const char *elem); dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, const u8 *value, size_t value_len); dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict, - DBusMessageIter *iter_dict_entry, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array); + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); static inline dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict, @@ -120,7 +120,11 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, * Reading a dict from a DBusMessage */ -#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100) +/* + * Used only in struct wpa_dbus_dict_entry::array_type internally to identify + * special binary array case. + */ +#define WPAS_DBUS_TYPE_BINARRAY ((int) '@') struct wpa_dbus_dict_entry { int type; /** the dbus type of the dict entry's value */ diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 5e58c5ba..b21b7a86 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -75,8 +75,7 @@ static DBusHandlerResult noc_filter(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) - { + for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s->preq_notify_peer != NULL && os_strcmp(name, wpa_s->preq_notify_peer) == 0 && (new_owner == NULL || os_strlen(new_owner) == 0)) { @@ -148,22 +147,14 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &wpa_s->dbus_new_path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties( - iface, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, &iter)) - goto err; - } - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + &wpa_s->dbus_new_path) || + (properties && + !wpa_dbus_get_object_properties( + iface, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -229,7 +220,7 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) /** - * wpas_dbus_signal_blob - Send a BSS related event signal + * wpas_dbus_signal_bss - Send a BSS related event signal * @wpa_s: %wpa_supplicant network interface data * @bss_obj_path: BSS object path * @sig_name: signal name - BSSAdded or BSSRemoved @@ -259,22 +250,14 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &bss_obj_path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties(iface, bss_obj_path, - WPAS_DBUS_NEW_IFACE_BSS, - &iter)) - goto err; - } - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + &bss_obj_path) || + (properties && + !wpa_dbus_get_object_properties(iface, bss_obj_path, + WPAS_DBUS_NEW_IFACE_BSS, + &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -407,23 +390,14 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = net_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties( - iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK, - &iter)) - goto err; - } - - dbus_connection_send(iface->con, msg, NULL); - - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + &path) || + (properties && + !wpa_dbus_get_object_properties( + iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK, + &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -513,19 +487,12 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &net_ptr)) - goto err; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field)) - goto err; - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt)) - goto err; - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + &net_ptr) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -543,6 +510,7 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, { char path[WPAS_DBUS_OBJECT_PATH_MAX]; + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", wpa_s->dbus_new_path, ssid->id); @@ -749,15 +717,11 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, if (cred->encr_type & WPS_ENCR_AES) encr_type[et_num++] = "aes"; - if (wpa_s->current_ssid) { - if (!wpa_dbus_dict_append_byte_array( - &dict_iter, "BSSID", - (const char *) wpa_s->current_ssid->bssid, - ETH_ALEN)) - goto nomem; - } - - if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", + if ((wpa_s->current_ssid && + !wpa_dbus_dict_append_byte_array( + &dict_iter, "BSSID", + (const char *) wpa_s->current_ssid->bssid, ETH_ALEN)) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", (const char *) cred->ssid, cred->ssid_len) || !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType", @@ -804,29 +768,20 @@ void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, return; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto nomem; - - if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) || - !wpa_dbus_dict_append_string(&dict_iter, "subject", subject)) - goto nomem; - - if (cert_hash && - !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash)) - goto nomem; - - if (cert && - !wpa_dbus_dict_append_byte_array(&dict_iter, "cert", - wpabuf_head(cert), - wpabuf_len(cert))) - goto nomem; - - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto nomem; - - dbus_connection_send(iface->con, msg, NULL); - -nomem: + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) || + !wpa_dbus_dict_append_string(&dict_iter, "subject", subject) || + (cert_hash && + !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", + cert_hash)) || + (cert && + !wpa_dbus_dict_append_byte_array(&dict_iter, "cert", + wpabuf_head(cert), + wpabuf_len(cert))) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -852,15 +807,12 @@ void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) - || + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, ¶meter)) - goto nomem; - - dbus_connection_send(iface->con, msg, NULL); - -nomem: + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -946,49 +898,40 @@ void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, const char *role) { - int error = 1; DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface = wpa_s->global->dbus; + struct wpa_supplicant *parent; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + if (!wpa_s->dbus_groupobj_path) return; - msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path, + msg = dbus_message_new_signal(parent->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "GroupFinished"); if (msg == NULL) return; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto nomem; - - if (!wpa_dbus_dict_append_object_path(&dict_iter, + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "interface_object", - wpa_s->dbus_new_path)) - goto nomem; - - if (!wpa_dbus_dict_append_string(&dict_iter, "role", role)) - goto nomem; - - if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + wpa_s->dbus_new_path) || + !wpa_dbus_dict_append_string(&dict_iter, "role", role) || + !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto nomem; - - error = 0; - dbus_connection_send(iface->con, msg, NULL); - -nomem: - if (error > 0) - wpa_printf(MSG_ERROR, - "dbus: Failed to construct GroupFinished"); - + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -1034,6 +977,9 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + if (request || !status) { if (config_methods & WPS_CONFIG_DISPLAY) _signal = request ? @@ -1048,9 +994,10 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, "ProvisionDiscoveryPBCResponse"; else return; /* Unknown or un-supported method */ - } else if (!request && status) + } else { /* Explicit check for failure response */ _signal = "ProvisionDiscoveryFailure"; + } add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) || (!request && !status && @@ -1119,6 +1066,9 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(src)); @@ -1245,8 +1195,13 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; + struct wpa_supplicant *parent; - iface = wpa_s->parent->global->dbus; + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + + iface = parent->global->dbus; /* Do nothing if the control interface is not turned on */ if (iface == NULL) @@ -1256,41 +1211,33 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, return; /* New interface has been created for this group */ - msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path, + msg = dbus_message_new_signal(parent->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "GroupStarted"); if (msg == NULL) return; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto nomem; - /* * In case the device supports creating a separate interface the * DBus client will need to know the object path for the interface * object this group was created on, so include it here. */ - if (!wpa_dbus_dict_append_object_path(&dict_iter, - "interface_object", - wpa_s->dbus_new_path)) - goto nomem; - - if (!wpa_dbus_dict_append_string(&dict_iter, "role", - client ? "client" : "GO")) - goto nomem; - - if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, + "interface_object", + wpa_s->dbus_new_path) || + !wpa_dbus_dict_append_string(&dict_iter, "role", + client ? "client" : "GO") || + !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || - !wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto nomem; - - dbus_connection_send(iface->con, msg, NULL); - - if (client) - peer_groups_changed(wpa_s); - -nomem: + !wpa_dbus_dict_close_write(&iter, &dict_iter)) { + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + } else { + dbus_connection_send(iface->con, msg, NULL); + if (client) + peer_groups_changed(wpa_s); + } dbus_message_unref(msg); } @@ -1315,6 +1262,9 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_memset(freqs, 0, sizeof(freqs)); /* Do nothing if the control interface is not turned on */ if (iface == NULL) @@ -1333,9 +1283,8 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, return; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto err; - if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", path) || !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status)) goto err; @@ -1344,15 +1293,10 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, int i = 0; int freq_list_num = 0; - if (res->role_go) { - if (!wpa_dbus_dict_append_byte_array( - &dict_iter, "passphrase", - (const char *) res->passphrase, - sizeof(res->passphrase))) - goto err; - } - - if (!wpa_dbus_dict_append_string(&dict_iter, "role_go", + if ((res->role_go && + !wpa_dbus_dict_append_string(&dict_iter, "passphrase", + res->passphrase)) || + !wpa_dbus_dict_append_string(&dict_iter, "role_go", res->role_go ? "GO" : "client") || !wpa_dbus_dict_append_int32(&dict_iter, "frequency", @@ -1387,22 +1331,16 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, DBUS_TYPE_INT32_AS_STRING, &iter_dict_entry, &iter_dict_val, - &iter_dict_array)) - goto err; - - if (!dbus_message_iter_append_fixed_array(&iter_dict_array, + &iter_dict_array) || + !dbus_message_iter_append_fixed_array(&iter_dict_array, DBUS_TYPE_INT32, &f_array, - freq_list_num)) - goto err; - - if (!wpa_dbus_dict_end_array(&dict_iter, + freq_list_num) || + !wpa_dbus_dict_end_array(&dict_iter, &iter_dict_entry, &iter_dict_val, - &iter_dict_array)) - goto err; - - if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group", + &iter_dict_array) || + !wpa_dbus_dict_append_int32(&dict_iter, "persistent_group", res->persistent_group) || !wpa_dbus_dict_append_uint32(&dict_iter, "peer_config_timeout", @@ -1441,6 +1379,9 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "InvitationResult"); @@ -1449,23 +1390,16 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, return; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto nomem; - - if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status)) - goto nomem; - if (bssid) { - if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID", - (const char *) bssid, - ETH_ALEN)) - goto nomem; - } - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto nomem; - - dbus_connection_send(iface->con, msg, NULL); - -nomem: + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_int32(&dict_iter, "status", status) || + (bssid && + !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID", + (const char *) bssid, + ETH_ALEN)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -1486,6 +1420,7 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + struct wpa_supplicant *parent; iface = wpa_s->global->dbus; @@ -1496,10 +1431,14 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, if (!wpa_s->dbus_groupobj_path) return; + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->parent->dbus_new_path, MAC2STR(peer_addr)); + parent->dbus_new_path, MAC2STR(peer_addr)); msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, WPAS_DBUS_NEW_IFACE_P2P_GROUP, @@ -1510,18 +1449,12 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = peer_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) - goto err; - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - - wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr); - - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + &path)) { + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + } else { + dbus_connection_send(iface->con, msg, NULL); + wpas_dbus_signal_peer_groups_changed(parent, peer_addr); + } dbus_message_unref(msg); } @@ -1542,6 +1475,7 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + struct wpa_supplicant *parent; iface = wpa_s->global->dbus; @@ -1552,10 +1486,14 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, if (!wpa_s->dbus_groupobj_path) return; + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_groupobj_path, MAC2STR(peer_addr)); + parent->dbus_new_path, MAC2STR(peer_addr)); msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, WPAS_DBUS_NEW_IFACE_P2P_GROUP, @@ -1566,19 +1504,13 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = peer_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) - goto err; - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - - wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr); - - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected " - "signal"); + &path)) { + wpa_printf(MSG_ERROR, + "dbus: Failed to construct PeerDisconnected signal"); + } else { + dbus_connection_send(iface->con, msg, NULL); + wpas_dbus_signal_peer_groups_changed(parent, peer_addr); + } dbus_message_unref(msg); } @@ -1605,22 +1537,26 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + return; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ServiceDiscoveryRequest"); if (msg == NULL) return; - /* Check if this is a known peer */ - if (!p2p_peer_known(wpa_s->global->p2p, sa)) - goto error; - os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa)); @@ -1628,11 +1564,8 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, path = peer_obj_path; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto error; - - - if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", path) || !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) || !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token", @@ -1643,13 +1576,9 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, (const char *) tlvs, tlvs_len) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto error; - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - return; -error: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -1674,22 +1603,26 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + return; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "ServiceDiscoveryResponse"); + "ServiceDiscoveryResponse"); if (msg == NULL) return; - /* Check if this is a known peer */ - if (!p2p_peer_known(wpa_s->global->p2p, sa)) - goto error; - os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa)); @@ -1697,10 +1630,8 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, path = peer_obj_path; dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto error; - - if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", path) || !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator", update_indic) || @@ -1708,17 +1639,13 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, (const char *) tlvs, tlvs_len) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto error; - - - dbus_connection_send(iface->con, msg, NULL); - dbus_message_unref(msg); - return; -error: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } + /** * wpas_dbus_signal_persistent_group - Send a persistent group related * event signal @@ -1744,6 +1671,9 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", wpa_s->dbus_new_path, id); @@ -1757,23 +1687,15 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = pgrp_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties( - iface, pgrp_obj_path, - WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)) - goto err; - } - - dbus_connection_send(iface->con, msg, NULL); - - dbus_message_unref(msg); - return; + &path) || + (properties && + !wpa_dbus_get_object_properties( + iface, pgrp_obj_path, + WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -1832,6 +1754,9 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "WpsFailed"); @@ -1853,7 +1778,7 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, dbus_message_unref(msg); } -#endif /*CONFIG_P2P*/ +#endif /* CONFIG_P2P */ /** @@ -2047,7 +1972,7 @@ static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc, static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { { "CreateInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_create_interface, + (WPADBusMethodHandler) wpas_dbus_handler_create_interface, { { "args", "a{sv}", ARG_IN }, { "path", "o", ARG_OUT }, @@ -2055,14 +1980,14 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { } }, { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface, + (WPADBusMethodHandler) wpas_dbus_handler_remove_interface, { { "path", "o", ARG_IN }, END_ARGS } }, { "GetInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_interface, + (WPADBusMethodHandler) wpas_dbus_handler_get_interface, { { "ifname", "s", ARG_IN }, { "path", "o", ARG_OUT }, @@ -2120,14 +2045,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { END_ARGS } }, - { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, - { - { "path", "o", ARG_OUT }, - { "field", "s", ARG_OUT }, - { "text", "s", ARG_OUT }, - END_ARGS - } - }, /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE, { @@ -2154,8 +2071,8 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); return -1; } @@ -2269,16 +2186,16 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s, net_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } /* allocate memory for handlers arguments */ arg = os_zalloc(sizeof(struct network_handler_args)); if (!arg) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create arguments for method"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for method"); goto err; } @@ -2484,15 +2401,15 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } arg = os_zalloc(sizeof(struct bss_handler_args)); if (!arg) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create arguments for handler"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for handler"); goto err; } arg->wpa_s = wpa_s; @@ -2525,27 +2442,27 @@ err: static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_scan, + (WPADBusMethodHandler) wpas_dbus_handler_scan, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_signal_poll, + (WPADBusMethodHandler) wpas_dbus_handler_signal_poll, { { "args", "a{sv}", ARG_OUT }, END_ARGS } }, { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, + (WPADBusMethodHandler) wpas_dbus_handler_disconnect, { END_ARGS } }, { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_network, + (WPADBusMethodHandler) wpas_dbus_handler_add_network, { { "args", "a{sv}", ARG_IN }, { "path", "o", ARG_OUT }, @@ -2553,39 +2470,39 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_reassociate, + (WPADBusMethodHandler) wpas_dbus_handler_reassociate, { END_ARGS } }, { "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_reattach, + (WPADBusMethodHandler) wpas_dbus_handler_reattach, { END_ARGS } }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, + (WPADBusMethodHandler) wpas_dbus_handler_remove_network, { { "path", "o", ARG_IN }, END_ARGS } }, { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks, + (WPADBusMethodHandler) wpas_dbus_handler_remove_all_networks, { END_ARGS } }, { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_select_network, + (WPADBusMethodHandler) wpas_dbus_handler_select_network, { { "path", "o", ARG_IN }, END_ARGS } }, { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_network_reply, + (WPADBusMethodHandler) wpas_dbus_handler_network_reply, { { "path", "o", ARG_IN }, { "field", "s", ARG_IN }, @@ -2595,7 +2512,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, #ifndef CONFIG_NO_CONFIG_BLOBS { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, + (WPADBusMethodHandler) wpas_dbus_handler_add_blob, { { "name", "s", ARG_IN }, { "data", "ay", ARG_IN }, @@ -2603,7 +2520,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_blob, + (WPADBusMethodHandler) wpas_dbus_handler_get_blob, { { "name", "s", ARG_IN }, { "data", "ay", ARG_OUT }, @@ -2611,7 +2528,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob, + (WPADBusMethodHandler) wpas_dbus_handler_remove_blob, { { "name", "s", ARG_IN }, END_ARGS @@ -2620,7 +2537,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { #endif /* CONFIG_NO_CONFIG_BLOBS */ { "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) - &wpas_dbus_handler_set_pkcs11_engine_and_module_path, + wpas_dbus_handler_set_pkcs11_engine_and_module_path, { { "pkcs11_engine_path", "s", ARG_IN }, { "pkcs11_module_path", "s", ARG_IN }, @@ -2629,7 +2546,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, #ifdef CONFIG_WPS { "Start", WPAS_DBUS_NEW_IFACE_WPS, - (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, + (WPADBusMethodHandler) wpas_dbus_handler_wps_start, { { "args", "a{sv}", ARG_IN }, { "output", "a{sv}", ARG_OUT }, @@ -2639,41 +2556,41 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_find, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_find, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_stop_find, { END_ARGS } }, { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen, { { "timeout", "i", ARG_IN }, END_ARGS } }, { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_extendedlisten, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_presence_request, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req, { { "peer", "o", ARG_IN }, { "config_method", "s", ARG_IN }, @@ -2681,7 +2598,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect, { { "args", "a{sv}", ARG_IN }, { "generated_pin", "s", ARG_OUT }, @@ -2689,60 +2606,60 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_group_add, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_disconnect, { END_ARGS } }, { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer, { { "peer", "o", ARG_IN }, END_ARGS } }, { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush, { END_ARGS } }, { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_add_service, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_delete_service, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service, { END_ARGS } }, { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_req, { { "args", "a{sv}", ARG_IN }, { "ref", "t", ARG_OUT }, @@ -2750,27 +2667,27 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_res, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_cancel_req, { { "args", "t", ARG_IN }, END_ARGS } }, { "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_update, { END_ARGS } }, { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_serv_disc_external, { { "arg", "i", ARG_IN }, END_ARGS @@ -2800,7 +2717,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, #endif /* CONFIG_P2P */ { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss, + (WPADBusMethodHandler) wpas_dbus_handler_flush_bss, { { "age", "u", ARG_IN }, END_ARGS @@ -2821,20 +2738,20 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, #endif /* CONFIG_AP */ { "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_eap_logoff, + (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff, { END_ARGS } }, { "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_eap_logon, + (WPADBusMethodHandler) wpas_dbus_handler_eap_logon, { END_ARGS } }, #ifdef CONFIG_AUTOSCAN { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, + (WPADBusMethodHandler) wpas_dbus_handler_autoscan, { { "arg", "s", ARG_IN }, END_ARGS @@ -3080,12 +2997,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - { - { "states", "a{ss}", ARG_OUT }, - END_ARGS - } - }, { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, @@ -3246,6 +3157,14 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "field", "s", ARG_OUT }, + { "text", "s", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; @@ -3272,8 +3191,8 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } @@ -3415,6 +3334,9 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, DBusMessageIter iter; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ @@ -3434,15 +3356,10 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, path = peer_obj_path; if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path)) - goto err; - - dbus_connection_send(iface->con, msg, NULL); - - dbus_message_unref(msg); - return; + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -3500,6 +3417,9 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) if (ctrl_iface == NULL) return 0; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(dev_addr)); @@ -3508,16 +3428,16 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) peer_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } /* allocate memory for handlers arguments */ arg = os_zalloc(sizeof(struct peer_handler_args)); if (!arg) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create arguments for method"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for method"); goto err; } @@ -3559,6 +3479,10 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) return 0; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; @@ -3580,6 +3504,9 @@ void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, { char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(dev_addr)); @@ -3685,8 +3612,8 @@ void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, group_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } @@ -3723,6 +3650,9 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return; @@ -3783,6 +3713,9 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, if (ssid->disabled != 2 && !ssid->p2p_persistent_group) return -1; /* should we return w/o complaining? */ + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; @@ -3799,8 +3732,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, pgrp_obj_path); obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to create " - "object description"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to create object description"); goto err; } @@ -3811,8 +3744,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, /* allocate memory for handlers arguments */ arg = os_zalloc(sizeof(struct network_handler_args)); if (!arg) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to create " - "arguments for method"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to create arguments for method"); goto err; } @@ -3862,6 +3795,10 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) return 0; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index 9f6c4a39..166db5dc 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -29,13 +29,13 @@ #include "dbus_common_i.h" #include "drivers/driver.h" -static const char *debug_strings[] = { +static const char * const debug_strings[] = { "excessive", "msgdump", "debug", "info", "warning", "error", NULL }; /** - * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message + * wpas_dbus_error_unknown_error - Return a new UnknownError error message * @message: Pointer to incoming dbus message this error refers to * @arg: Optional string appended to error message * Returns: a dbus error message @@ -45,20 +45,6 @@ static const char *debug_strings[] = { DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg) { - /* - * This function can be called as a result of a failure - * within internal getter calls, which will call this function - * with a NULL message parameter. However, dbus_message_new_error - * looks very unkindly (i.e, abort()) on a NULL message, so - * in this case, we should not call it. - */ - if (message == NULL) { - wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error " - "called with NULL message (arg=%s)", - arg ? arg : "N/A"); - return NULL; - } - return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR, arg); } @@ -73,9 +59,9 @@ DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, */ static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, - "wpa_supplicant knows nothing about " - "this interface."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, + "wpa_supplicant knows nothing about this interface."); } @@ -88,9 +74,9 @@ static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message) */ static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, - "There is no such a network in this " - "interface."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such a network in this interface."); } @@ -106,9 +92,9 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, { DBusMessage *reply; - reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS, - "Did not receive correct message " - "arguments."); + reply = dbus_message_new_error( + message, WPAS_DBUS_ERROR_INVALID_ARGS, + "Did not receive correct message arguments."); if (arg != NULL) dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); @@ -125,20 +111,23 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, * * Convenience function to create and return a scan error */ -DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message, - const char *error) +static DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message, + const char *error) { - DBusMessage *reply; + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, + error); +} - reply = dbus_message_new_error(message, - WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, - error); - return reply; +DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message) +{ + wpa_printf(MSG_DEBUG, "dbus: Failed to allocate memory"); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); } -static const char *dont_quote[] = { +static const char * const dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", "bssid", "scan_freq", "freq_list", NULL @@ -147,6 +136,7 @@ static const char *dont_quote[] = { static dbus_bool_t should_quote_opt(const char *key) { int i = 0; + while (dont_quote[i] != NULL) { if (os_strcmp(key, dont_quote[i]) == 0) return FALSE; @@ -233,7 +223,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "\"%s\"", entry.str_value); - if (ret < 0 || (size_t) ret != (size - 1)) + if (os_snprintf_error(size, ret)) goto error; } else { value = os_strdup(entry.str_value); @@ -247,7 +237,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "%u", entry.uint32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else if (entry.type == DBUS_TYPE_INT32) { value = os_zalloc(size); @@ -256,7 +246,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "%d", entry.int32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else goto error; @@ -306,27 +296,21 @@ dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter, if (!dbus_type_is_basic(type)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: given type is not basic", __func__); + "%s: given type is not basic", __func__); return FALSE; } if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - wpa_dbus_type_as_string(type), - &variant_iter)) - goto error; - - if (!dbus_message_iter_append_basic(&variant_iter, type, val)) - goto error; - - if (!dbus_message_iter_close_container(iter, &variant_iter)) - goto error; + wpa_dbus_type_as_string(type), + &variant_iter) || + !dbus_message_iter_append_basic(&variant_iter, type, val) || + !dbus_message_iter_close_container(iter, &variant_iter)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: error constructing reply", __func__); + return FALSE; + } return TRUE; - -error: - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: error constructing reply", __func__); - return FALSE; } @@ -389,7 +373,7 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, if (!dbus_type_is_basic(type)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: given type is not basic", __func__); + "%s: given type is not basic", __func__); return FALSE; } @@ -397,20 +381,15 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, type_str[1] = sub_type_str[0]; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - type_str, &variant_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 1", __func__); - return FALSE; - } - - if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + type_str, &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, sub_type_str, &array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 2", __func__); + "%s: failed to construct message", __func__); return FALSE; } - switch(type) { + switch (type) { case DBUS_TYPE_BYTE: case DBUS_TYPE_BOOLEAN: element_size = 1; @@ -436,7 +415,7 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, break; default: dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: unknown element type %d", __func__, type); + "%s: unknown element type %d", __func__, type); return FALSE; } @@ -450,15 +429,10 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, } } - if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 3", __func__); - return FALSE; - } - - if (!dbus_message_iter_close_container(iter, &variant_iter)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 4", __func__); + "%s: failed to construct message 3", __func__); return FALSE; } @@ -501,15 +475,11 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, inner_type_str[1] = sub_type_str[0]; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - type_str, &variant_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 1", __func__); - return FALSE; - } - if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + type_str, &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, inner_type_str, &array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 2", __func__); + "%s: failed to construct message", __func__); return FALSE; } @@ -520,15 +490,10 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, } - if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to close message 2", __func__); - return FALSE; - } - - if (!dbus_message_iter_close_container(iter, &variant_iter)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to close message 1", __func__); + "%s: failed to close message", __func__); return FALSE; } @@ -566,29 +531,29 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "Driver") && - (entry.type == DBUS_TYPE_STRING)) { + if (os_strcmp(entry.key, "Driver") == 0 && + entry.type == DBUS_TYPE_STRING) { os_free(driver); driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; - } else if (!os_strcmp(entry.key, "Ifname") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "Ifname") == 0 && + entry.type == DBUS_TYPE_STRING) { os_free(ifname); ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (ifname == NULL) goto error; - } else if (!os_strcmp(entry.key, "ConfigFile") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "ConfigFile") == 0 && + entry.type == DBUS_TYPE_STRING) { os_free(confname); confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) goto error; - } else if (!os_strcmp(entry.key, "BridgeIfname") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "BridgeIfname") == 0 && + entry.type == DBUS_TYPE_STRING) { os_free(bridge_ifname); bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); @@ -608,28 +573,30 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, * an error if we already control it. */ if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error(message, - WPAS_DBUS_ERROR_IFACE_EXISTS, - "wpa_supplicant already " - "controls this interface."); + reply = dbus_message_new_error( + message, WPAS_DBUS_ERROR_IFACE_EXISTS, + "wpa_supplicant already controls this interface."); } else { struct wpa_supplicant *wpa_s; struct wpa_interface iface; + os_memset(&iface, 0, sizeof(iface)); iface.driver = driver; iface.ifname = ifname; iface.confname = confname; iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ - if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + wpa_s = wpa_supplicant_add_iface(global, &iface); + if (wpa_s) { const char *path = wpa_s->dbus_new_path; + reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); + &path, DBUS_TYPE_INVALID); } else { reply = wpas_dbus_error_unknown_error( - message, "wpa_supplicant couldn't grab this " - "interface."); + message, + "wpa_supplicant couldn't grab this interface."); } } @@ -672,8 +639,8 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, reply = wpas_dbus_error_iface_unknown(message); else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_error_unknown_error( - message, "wpa_supplicant couldn't remove this " - "interface."); + message, + "wpa_supplicant couldn't remove this interface."); } return reply; @@ -707,13 +674,11 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, path = wpa_s->dbus_new_path; reply = dbus_message_new_method_return(message); if (reply == NULL) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); } return reply; @@ -756,8 +721,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, * Getter for "DebugTimestamp" property. */ dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_timestamp, error); @@ -812,8 +777,8 @@ dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, if (val < 0 || wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp, wpa_debug_show_keys)) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug " - "level value"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "wrong debug level value"); return FALSE; } @@ -963,8 +928,8 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, * and P2P that are determined at compile time. */ dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; size_t num_items = 0; @@ -993,8 +958,8 @@ static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var, char **type, DBusMessage **reply) { if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Type must be a string"); + wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a string", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong Type value type. String required"); return -1; @@ -1016,36 +981,36 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, int len; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " - "must be an array of arrays of bytes"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: ssids must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong SSIDs value type. Array of arrays of " - "bytes required"); + message, + "Wrong SSIDs value type. Array of arrays of bytes required"); return -1; } dbus_message_iter_recurse(var, &array_iter); if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) - { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " - "must be an array of arrays of bytes"); + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: ssids must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong SSIDs value type. Array of arrays of " - "bytes required"); + message, + "Wrong SSIDs value type. Array of arrays of bytes required"); return -1; } - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) - { + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) { if (ssids_num >= WPAS_MAX_SCAN_SSIDS) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Too many ssids specified on scan dbus " - "call"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Too many ssids specified on scan dbus call", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Too many ssids specified. Specify " - "at most four"); + message, + "Too many ssids specified. Specify at most four"); return -1; } @@ -1055,9 +1020,8 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, if (len > MAX_SSID_LEN) { wpa_printf(MSG_DEBUG, - "wpas_dbus_handler_scan[dbus]: " - "SSID too long (len=%d max_len=%d)", - len, MAX_SSID_LEN); + "%s[dbus]: SSID too long (len=%d max_len=%d)", + __func__, len, MAX_SSID_LEN); *reply = wpas_dbus_error_invalid_args( message, "Invalid SSID: too long"); return -1; @@ -1066,12 +1030,7 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, if (len != 0) { ssid = os_malloc(len); if (ssid == NULL) { - wpa_printf(MSG_DEBUG, - "wpas_dbus_handler_scan[dbus]: " - "out of memory. Cannot allocate " - "memory for SSID"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } os_memcpy(ssid, val, len); @@ -1103,28 +1062,28 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var, int len; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " - "be an array of arrays of bytes"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: ies must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong IEs value type. Array of arrays of " - "bytes required"); + message, + "Wrong IEs value type. Array of arrays of bytes required"); return -1; } dbus_message_iter_recurse(var, &array_iter); if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) - { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " - "be an array of arrays of bytes"); + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: ies must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong IEs value type. Array required"); return -1; } - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) - { + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) { dbus_message_iter_recurse(&array_iter, &sub_array_iter); dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); @@ -1135,12 +1094,8 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var, nies = os_realloc(ies, ies_len + len); if (nies == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. Cannot allocate memory for " - "IE"); os_free(ies); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } ies = nies; @@ -1166,11 +1121,12 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, int freqs_num = 0; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channels must be an array of structs"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channels must be an array of structs", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channels value type. Array of structs " - "required"); + message, + "Wrong Channels value type. Array of structs required"); return -1; } @@ -1178,11 +1134,11 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) { wpa_printf(MSG_DEBUG, - "wpas_dbus_handler_scan[dbus]: Channels must be an " - "array of structs"); + "%s[dbus]: Channels must be an array of structs", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channels value type. Array of structs " - "required"); + message, + "Wrong Channels value type. Array of structs required"); return -1; } @@ -1194,14 +1150,14 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (dbus_message_iter_get_arg_type(&sub_array_iter) != DBUS_TYPE_UINT32) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channel must by specified by struct of " - "two UINT32s %c", + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channel must by specified by struct of two UINT32s %c", + __func__, dbus_message_iter_get_arg_type( &sub_array_iter)); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channel struct. Two UINT32s " - "required"); + message, + "Wrong Channel struct. Two UINT32s required"); os_free(freqs); return -1; } @@ -1210,9 +1166,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (!dbus_message_iter_next(&sub_array_iter) || dbus_message_iter_get_arg_type(&sub_array_iter) != DBUS_TYPE_UINT32) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channel must by specified by struct of " - "two UINT32s"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channel must by specified by struct of two UINT32s", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong Channel struct. Two UINT32s required"); @@ -1232,11 +1188,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, freqs = nfreqs; } if (freqs == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. can't allocate memory for " - "freqs"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } @@ -1251,10 +1203,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, os_free(freqs); freqs = nfreqs; if (freqs == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. Can't allocate memory for freqs"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } freqs[freqs_num] = 0; @@ -1270,8 +1219,8 @@ static int wpas_dbus_get_scan_allow_roam(DBusMessage *message, DBusMessage **reply) { if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Type must be a boolean"); + wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a boolean", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong Type value type. Boolean required"); return -1; @@ -1308,7 +1257,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, dbus_message_iter_recurse(&iter, &dict_iter); while (dbus_message_iter_get_arg_type(&dict_iter) == - DBUS_TYPE_DICT_ENTRY) { + DBUS_TYPE_DICT_ENTRY) { dbus_message_iter_recurse(&dict_iter, &entry_iter); dbus_message_iter_get_basic(&entry_iter, &key); dbus_message_iter_next(&entry_iter); @@ -1337,8 +1286,8 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, &reply) < 0) goto out; } else { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Unknown argument %s", key); + wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown argument %s", + __func__, key); reply = wpas_dbus_error_invalid_args(message, key); goto out; } @@ -1347,19 +1296,20 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, } if (!type) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Scan type not specified"); + wpa_printf(MSG_DEBUG, "%s[dbus]: Scan type not specified", + __func__); reply = wpas_dbus_error_invalid_args(message, key); goto out; } - if (!os_strcmp(type, "passive")) { + if (os_strcmp(type, "passive") == 0) { if (params.num_ssids || params.extra_ies_len) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "SSIDs or IEs specified for passive scan."); + wpa_printf(MSG_DEBUG, + "%s[dbus]: SSIDs or IEs specified for passive scan.", + __func__); reply = wpas_dbus_error_invalid_args( - message, "You can specify only Channels in " - "passive scan"); + message, + "You can specify only Channels in passive scan"); goto out; } else if (params.freqs && params.freqs[0]) { if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { @@ -1370,7 +1320,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } - } else if (!os_strcmp(type, "active")) { + } else if (os_strcmp(type, "active") == 0) { if (!params.num_ssids) { /* Add wildcard ssid */ params.num_ssids++; @@ -1383,8 +1333,8 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, message, "Scan request rejected"); } } else { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Unknown scan type: %s", type); + wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s", + __func__, type); reply = wpas_dbus_error_invalid_args(message, "Wrong scan type"); goto out; @@ -1433,45 +1383,30 @@ DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, dbus_message_iter_init_append(reply, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - "a{sv}", &variant_iter)) - goto nomem; - if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) - goto nomem; - - if (!wpa_dbus_dict_append_int32(&iter_dict, "rssi", si.current_signal)) - goto nomem; - if (!wpa_dbus_dict_append_int32(&iter_dict, "linkspeed", - si.current_txrate / 1000)) - goto nomem; - if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", si.current_noise)) - goto nomem; - if (!wpa_dbus_dict_append_uint32(&iter_dict, "frequency", si.frequency)) - goto nomem; - - if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { - if (!wpa_dbus_dict_append_string(&iter_dict, "width", - channel_width_to_string(si.chanwidth))) - goto nomem; - } - - if (si.center_frq1 > 0 && si.center_frq2 > 0) { - if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1", - si.center_frq1)) - goto nomem; - if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq2", - si.center_frq2)) - goto nomem; - } - - if (si.avg_signal) { - if (!wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi", - si.avg_signal)) - goto nomem; - } - - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(&iter, &variant_iter)) + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) || + !wpa_dbus_dict_append_int32(&iter_dict, "rssi", + si.current_signal) || + !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed", + si.current_txrate / 1000) || + !wpa_dbus_dict_append_int32(&iter_dict, "noise", + si.current_noise) || + !wpa_dbus_dict_append_uint32(&iter_dict, "frequency", + si.frequency) || + (si.chanwidth != CHAN_WIDTH_UNKNOWN && + !wpa_dbus_dict_append_string( + &iter_dict, "width", + channel_width_to_string(si.chanwidth))) || + (si.center_frq1 > 0 && si.center_frq2 > 0 && + (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1", + si.center_frq1) || + !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2", + si.center_frq2))) || + (si.avg_signal && + !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi", + si.avg_signal)) || + !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(&iter, &variant_iter)) goto nomem; return reply; @@ -1479,8 +1414,7 @@ DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, nomem: if (reply) dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); - return reply; + return wpas_dbus_error_no_memory(message); } @@ -1530,12 +1464,11 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: " - "can't add new interface."); + wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", + __func__); reply = wpas_dbus_error_unknown_error( message, - "wpa_supplicant could not add " - "a network on this interface."); + "wpa_supplicant could not add a network on this interface."); goto err; } wpas_notify_network_added(wpa_s, ssid); @@ -1544,9 +1477,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, dbus_error_init(&error); if (!set_network_properties(wpa_s, ssid, &iter, &error)) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:" - "control interface couldn't set network " - "properties"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: control interface couldn't set network properties", + __func__); reply = wpas_dbus_reply_new_from_error(message, &error, DBUS_ERROR_INVALID_ARGS, "Failed to add network"); @@ -1561,15 +1494,13 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, reply = dbus_message_new_method_return(message); if (reply == NULL) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } @@ -1642,7 +1573,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; int id; struct wpa_ssid *ssid; int was_disabled; @@ -1652,7 +1583,9 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ - iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1680,25 +1613,24 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); else if (!was_disabled && wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " - "network from filters"); + wpa_printf(MSG_DEBUG, + "Stop ongoing sched_scan to remove network from filters"); wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } if (wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_ERROR, - "wpas_dbus_handler_remove_network[dbus]: " - "error occurred when removing network %d", id); + "%s[dbus]: error occurred when removing network %d", + __func__, id); reply = wpas_dbus_error_unknown_error( - message, "error removing the specified network on " - "this interface."); + message, + "error removing the specified network on is interface."); goto out; } out: os_free(iface); - os_free(net_id); return reply; } @@ -1711,9 +1643,8 @@ static void remove_network(void *arg, struct wpa_ssid *ssid) if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { wpa_printf(MSG_ERROR, - "wpas_dbus_handler_remove_all_networks[dbus]: " - "error occurred when removing network %d", - ssid->id); + "%s[dbus]: error occurred when removing network %d", + __func__, ssid->id); return; } @@ -1756,7 +1687,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; int id; struct wpa_ssid *ssid; @@ -1765,7 +1696,9 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ - iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1790,7 +1723,6 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, out: os_free(iface); - os_free(net_id); return reply; } @@ -1809,20 +1741,22 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, #ifdef IEEE8021X_EAPOL DBusMessage *reply = NULL; const char *op, *field, *value; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; int id; struct wpa_ssid *ssid; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_STRING, &field, - DBUS_TYPE_STRING, &value, - DBUS_TYPE_INVALID)) + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_STRING, &field, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID)) return wpas_dbus_error_invalid_args(message, NULL); /* Extract the network ID and ensure the network */ /* is actually a child of this interface */ - iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1852,7 +1786,6 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, out: os_free(iface); - os_free(net_id); return reply; #else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); @@ -1898,26 +1831,18 @@ DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, blob = os_zalloc(sizeof(*blob)); if (!blob) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } blob->data = os_malloc(blob_len); - if (!blob->data) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + blob->name = os_strdup(blob_name); + if (!blob->data || !blob->name) { + reply = wpas_dbus_error_no_memory(message); goto err; } os_memcpy(blob->data, blob_data, blob_len); - blob->len = blob_len; - blob->name = os_strdup(blob_name); - if (!blob->name) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto err; - } wpa_config_set_blob(wpa_s->conf, blob); wpas_notify_blob_added(wpa_s, blob->name); @@ -1962,39 +1887,21 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, } reply = dbus_message_new_method_return(message); - if (!reply) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } + if (!reply) + return wpas_dbus_error_no_memory(message); dbus_message_iter_init_append(reply, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, - &array_iter)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } - - if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, - &(blob->data), blob->len)) { + &array_iter) || + !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &(blob->data), blob->len) || + !dbus_message_iter_close_container(&iter, &array_iter)) { dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } - - if (!dbus_message_iter_close_container(&iter, &array_iter)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; + reply = wpas_dbus_error_no_memory(message); } -out: return reply; } @@ -2076,11 +1983,10 @@ DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, if (arg != NULL && os_strlen(arg) > 0) { char *tmp; + tmp = os_strdup(arg); if (tmp == NULL) { - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); } else { os_free(wpa_s->conf->autoscan); wpa_s->conf->autoscan = tmp; @@ -2342,8 +2248,7 @@ DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( pkcs11_module_path)) return dbus_message_new_error( message, DBUS_ERROR_FAILED, - "Reinit of the EAPOL state machine with the new PKCS " - "#11 engine and module path failed."); + "Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed."); wpa_dbus_mark_property_changed( wpa_s->global->dbus, wpa_s->dbus_new_path, @@ -2376,10 +2281,8 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, const char *scans[] = { "active", "passive", "ssid" }; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - "a{sv}", &variant_iter)) - goto nomem; - - if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) goto nomem; res = wpa_drv_get_capa(wpa_s, &capa); @@ -2387,6 +2290,7 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** pairwise cipher */ if (res < 0) { const char *args[] = {"ccmp", "tkip", "none"}; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Pairwise", args, ARRAY_SIZE(args))) @@ -2395,46 +2299,26 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp-256")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp-256")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "tkip")) - goto nomem; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "none")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "none")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2446,6 +2330,7 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, const char *args[] = { "ccmp", "tkip", "wep104", "wep40" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Group", args, ARRAY_SIZE(args))) @@ -2454,52 +2339,29 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp-256")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp-256")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "tkip")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wep104")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wep40")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wep104")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wep40")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2523,28 +2385,22 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "none")) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, + &iter_array) || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "none") || + !wpa_dbus_dict_string_array_add_element(&iter_array, "ieee8021x")) goto nomem; if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-eap")) + &iter_array, "wpa-eap") || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-eap"))) goto nomem; - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-ft-eap")) - goto nomem; - /* TODO: Ensure that driver actually supports sha256 encryption. */ #ifdef CONFIG_IEEE80211W if (!wpa_dbus_dict_string_array_add_element( @@ -2556,14 +2412,13 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-psk")) + &iter_array, "wpa-psk") || + ((capa.key_mgmt & + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-psk"))) goto nomem; - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-ft-psk")) - goto nomem; - /* TODO: Ensure that driver actually supports sha256 encryption. */ #ifdef CONFIG_IEEE80211W if (!wpa_dbus_dict_string_array_add_element( @@ -2572,11 +2427,10 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, #endif /* CONFIG_IEEE80211W */ } - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-none")) - goto nomem; - } + if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element(&iter_array, + "wpa-none")) + goto nomem; #ifdef CONFIG_WPS @@ -2595,6 +2449,7 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** WPA protocol */ if (res < 0) { const char *args[] = { "rsn", "wpa" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Protocol", args, ARRAY_SIZE(args))) @@ -2603,24 +2458,16 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "rsn")) - goto nomem; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "rsn")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2630,6 +2477,7 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** auth alg */ if (res < 0) { const char *args[] = { "open", "shared", "leap" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "AuthAlg", args, ARRAY_SIZE(args))) @@ -2641,25 +2489,16 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, &iter_array)) goto nomem; - if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "open")) - goto nomem; - } - - if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "shared")) - goto nomem; - } - - if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "leap")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + if (((capa.auth & WPA_DRIVER_AUTH_OPEN) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "open")) || + ((capa.auth & WPA_DRIVER_AUTH_SHARED) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "shared")) || + ((capa.auth & WPA_DRIVER_AUTH_LEAP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "leap")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2675,32 +2514,18 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "infrastructure")) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ad-hoc")) - goto nomem; - - if (res >= 0) { - if (capa.flags & (WPA_DRIVER_FLAGS_AP)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ap")) - goto nomem; - } - - if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "p2p")) - goto nomem; - } - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + !wpa_dbus_dict_string_array_add_element( + &iter_array, "infrastructure") || + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ad-hoc") || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ap")) || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "p2p")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2715,9 +2540,8 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, goto nomem; } - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -2778,7 +2602,7 @@ dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, * Getter for "scanning" property. */ dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data) + void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; @@ -2900,6 +2724,7 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t reason = wpa_s->disconnect_reason; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, &reason, error); } @@ -3154,8 +2979,8 @@ dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, const char *driver; if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: " - "wpa_s has no driver set"); + wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set", + __func__); dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set", __func__); return FALSE; @@ -3275,6 +3100,7 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, { struct wpa_supplicant *wpa_s = user_data; const char *bridge_ifname = wpa_s->bridge_ifname; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &bridge_ifname, error); } @@ -3349,14 +3175,6 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; - if (wpa_s->conf == NULL) { - wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting " - "networks list.", __func__); - dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error " - "occurred getting the networks list", __func__); - return FALSE; - } - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (!network_is_persistent_group(ssid)) num++; @@ -3373,7 +3191,8 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, continue; paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (paths[i] == NULL) { - dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); + dbus_set_error(error, DBUS_ERROR_NO_MEMORY, + "no memory"); goto out; } @@ -3411,16 +3230,6 @@ dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_engine_path; - if (wpa_s->conf == NULL) { - wpa_printf(MSG_ERROR, - "wpas_dbus_getter_pkcs11_engine_path[dbus]: An " - "error occurred getting the PKCS #11 engine path."); - dbus_set_error_const( - error, DBUS_ERROR_FAILED, - "An error occured getting the PKCS #11 engine path."); - return FALSE; - } - if (wpa_s->conf->pkcs11_engine_path == NULL) pkcs11_engine_path = ""; else @@ -3446,16 +3255,6 @@ dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; const char *pkcs11_module_path; - if (wpa_s->conf == NULL) { - wpa_printf(MSG_ERROR, - "wpas_dbus_getter_pkcs11_module_path[dbus]: An " - "error occurred getting the PKCS #11 module path."); - dbus_set_error_const( - error, DBUS_ERROR_FAILED, - "An error occured getting the PKCS #11 module path."); - return FALSE; - } - if (wpa_s->conf->pkcs11_module_path == NULL) pkcs11_module_path = ""; else @@ -3534,7 +3333,7 @@ static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, if (!res) { wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found", - func_name, args->id); + func_name, args->id); dbus_set_error(error, DBUS_ERROR_FAILED, "%s: BSS %d not found", func_name, args->id); @@ -3775,7 +3574,7 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, DBusMessageIter iter_dict, variant_iter; const char *group; const char *pairwise[5]; /* max 5 pairwise ciphers is supported */ - const char *key_mgmt[7]; /* max 7 key managements may be supported */ + const char *key_mgmt[8]; /* max 8 key managements may be supported */ int n; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, @@ -3799,6 +3598,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, key_mgmt[n++] = "wpa-ft-eap"; if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) key_mgmt[n++] = "wpa-eap-sha256"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + key_mgmt[n++] = "wpa-eap-suite-b"; if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE) key_mgmt[n++] = "wpa-none"; @@ -3872,9 +3673,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, goto nomem; } - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -3908,12 +3708,10 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, os_memset(&wpa_data, 0, sizeof(wpa_data)); ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE); - if (ie) { - if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, - "failed to parse WPA IE"); - return FALSE; - } + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to parse WPA IE"); + return FALSE; } return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); @@ -3943,12 +3741,10 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, os_memset(&wpa_data, 0, sizeof(wpa_data)); ie = wpa_bss_get_ie(res, WLAN_EID_RSN); - if (ie) { - if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, - "failed to parse RSN IE"); - return FALSE; - } + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) { + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "failed to parse RSN IE"); + return FALSE; } return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error); @@ -3980,10 +3776,8 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, return FALSE; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - "a{sv}", &variant_iter)) - goto nomem; - - if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) goto nomem; #ifdef CONFIG_WPS @@ -3993,15 +3787,14 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, type = "pbc"; else if (wps_is_selected_pin_registrar(wps_ie)) type = "pin"; + + wpabuf_free(wps_ie); } #endif /* CONFIG_WPS */ - if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type)) - goto nomem; - - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) || + !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -4223,8 +4016,7 @@ DBusMessage * wpas_dbus_handler_subscribe_preq( name = os_strdup(dbus_message_get_sender(message)); if (!name) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - "out of memory"); + return wpas_dbus_error_no_memory(message); wpa_s->preq_notify_peer = name; @@ -4304,28 +4096,22 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto fail; - if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", - (const char *) addr, - ETH_ALEN)) - goto fail; - if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", - (const char *) dst, - ETH_ALEN)) - goto fail; - if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", - (const char *) bssid, - ETH_ALEN)) - goto fail; - if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", - (const char *) ie, - ie_len)) - goto fail; - if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", - ssi_signal)) - goto fail; - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", + (const char *) addr, + ETH_ALEN)) || + (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", + (const char *) dst, + ETH_ALEN)) || + (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) || + (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", + (const char *) ie, + ie_len)) || + (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", + ssi_signal)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) goto fail; dbus_connection_send(priv->con, msg, NULL); diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index f6a83cdf..6113db50 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -55,8 +55,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, void *user_data); dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, DBusError *error, @@ -319,6 +319,7 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg); +DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message); DBusMessage * wpas_dbus_handler_subscribe_preq( DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 7867f0c8..9c880a23 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -35,9 +35,9 @@ * @addr - out param must be of ETH_ALEN size * Returns 0 if valid (including MAC), -1 otherwise */ -static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN]) +static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN]) { - char *p; + const char *p; if (!peer_path) return -1; @@ -57,12 +57,12 @@ static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN]) * * Convenience function to create and return an invalid persistent group error. */ -static DBusMessage * wpas_dbus_error_persistent_group_unknown( - DBusMessage *message) +static DBusMessage * +wpas_dbus_error_persistent_group_unknown(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, - "There is no such persistent group in " - "this P2P device."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such persistent group in this P2P device."); } @@ -74,7 +74,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, DBusMessageIter iter; DBusMessageIter iter_dict; unsigned int timeout = 0; - enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL; + enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; int num_req_dev_types = 0; unsigned int i; u8 *req_dev_types = NULL; @@ -89,12 +89,12 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "Timeout") && - (entry.type == DBUS_TYPE_INT32)) { + if (os_strcmp(entry.key, "Timeout") == 0 && + entry.type == DBUS_TYPE_INT32) { timeout = entry.uint32_value; } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != WPAS_DBUS_TYPE_BINARRAY)) + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY) goto error_clear; os_free(req_dev_types); @@ -105,20 +105,20 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, for (i = 0; i < entry.array_len; i++) { if (wpabuf_len(entry.binarray_value[i]) != - WPS_DEV_TYPE_LEN) + WPS_DEV_TYPE_LEN) goto error_clear; os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN, wpabuf_head(entry.binarray_value[i]), WPS_DEV_TYPE_LEN); } num_req_dev_types = entry.array_len; - } else if (!os_strcmp(entry.key, "DiscoveryType") && - (entry.type == DBUS_TYPE_STRING)) { - if (!os_strcmp(entry.str_value, "start_with_full")) + } else if (os_strcmp(entry.key, "DiscoveryType") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "start_with_full") == 0) type = P2P_FIND_START_WITH_FULL; - else if (!os_strcmp(entry.str_value, "social")) + else if (os_strcmp(entry.str_value, "social") == 0) type = P2P_FIND_ONLY_SOCIAL; - else if (!os_strcmp(entry.str_value, "progressive")) + else if (os_strcmp(entry.str_value, "progressive") == 0) type = P2P_FIND_PROGRESSIVE; else goto error_clear; @@ -127,6 +127,9 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, NULL, 0); os_free(req_dev_types); @@ -144,6 +147,9 @@ error: DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + wpas_p2p_stop_find(wpa_s); return NULL; } @@ -162,6 +168,9 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_reject(wpa_s, peer_addr) < 0) return wpas_dbus_error_unknown_error(message, "Failed to call wpas_p2p_reject method."); @@ -177,12 +186,16 @@ DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout, DBUS_TYPE_INVALID)) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; - if (wpas_p2p_listen(wpa_s, (unsigned int)timeout)) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) { + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_UNKNOWN_ERROR, + "Could not start P2P listen"); + } return NULL; } @@ -206,17 +219,20 @@ DBusMessage * wpas_dbus_handler_p2p_extendedlisten( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "period") && - (entry.type == DBUS_TYPE_INT32)) + if (os_strcmp(entry.key, "period") == 0 && + entry.type == DBUS_TYPE_INT32) period = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval") && - (entry.type == DBUS_TYPE_INT32)) + else if (os_strcmp(entry.key, "interval") == 0 && + entry.type == DBUS_TYPE_INT32) interval = entry.uint32_value; else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_ext_listen(wpa_s, period, interval)) return wpas_dbus_error_unknown_error( message, "failed to initiate a p2p_ext_listen."); @@ -248,16 +264,16 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "duration1") && - (entry.type == DBUS_TYPE_INT32)) + if (os_strcmp(entry.key, "duration1") == 0 && + entry.type == DBUS_TYPE_INT32) dur1 = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval1") && + else if (os_strcmp(entry.key, "interval1") == 0 && entry.type == DBUS_TYPE_INT32) int1 = entry.uint32_value; - else if (!os_strcmp(entry.key, "duration2") && + else if (os_strcmp(entry.key, "duration2") == 0 && entry.type == DBUS_TYPE_INT32) dur2 = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval2") && + else if (os_strcmp(entry.key, "interval2") == 0 && entry.type == DBUS_TYPE_INT32) int2 = entry.uint32_value; else @@ -265,6 +281,10 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( wpa_dbus_dict_entry_clear(&entry); } + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) return wpas_dbus_error_unknown_error(message, "Failed to invoke presence request."); @@ -289,7 +309,6 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, int persistent_group = 0; int freq = 0; char *iface = NULL; - char *net_id_str = NULL; unsigned int group_id = 0; struct wpa_ssid *ssid; @@ -302,15 +321,16 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto inv_args; - if (!os_strcmp(entry.key, "persistent") && - (entry.type == DBUS_TYPE_BOOLEAN)) { - persistent_group = (entry.bool_value == TRUE) ? 1 : 0; - } else if (!os_strcmp(entry.key, "frequency") && - (entry.type == DBUS_TYPE_INT32)) { + if (os_strcmp(entry.key, "persistent") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + persistent_group = entry.bool_value; + } else if (os_strcmp(entry.key, "frequency") == 0 && + entry.type == DBUS_TYPE_INT32) { freq = entry.int32_value; if (freq <= 0) goto inv_args_clear; - } else if (!os_strcmp(entry.key, "persistent_group_object") && + } else if (os_strcmp(entry.key, "persistent_group_object") == + 0 && entry.type == DBUS_TYPE_OBJECT_PATH) pg_object_path = os_strdup(entry.str_value); else @@ -319,15 +339,21 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (pg_object_path != NULL) { + char *net_id_str; + /* * A persistent group Object Path is defined meaning we want * to re-invoke a persistent group. */ - iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1, - &net_id_str, NULL); - if (iface == NULL || + iface = wpas_dbus_new_decompose_object_path( + pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &net_id_str); + if (iface == NULL || net_id_str == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, @@ -359,7 +385,6 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, out: os_free(pg_object_path); - os_free(net_id_str); os_free(iface); return reply; inv_args_clear: @@ -394,8 +419,7 @@ static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, "P2P is not available for this interface"); } dbus_set_error_const(error, DBUS_ERROR_FAILED, - "P2P is not available for this " - "interface"); + "P2P is not available for this interface"); return FALSE; } return TRUE; @@ -410,6 +434,9 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; p2p_flush(wpa_s->global->p2p); @@ -450,42 +477,42 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto inv_args; - if (!os_strcmp(entry.key, "peer") && - (entry.type == DBUS_TYPE_OBJECT_PATH)) { + if (os_strcmp(entry.key, "peer") == 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "persistent") && - (entry.type == DBUS_TYPE_BOOLEAN)) { - persistent_group = (entry.bool_value == TRUE) ? 1 : 0; - } else if (!os_strcmp(entry.key, "join") && - (entry.type == DBUS_TYPE_BOOLEAN)) { - join = (entry.bool_value == TRUE) ? 1 : 0; - } else if (!os_strcmp(entry.key, "authorize_only") && - (entry.type == DBUS_TYPE_BOOLEAN)) { - authorize_only = (entry.bool_value == TRUE) ? 1 : 0; - } else if (!os_strcmp(entry.key, "frequency") && - (entry.type == DBUS_TYPE_INT32)) { + } else if (os_strcmp(entry.key, "persistent") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + persistent_group = entry.bool_value; + } else if (os_strcmp(entry.key, "join") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + join = entry.bool_value; + } else if (os_strcmp(entry.key, "authorize_only") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + authorize_only = entry.bool_value; + } else if (os_strcmp(entry.key, "frequency") == 0 && + entry.type == DBUS_TYPE_INT32) { freq = entry.int32_value; if (freq <= 0) goto inv_args_clear; - } else if (!os_strcmp(entry.key, "go_intent") && - (entry.type == DBUS_TYPE_INT32)) { + } else if (os_strcmp(entry.key, "go_intent") == 0 && + entry.type == DBUS_TYPE_INT32) { go_intent = entry.int32_value; if ((go_intent < 0) || (go_intent > 15)) goto inv_args_clear; - } else if (!os_strcmp(entry.key, "wps_method") && - (entry.type == DBUS_TYPE_STRING)) { - if (!os_strcmp(entry.str_value, "pbc")) + } else if (os_strcmp(entry.key, "wps_method") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "pbc") == 0) wps_method = WPS_PBC; - else if (!os_strcmp(entry.str_value, "pin")) + else if (os_strcmp(entry.str_value, "pin") == 0) wps_method = WPS_PIN_DISPLAY; - else if (!os_strcmp(entry.str_value, "display")) + else if (os_strcmp(entry.str_value, "display") == 0) wps_method = WPS_PIN_DISPLAY; - else if (!os_strcmp(entry.str_value, "keypad")) + else if (os_strcmp(entry.str_value, "keypad") == 0) wps_method = WPS_PIN_KEYPAD; else goto inv_args_clear; - } else if (!os_strcmp(entry.key, "pin") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "pin") == 0 && + entry.type == DBUS_TYPE_STRING) { pin = os_strdup(entry.str_value); } else goto inv_args_clear; @@ -493,17 +520,20 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (!peer_object_path || (wps_method == WPS_NOT_READY) || - (parse_peer_object_path(peer_object_path, addr) < 0) || + if (wps_method == WPS_NOT_READY || + parse_peer_object_path(peer_object_path, addr) < 0 || !p2p_peer_known(wpa_s->global->p2p, addr)) goto inv_args; /* * Validate the wps_method specified and the pin value. */ - if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD)) + if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD) goto inv_args; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, go_intent, freq, -1, 0, 0, 0); @@ -511,6 +541,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, if (new_pin >= 0) { char npin[9]; char *generated_pin; + os_snprintf(npin, sizeof(npin), "%08d", new_pin); generated_pin = npin; reply = dbus_message_new_method_return(message); @@ -519,8 +550,8 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, } else { switch (new_pin) { case -2: - err_msg = "connect failed due to channel " - "unavailability."; + err_msg = + "connect failed due to channel unavailability."; iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE; break; @@ -566,7 +597,6 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, char *peer_object_path = NULL; char *pg_object_path = NULL; char *iface = NULL; - char *net_id_str = NULL; u8 peer_addr[ETH_ALEN]; unsigned int group_id = 0; int persistent = 0; @@ -584,12 +614,13 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto err; - if (!os_strcmp(entry.key, "peer") && - (entry.type == DBUS_TYPE_OBJECT_PATH)) { + if (os_strcmp(entry.key, "peer") == 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); - } else if (!os_strcmp(entry.key, "persistent_group_object") && - (entry.type == DBUS_TYPE_OBJECT_PATH)) { + } else if (os_strcmp(entry.key, "persistent_group_object") == + 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { pg_object_path = os_strdup(entry.str_value); persistent = 1; wpa_dbus_dict_entry_clear(&entry); @@ -599,21 +630,25 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, } } - if (!peer_object_path || - (parse_peer_object_path(peer_object_path, peer_addr) < 0) || - !p2p_peer_known(wpa_s->global->p2p, peer_addr)) { + if (parse_peer_object_path(peer_object_path, peer_addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, peer_addr)) goto err; - } + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; if (persistent) { + char *net_id_str; /* * A group ID is defined meaning we want to re-invoke a * persistent group */ - iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1, - &net_id_str, NULL); - if (iface == NULL || + iface = wpas_dbus_new_decompose_object_path( + pg_object_path, + WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &net_id_str); + if (iface == NULL || net_id_str == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); @@ -652,6 +687,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, } out: + os_free(iface); os_free(pg_object_path); os_free(peer_object_path); return reply; @@ -690,6 +726,9 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, WPAS_P2P_PD_FOR_GO_NEG) < 0) return wpas_dbus_error_unknown_error(message, @@ -719,6 +758,9 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter) || !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) @@ -732,8 +774,8 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, /* Primary device type */ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType", - (char *)wpa_s->conf->device_type, - WPS_DEV_TYPE_LEN)) + (char *) wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN)) goto err_no_mem; /* Secondary device types */ @@ -768,75 +810,37 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, wpa_s->conf->wps_vendor_ext[i]; } - if (num_vendor_extensions && - !wpa_dbus_dict_append_wpabuf_array(&dict_iter, - "VendorExtension", - vendor_ext, - num_vendor_extensions)) - goto err_no_mem; - - /* GO Intent */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent", - wpa_s->conf->p2p_go_intent)) - goto err_no_mem; - - /* Persistent Reconnect */ - if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect", - wpa_s->conf->persistent_reconnect)) - goto err_no_mem; - - /* Listen Reg Class */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass", - wpa_s->conf->p2p_listen_reg_class)) - goto err_no_mem; - - /* Listen Channel */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel", - wpa_s->conf->p2p_listen_channel)) - goto err_no_mem; - - /* Oper Reg Class */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass", - wpa_s->conf->p2p_oper_reg_class)) - goto err_no_mem; - - /* Oper Channel */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel", - wpa_s->conf->p2p_oper_channel)) - goto err_no_mem; - - /* SSID Postfix */ - if (wpa_s->conf->p2p_ssid_postfix && - !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix", - wpa_s->conf->p2p_ssid_postfix)) - goto err_no_mem; - - /* Intra Bss */ - if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss", - wpa_s->conf->p2p_intra_bss)) - goto err_no_mem; - - /* Group Idle */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle", - wpa_s->conf->p2p_group_idle)) - goto err_no_mem; - - /* Dissasociation low ack */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack", - wpa_s->conf->disassoc_low_ack)) - goto err_no_mem; - - /* No Group Iface */ - if (!wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface", - wpa_s->conf->p2p_no_group_iface)) - goto err_no_mem; - - /* P2P Search Delay */ - if (!wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay", - wpa_s->conf->p2p_search_delay)) - goto err_no_mem; - - if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || + if ((num_vendor_extensions && + !wpa_dbus_dict_append_wpabuf_array(&dict_iter, + "VendorExtension", + vendor_ext, + num_vendor_extensions)) || + !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent", + wpa_s->conf->p2p_go_intent) || + !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect", + wpa_s->conf->persistent_reconnect) || + !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass", + wpa_s->conf->p2p_listen_reg_class) || + !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel", + wpa_s->conf->p2p_listen_channel) || + !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass", + wpa_s->conf->p2p_oper_reg_class) || + !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel", + wpa_s->conf->p2p_oper_channel) || + (wpa_s->conf->p2p_ssid_postfix && + !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix", + wpa_s->conf->p2p_ssid_postfix)) || + !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss", + wpa_s->conf->p2p_intra_bss) || + !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle", + wpa_s->conf->p2p_group_idle) || + !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack", + wpa_s->conf->disassoc_low_ack) || + !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface", + wpa_s->conf->p2p_no_group_iface) || + !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay", + wpa_s->conf->p2p_search_delay) || + !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || !dbus_message_iter_close_container(iter, &variant_iter)) goto err_no_mem; @@ -860,6 +864,9 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + dbus_message_iter_recurse(iter, &variant_iter); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; @@ -915,8 +922,8 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, wpa_s->conf->changed_parameters |= CFG_CHANGED_SEC_DEVICE_TYPE; } else if (os_strcmp(entry.key, "VendorExtension") == 0) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) || + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY || (entry.array_len > P2P_MAX_WPS_VENDOR_EXT)) goto error; @@ -932,30 +939,30 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, } else wpa_s->conf->wps_vendor_ext[i] = NULL; } - } else if ((os_strcmp(entry.key, "GOIntent") == 0) && - (entry.type == DBUS_TYPE_UINT32) && + } else if (os_strcmp(entry.key, "GOIntent") == 0 && + entry.type == DBUS_TYPE_UINT32 && (entry.uint32_value <= 15)) wpa_s->conf->p2p_go_intent = entry.uint32_value; - else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) && - (entry.type == DBUS_TYPE_BOOLEAN)) + else if (os_strcmp(entry.key, "PersistentReconnect") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) wpa_s->conf->persistent_reconnect = entry.bool_value; - else if ((os_strcmp(entry.key, "ListenRegClass") == 0) && - (entry.type == DBUS_TYPE_UINT32)) { + else if (os_strcmp(entry.key, "ListenRegClass") == 0 && + entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_listen_reg_class = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_LISTEN_CHANNEL; - } else if ((os_strcmp(entry.key, "ListenChannel") == 0) && - (entry.type == DBUS_TYPE_UINT32)) { + } else if (os_strcmp(entry.key, "ListenChannel") == 0 && + entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_listen_channel = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_LISTEN_CHANNEL; - } else if ((os_strcmp(entry.key, "OperRegClass") == 0) && - (entry.type == DBUS_TYPE_UINT32)) { + } else if (os_strcmp(entry.key, "OperRegClass") == 0 && + entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_oper_reg_class = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_OPER_CHANNEL; - } else if ((os_strcmp(entry.key, "OperChannel") == 0) && - (entry.type == DBUS_TYPE_UINT32)) { + } else if (os_strcmp(entry.key, "OperChannel") == 0 && + entry.type == DBUS_TYPE_UINT32) { wpa_s->conf->p2p_oper_channel = entry.uint32_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_OPER_CHANNEL; @@ -974,13 +981,13 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_SSID_POSTFIX; - } else if ((os_strcmp(entry.key, "IntraBss") == 0) && - (entry.type == DBUS_TYPE_BOOLEAN)) { + } else if (os_strcmp(entry.key, "IntraBss") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { wpa_s->conf->p2p_intra_bss = entry.bool_value; wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_INTRA_BSS; - } else if ((os_strcmp(entry.key, "GroupIdle") == 0) && - (entry.type == DBUS_TYPE_UINT32)) + } else if (os_strcmp(entry.key, "GroupIdle") == 0 && + entry.type == DBUS_TYPE_UINT32) wpa_s->conf->p2p_group_idle = entry.uint32_value; else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 && entry.type == DBUS_TYPE_UINT32) @@ -1260,8 +1267,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1285,8 +1292,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1310,8 +1317,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1369,8 +1376,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, peer_args->p2p_device_addr, 0); if (info == NULL) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "failed to find peer"); + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } @@ -1378,18 +1384,13 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, - &variant_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 1", __func__); - return FALSE; - } - - if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 2", __func__); + "%s: failed to construct message 1", __func__); return FALSE; } @@ -1404,29 +1405,14 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( if (!dbus_message_iter_open_container( &array_iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, - &inner_array_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct " - "message 3 (%d)", - __func__, i); - return FALSE; - } - - if (!dbus_message_iter_append_fixed_array( + &inner_array_iter) || + !dbus_message_iter_append_fixed_array( &inner_array_iter, DBUS_TYPE_BYTE, - &sec_dev_type_list, WPS_DEV_TYPE_LEN)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct " - "message 4 (%d)", - __func__, i); - return FALSE; - } - - if (!dbus_message_iter_close_container( + &sec_dev_type_list, WPS_DEV_TYPE_LEN) || + !dbus_message_iter_close_container( &array_iter, &inner_array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct " - "message 5 (%d)", + "%s: failed to construct message 2 (%d)", __func__, i); return FALSE; } @@ -1435,15 +1421,10 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( } } - if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 6", __func__); - return FALSE; - } - - if (!dbus_message_iter_close_container(iter, &variant_iter)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 7", __func__); + "%s: failed to construct message 3", __func__); return FALSE; } @@ -1583,7 +1564,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; struct peer_group_data data; - struct wpa_supplicant *wpa_s_go; + struct wpa_supplicant *wpa_s, *wpa_s_go; dbus_bool_t success = FALSE; info = p2p_get_peer_found(peer_args->wpa_s->global->p2p, @@ -1595,8 +1576,12 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, } os_memset(&data, 0, sizeof(data)); - wpa_s_go = wpas_get_p2p_client_iface(peer_args->wpa_s, - info->p2p_device_addr); + + wpa_s = peer_args->wpa_s; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + + wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr); if (wpa_s_go) { data.paths = os_calloc(1, sizeof(char *)); if (data.paths == NULL) @@ -1651,15 +1636,6 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; - if (wpa_s->conf == NULL) { - wpa_printf(MSG_ERROR, "dbus: %s: " - "An error occurred getting persistent groups list", - __func__); - dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error " - "occurred getting persistent groups list"); - return FALSE; - } - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) if (network_is_persistent_group(ssid)) num++; @@ -1772,12 +1748,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - wpa_printf(MSG_ERROR, "dbus: %s: " - "Cannot add new persistent group", __func__); + wpa_printf(MSG_ERROR, + "dbus: %s: Cannot add new persistent group", + __func__); reply = wpas_dbus_error_unknown_error( message, - "wpa_supplicant could not add " - "a persistent group on this interface."); + "wpa_supplicant could not add a persistent group on this interface."); goto err; } @@ -1790,13 +1766,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( dbus_error_init(&error); if (!set_network_properties(wpa_s, ssid, &iter, &error)) { - wpa_printf(MSG_DEBUG, "dbus: %s: " - "Control interface could not set persistent group " - "properties", __func__); - reply = wpas_dbus_reply_new_from_error(message, &error, - DBUS_ERROR_INVALID_ARGS, - "Failed to set network " - "properties"); + wpa_printf(MSG_DEBUG, + "dbus: %s: Control interface could not set persistent group properties", + __func__); + reply = wpas_dbus_reply_new_from_error( + message, &error, DBUS_ERROR_INVALID_ARGS, + "Failed to set network properties"); dbus_error_free(&error); goto err; } @@ -1808,15 +1783,13 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( reply = dbus_message_new_method_return(message); if (reply == NULL) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } @@ -1846,7 +1819,7 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *persistent_group_id = NULL; + char *iface = NULL, *persistent_group_id; int id; struct wpa_ssid *ssid; @@ -1857,10 +1830,11 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( * Extract the network ID and ensure the network is actually a child of * this interface. */ - iface = wpas_dbus_new_decompose_object_path(op, 1, - &persistent_group_id, - NULL); - if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { + iface = wpas_dbus_new_decompose_object_path( + op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &persistent_group_id); + if (iface == NULL || persistent_group_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1880,19 +1854,17 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( wpas_notify_persistent_group_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, id) < 0) { - wpa_printf(MSG_ERROR, "dbus: %s: " - "error occurred when removing persistent group %d", + wpa_printf(MSG_ERROR, + "dbus: %s: error occurred when removing persistent group %d", __func__, id); reply = wpas_dbus_error_unknown_error( message, - "error removing the specified persistent group on " - "this interface."); + "error removing the specified persistent group on this interface."); goto out; } out: os_free(iface); - os_free(persistent_group_id); return reply; } @@ -1903,8 +1875,8 @@ static void remove_persistent_group(struct wpa_supplicant *wpa_s, wpas_notify_persistent_group_removed(wpa_s, ssid); if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) { - wpa_printf(MSG_ERROR, "dbus: %s: " - "error occurred when removing persistent group %d", + wpa_printf(MSG_ERROR, + "dbus: %s: error occurred when removing persistent group %d", __func__, ssid->id); return; } @@ -2012,6 +1984,7 @@ dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; + if (wpa_s->current_ssid == NULL) return FALSE; return wpas_dbus_simple_array_property_getter( @@ -2072,15 +2045,14 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - u8 role = wpas_get_p2p_role(wpa_s); - char *p_pass = NULL; + char *p_pass; + struct wpa_ssid *ssid = wpa_s->current_ssid; - /* Verify correct role for this property */ - if (role == WPAS_P2P_ROLE_GO) { - if (wpa_s->current_ssid == NULL) - return FALSE; - p_pass = wpa_s->current_ssid->passphrase; - } else + if (ssid == NULL) + return FALSE; + + p_pass = ssid->passphrase; + if (!p_pass) p_pass = ""; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, @@ -2093,20 +2065,20 @@ dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - u8 role = wpas_get_p2p_role(wpa_s); u8 *p_psk = NULL; u8 psk_len = 0; + struct wpa_ssid *ssid = wpa_s->current_ssid; - /* Verify correct role for this property */ - if (role == WPAS_P2P_ROLE_CLIENT) { - if (wpa_s->current_ssid == NULL) - return FALSE; - p_psk = wpa_s->current_ssid->psk; - psk_len = 32; + if (ssid == NULL) + return FALSE; + + if (ssid->psk_set) { + p_psk = ssid->psk; + psk_len = sizeof(ssid->psk); } return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, - &p_psk, psk_len, error); + p_psk, psk_len, error); } @@ -2150,7 +2122,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - DBusMessageIter variant_iter, iter_dict; + DBusMessageIter variant_iter, iter_dict, array_iter, sub; struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; unsigned int i; struct hostapd_data *hapd = NULL; @@ -2162,6 +2134,82 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, return FALSE; dbus_message_iter_recurse(iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) + return FALSE; + + /* + * This is supposed to be array of bytearrays (aay), but the earlier + * implementation used a dict with "WPSVendorExtensions" as the key in + * this setter function which does not match the format used by the + * getter function. For backwards compatibility, allow both formats to + * be used in the setter. + */ + if (dbus_message_iter_get_element_type(&variant_iter) == + DBUS_TYPE_ARRAY) { + /* This is the proper format matching the getter */ + struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS]; + + dbus_message_iter_recurse(&variant_iter, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != + DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&array_iter) != + DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "dbus: Not an array of array of bytes"); + return FALSE; + } + + i = 0; + os_memset(vals, 0, sizeof(vals)); + + while (dbus_message_iter_get_arg_type(&array_iter) == + DBUS_TYPE_ARRAY) { + char *val; + int len; + + if (i == MAX_WPS_VENDOR_EXTENSIONS) { + wpa_printf(MSG_DEBUG, + "dbus: Too many WPSVendorExtensions values"); + i = MAX_WPS_VENDOR_EXTENSIONS + 1; + break; + } + + dbus_message_iter_recurse(&array_iter, &sub); + dbus_message_iter_get_fixed_array(&sub, &val, &len); + wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]", + val, len); + vals[i] = wpabuf_alloc_copy(val, len); + if (vals[i] == NULL) { + i = MAX_WPS_VENDOR_EXTENSIONS + 1; + break; + } + i++; + dbus_message_iter_next(&array_iter); + } + + if (i > MAX_WPS_VENDOR_EXTENSIONS) { + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(vals[i]); + return FALSE; + } + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(hapd->conf->wps_vendor_ext[i]); + hapd->conf->wps_vendor_ext[i] = vals[i]; + } + + hostapd_update_wps(hapd); + + return TRUE; + } + + if (dbus_message_iter_get_element_type(&variant_iter) != + DBUS_TYPE_DICT_ENTRY) + return FALSE; + + wpa_printf(MSG_DEBUG, + "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter"); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; @@ -2179,6 +2227,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, goto error; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(hapd->conf->wps_vendor_ext[i]); if (i < entry.array_len) { hapd->conf->wps_vendor_ext[i] = entry.binarray_value[i]; @@ -2227,30 +2276,31 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "service_type") && - (entry.type == DBUS_TYPE_STRING)) { - if (!os_strcmp(entry.str_value, "upnp")) + if (os_strcmp(entry.key, "service_type") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; - else if (!os_strcmp(entry.str_value, "bonjour")) + else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; - } else if (!os_strcmp(entry.key, "version") && - entry.type == DBUS_TYPE_INT32) { + } else if (os_strcmp(entry.key, "version") == 0 && + entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; - } else if (!os_strcmp(entry.key, "service") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); service = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "query")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) + } else if (os_strcmp(entry.key, "query") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) goto error_clear; query = wpabuf_alloc_copy( entry.bytearray_value, entry.array_len); - } else if (!os_strcmp(entry.key, "response")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) + } else if (os_strcmp(entry.key, "response") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) goto error_clear; resp = wpabuf_alloc_copy(entry.bytearray_value, entry.array_len); @@ -2265,8 +2315,6 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0) goto error; - os_free(service); - service = NULL; } else if (bonjour == 1) { if (query == NULL || resp == NULL) goto error; @@ -2278,6 +2326,7 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, } else goto error; + os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); @@ -2312,11 +2361,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "service_type") && - (entry.type == DBUS_TYPE_STRING)) { - if (!os_strcmp(entry.str_value, "upnp")) + if (os_strcmp(entry.key, "service_type") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; - else if (!os_strcmp(entry.str_value, "bonjour")) + else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; @@ -2327,13 +2376,14 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "version") && + if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) version = entry.uint32_value; - else if (!os_strcmp(entry.key, "service") && - entry.type == DBUS_TYPE_STRING) + else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); service = os_strdup(entry.str_value); - else + } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); @@ -2343,7 +2393,6 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( goto error; ret = wpas_p2p_service_del_upnp(wpa_s, version, service); - os_free(service); if (ret != 0) goto error; } else if (bonjour == 1) { @@ -2351,10 +2400,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "query")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) + if (os_strcmp(entry.key, "query") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) goto error_clear; + wpabuf_free(query); query = wpabuf_alloc_copy( entry.bytearray_value, entry.array_len); @@ -2370,14 +2420,17 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( ret = wpas_p2p_service_del_bonjour(wpa_s, query); if (ret != 0) goto error; - wpabuf_free(query); } else goto error; + wpabuf_free(query); + os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: + wpabuf_free(query); + os_free(service); return wpas_dbus_error_invalid_args(message, NULL); } @@ -2413,22 +2466,22 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_req( while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "peer_object") && + if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "service_type") && + } else if (os_strcmp(entry.key, "service_type") == 0 && entry.type == DBUS_TYPE_STRING) { - if (!os_strcmp(entry.str_value, "upnp")) + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; else goto error_clear; - } else if (!os_strcmp(entry.key, "version") && + } else if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; - } else if (!os_strcmp(entry.key, "service") && + } else if (os_strcmp(entry.key, "service") == 0 && entry.type == DBUS_TYPE_STRING) { service = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "tlv")) { + } else if (os_strcmp(entry.key, "tlv") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; @@ -2506,16 +2559,17 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "peer_object") && + if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "frequency") && + } else if (os_strcmp(entry.key, "frequency") == 0 && entry.type == DBUS_TYPE_INT32) { freq = entry.uint32_value; - } else if (!os_strcmp(entry.key, "dialog_token") && - entry.type == DBUS_TYPE_UINT32) { + } else if (os_strcmp(entry.key, "dialog_token") == 0 && + (entry.type == DBUS_TYPE_UINT32 || + entry.type == DBUS_TYPE_INT32)) { dlg_tok = entry.uint32_value; - } else if (!os_strcmp(entry.key, "tlvs")) { + } else if (os_strcmp(entry.key, "tlvs") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; @@ -2526,12 +2580,9 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res( wpa_dbus_dict_entry_clear(&entry); } - if (!peer_object_path || - (parse_peer_object_path(peer_object_path, addr) < 0) || - !p2p_peer_known(wpa_s->global->p2p, addr)) - goto error; - - if (tlv == NULL) + if (parse_peer_object_path(peer_object_path, addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, addr) || + tlv == NULL) goto error; wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index 6e67c89e..fdaccbaf 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -109,34 +109,34 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, */ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( DBusMessageIter *iter, DBusError *error, void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( DBusMessageIter *iter, DBusError *error, void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, DBusError *error, diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index 8ecf7dba..a94a0e51 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -41,8 +41,8 @@ static int wpas_dbus_handler_wps_role(DBusMessage *message, dbus_message_iter_recurse(entry_iter, &variant_iter); if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, " - "string required"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Role type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Role must be a string"); return -1; @@ -70,10 +70,9 @@ static int wpas_dbus_handler_wps_type(DBusMessage *message, char *val; dbus_message_iter_recurse(entry_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != - DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, " - "string required"); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Type type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Type must be a string"); return -1; @@ -105,8 +104,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&variant_iter) != DBUS_TYPE_BYTE) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, " - "byte array required"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Bssid type, byte array required"); *reply = wpas_dbus_error_invalid_args( message, "Bssid must be a byte array"); return -1; @@ -114,8 +113,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, dbus_message_iter_recurse(&variant_iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, ¶ms->bssid, &len); if (len != ETH_ALEN) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length " - "%d", len); + wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d", + len); *reply = wpas_dbus_error_invalid_args(message, "Bssid is wrong length"); return -1; @@ -132,10 +131,9 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message, DBusMessageIter variant_iter; dbus_message_iter_recurse(entry_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != - DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, " - "string required"); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Pin type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Pin must be a string"); return -1; @@ -158,8 +156,8 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message, if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(&variant_iter) != DBUS_TYPE_BYTE) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong " - "P2PDeviceAddress type, byte array required"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong P2PDeviceAddress type, byte array required"); *reply = wpas_dbus_error_invalid_args( message, "P2PDeviceAddress must be a byte array"); return -1; @@ -168,11 +166,11 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message, dbus_message_iter_get_fixed_array(&array_iter, ¶ms->p2p_dev_addr, &len); if (len != ETH_ALEN) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong " - "P2PDeviceAddress length %d", len); - *reply = wpas_dbus_error_invalid_args(message, - "P2PDeviceAddress " - "has wrong length"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong P2PDeviceAddress length %d", + len); + *reply = wpas_dbus_error_invalid_args( + message, "P2PDeviceAddress has wrong length"); return -1; } return 0; @@ -249,54 +247,54 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, dbus_message_iter_next(&dict_iter); } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && params.type == 1) { + if (params.pin == NULL) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Pin required for registrar role"); + return wpas_dbus_error_invalid_args( + message, "Pin required for registrar role."); + } + ret = wpa_supplicant_ap_wps_pin(wpa_s, + params.bssid, + params.pin, + npin, sizeof(npin), 0); + } else if (wpa_s->ap_iface) { + ret = wpa_supplicant_ap_wps_pbc(wpa_s, + params.bssid, + params.p2p_dev_addr); + } else +#endif /* CONFIG_AP */ if (params.role == 0) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified"); return wpas_dbus_error_invalid_args(message, "Role not specified"); - } else if (params.role == 1 && params.type == 0) { + } else if (params.role == 2) { + if (params.pin == NULL) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Pin required for registrar role"); + return wpas_dbus_error_invalid_args( + message, "Pin required for registrar role."); + } + ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, + NULL); + } else if (params.type == 0) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified"); return wpas_dbus_error_invalid_args(message, "Type not specified"); - } else if (params.role == 2 && params.pin == NULL) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for " - "registrar role"); - return wpas_dbus_error_invalid_args( - message, "Pin required for registrar role."); - } - - if (params.role == 2) - ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, - NULL); - else if (params.type == 1) { -#ifdef CONFIG_AP - if (wpa_s->ap_iface) - ret = wpa_supplicant_ap_wps_pin(wpa_s, - params.bssid, - params.pin, - npin, sizeof(npin), 0); - else -#endif /* CONFIG_AP */ - { - ret = wpas_wps_start_pin(wpa_s, params.bssid, - params.pin, 0, - DEV_PW_DEFAULT); - if (ret > 0) - os_snprintf(npin, sizeof(npin), "%08d", ret); - } + } else if (params.type == 1) { + ret = wpas_wps_start_pin(wpa_s, params.bssid, + params.pin, 0, + DEV_PW_DEFAULT); + if (ret > 0) + os_snprintf(npin, sizeof(npin), "%08d", ret); } else { -#ifdef CONFIG_AP - if (wpa_s->ap_iface) - ret = wpa_supplicant_ap_wps_pbc(wpa_s, - params.bssid, - params.p2p_dev_addr); - else -#endif /* CONFIG_AP */ ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0); } if (ret < 0) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in " - "role %s and key %s", + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start wpas_wps_failed in role %s and key %s", (params.role == 1 ? "enrollee" : "registrar"), (params.type == 0 ? "" : (params.type == 1 ? "pin" : "pbc"))); @@ -305,31 +303,16 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, } reply = dbus_message_new_method_return(message); - if (!reply) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - } + if (!reply) + return wpas_dbus_error_no_memory(message); dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (os_strlen(npin) > 0 && + !wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) { dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - } - - if (os_strlen(npin) > 0) { - if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); - } - } - - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); } return reply; @@ -351,7 +334,8 @@ dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1); + dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &process, error); } @@ -378,7 +362,7 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, &process_credentials)) return FALSE; - old_pc = (wpa_s->conf->wps_cred_processing != 1); + old_pc = wpa_s->conf->wps_cred_processing != 1; wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1); if ((wpa_s->conf->wps_cred_processing != 1) != old_pc) @@ -408,6 +392,8 @@ dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, struct wpa_supplicant *wpa_s = user_data; char *methods = wpa_s->conf->config_methods; + if (methods == NULL) + methods = ""; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &methods, error); } diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c index 750522dc..15b09014 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/wpa_supplicant/dbus/dbus_new_helpers.c @@ -15,6 +15,7 @@ #include "dbus_common_i.h" #include "dbus_new.h" #include "dbus_new_helpers.h" +#include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" @@ -73,46 +74,36 @@ error: * with properties names as keys and theirs values as values. */ static DBusMessage * get_all_properties(DBusMessage *message, char *interface, - struct wpa_dbus_object_desc *obj_dsc) + struct wpa_dbus_object_desc *obj_dsc) { DBusMessage *reply; DBusMessageIter iter, dict_iter; DBusError error; reply = dbus_message_new_method_return(message); - if (reply == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply", - __func__); - return NULL; - } + if (reply == NULL) + return wpas_dbus_error_no_memory(message); dbus_message_iter_init_append(reply, &iter); if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { - wpa_printf(MSG_ERROR, "%s: out of memory creating reply", - __func__); dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - "out of memory"); - return reply; + return wpas_dbus_error_no_memory(message); } dbus_error_init(&error); if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, - interface, obj_dsc->user_data, &error)) - { + interface, obj_dsc->user_data, &error)) { dbus_message_unref(reply); - reply = wpas_dbus_reply_new_from_error(message, &error, - DBUS_ERROR_INVALID_ARGS, - "No readable properties" - " in this interface"); + reply = wpas_dbus_reply_new_from_error( + message, &error, DBUS_ERROR_INVALID_ARGS, + "No readable properties in this interface"); dbus_error_free(&error); return reply; } if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - "out of memory"); + return wpas_dbus_error_no_memory(message); } return reply; @@ -135,8 +126,9 @@ static int is_signature_correct(DBusMessage *message, for (arg = method_dsc->args; arg && arg->name; arg++) { if (arg->dir == ARG_IN) { size_t blen = registered_sig + MAX_SIG_LEN - pos; + ret = os_snprintf(pos, blen, "%s", arg->type); - if (ret < 0 || (size_t) ret >= blen) + if (os_snprintf_error(blen, ret)) return 0; pos += ret; } @@ -270,10 +262,13 @@ properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, } if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method, - WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) { + wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property); return properties_get(message, property_dsc, obj_dsc->user_data); + } + wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property); return properties_set(message, property_dsc, obj_dsc->user_data); } @@ -295,8 +290,7 @@ static DBusMessage * properties_handler(DBusMessage *message, !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { /* First argument: interface name (DBUS_TYPE_STRING) */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - { + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, NULL); @@ -352,8 +346,7 @@ static DBusMessage * msg_method_handler(DBusMessage *message, NULL); } - return method_dsc->method_handler(message, - obj_dsc->user_data); + return method_dsc->method_handler(message, obj_dsc->user_data); } @@ -388,8 +381,9 @@ static DBusHandlerResult message_handler(DBusConnection *connection, if (!method || !path || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)", - msg_interface, method, path); + wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); /* if message is introspection method call */ if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method, @@ -401,8 +395,7 @@ static DBusHandlerResult message_handler(DBusConnection *connection, #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */ reply = dbus_message_new_error( message, DBUS_ERROR_UNKNOWN_METHOD, - "wpa_supplicant was compiled without " - "introspection support."); + "wpa_supplicant was compiled without introspection support."); #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, WPAS_DBUS_INTERFACE_MAX)) { @@ -455,6 +448,7 @@ static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc) free_dbus_object_desc(obj_dsc); } + /** * wpa_dbus_ctrl_iface_init - Initialize dbus control interface * @application_data: Pointer to application specific data structure @@ -482,30 +476,28 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, obj_desc->path = os_strdup(dbus_path); /* Register the message handler for the global dbus interface */ - if (!dbus_connection_register_object_path(iface->con, - dbus_path, &wpa_vtable, - obj_desc)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler"); + if (!dbus_connection_register_object_path(iface->con, dbus_path, + &wpa_vtable, obj_desc)) { + wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); return -1; } /* Register our service with the message bus */ dbus_error_init(&error); - switch (dbus_bus_request_name(iface->con, dbus_service, - 0, &error)) { + switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) { case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: ret = 0; break; case DBUS_REQUEST_NAME_REPLY_EXISTS: case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "already registered"); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: already registered"); break; default: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "%s %s", error.name, error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: %s %s", + error.name, error.message); break; } dbus_error_free(&error); @@ -529,14 +521,12 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, * * Registers a new interface with dbus and assigns it a dbus object path. */ -int wpa_dbus_register_object_per_iface( - struct wpas_dbus_priv *ctrl_iface, - const char *path, const char *ifname, - struct wpa_dbus_object_desc *obj_desc) +int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface, + const char *path, const char *ifname, + struct wpa_dbus_object_desc *obj_desc) { DBusConnection *con; DBusError error; - DBusObjectPathVTable vtable = { &free_dbus_object_desc_cb, &message_handler, NULL, NULL, NULL, NULL @@ -554,14 +544,12 @@ int wpa_dbus_register_object_per_iface( /* Register the message handler for the interface functions */ if (!dbus_connection_try_register_object_path(con, path, &vtable, obj_desc, &error)) { - if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { + if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) { wpa_printf(MSG_DEBUG, "dbus: %s", error.message); } else { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler for interface %s object %s", - ifname, path); - wpa_printf(MSG_ERROR, "dbus error: %s", error.name); - wpa_printf(MSG_ERROR, "dbus: %s", error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)", + ifname, path, error.name, error.message); } dbus_error_free(&error); return -1; @@ -591,8 +579,9 @@ int wpa_dbus_unregister_object_per_iface( dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " - "private data: %s", __func__, path); + wpa_printf(MSG_ERROR, + "dbus: %s: Could not obtain object's private data: %s", + __func__, path); return 0; } @@ -626,24 +615,22 @@ static dbus_bool_t put_changed_properties( if (!dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, - NULL, &entry_iter)) - return FALSE; - - if (!dbus_message_iter_append_basic(&entry_iter, + NULL, &entry_iter) || + !dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &dsc->dbus_property)) return FALSE; dbus_error_init(&error); if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) { - if (dbus_error_is_set (&error)) { - wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " - "new value of property %s: (%s) %s", - __func__, dsc->dbus_property, - error.name, error.message); + if (dbus_error_is_set(&error)) { + wpa_printf(MSG_ERROR, + "dbus: %s: Cannot get new value of property %s: (%s) %s", + __func__, dsc->dbus_property, + error.name, error.message); } else { - wpa_printf(MSG_ERROR, "dbus: %s: Cannot get " - "new value of property %s", + wpa_printf(MSG_ERROR, + "dbus: %s: Cannot get new value of property %s", __func__, dsc->dbus_property); } dbus_error_free(&error); @@ -673,38 +660,23 @@ static void do_send_prop_changed_signal( dbus_message_iter_init_append(msg, &signal_iter); if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING, - &interface)) - goto err; - - /* Changed properties dict */ - if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, - "{sv}", &dict_iter)) - goto err; - - if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - /* Invalidated properties array (empty) */ - if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, - "s", &dict_iter)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - dbus_connection_send(con, msg, NULL); + &interface) || + /* Changed properties dict */ + !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "{sv}", &dict_iter) || + !put_changed_properties(obj_dsc, interface, &dict_iter, 0) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter) || + /* Invalidated properties array (empty) */ + !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "s", &dict_iter) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + } else { + dbus_connection_send(con, msg, NULL); + } -out: dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", - __func__); - goto out; } @@ -722,25 +694,16 @@ static void do_send_deprecated_prop_changed_signal( dbus_message_iter_init_append(msg, &signal_iter); if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, - "{sv}", &dict_iter)) - goto err; - - if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - dbus_connection_send(con, msg, NULL); + "{sv}", &dict_iter) || + !put_changed_properties(obj_dsc, interface, &dict_iter, 1) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + } else { + dbus_connection_send(con, msg, NULL); + } -out: dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", - __func__); - goto out; } @@ -772,8 +735,9 @@ static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) DBusConnection *con = eloop_ctx; struct wpa_dbus_object_desc *obj_desc = timeout_ctx; - wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " - "of object %s", __func__, obj_desc->path); + wpa_printf(MSG_DEBUG, + "dbus: %s: Timeout - sending changed properties of object %s", + __func__, obj_desc->path); wpa_dbus_flush_object_changed_properties(con, obj_desc->path); } @@ -884,8 +848,9 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, dbus_connection_get_object_path_data(iface->con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " - "could not obtain object's private data: %s", path); + wpa_printf(MSG_ERROR, + "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s", + path); return; } @@ -898,13 +863,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, } if (!dsc || !dsc->dbus_property) { - wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " - "no property %s in object %s", property, path); + wpa_printf(MSG_ERROR, + "dbus: wpa_dbus_property_changed: no property %s in object %s", + property, path); return; } if (!eloop_is_timeout_registered(flush_object_timeout_handler, - iface->con, obj_desc->path)) { + iface->con, obj_desc)) { eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, flush_object_timeout_handler, iface->con, obj_desc); @@ -936,8 +902,9 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, dbus_connection_get_object_path_data(iface->con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's " - "private data: %s", __func__, path); + wpa_printf(MSG_ERROR, + "dbus: %s: could not obtain object's private data: %s", + __func__, path); return FALSE; } @@ -951,10 +918,11 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, if (!fill_dict_with_properties(&dict_iter, obj_desc->properties, interface, obj_desc->user_data, &error)) { - wpa_printf(MSG_ERROR, "dbus: %s: failed to get object" - " properties: (%s) %s", __func__, - dbus_error_is_set(&error) ? error.name : "none", - dbus_error_is_set(&error) ? error.message : "none"); + wpa_printf(MSG_ERROR, + "dbus: %s: failed to get object properties: (%s) %s", + __func__, + dbus_error_is_set(&error) ? error.name : "none", + dbus_error_is_set(&error) ? error.message : "none"); dbus_error_free(&error); return FALSE; } @@ -965,29 +933,34 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, /** * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts * @path: The dbus object path - * @p2p_persistent_group: indicates whether to parse the path as a P2P - * persistent group object - * @network: (out) the configured network this object path refers to, if any - * @bssid: (out) the scanned bssid this object path refers to, if any - * Returns: The object path of the network interface this path refers to + * @sep: Separating part (e.g., "Networks" or "PersistentGroups") + * @item: (out) The part following the specified separator, if any + * Returns: The object path of the interface this path refers to + * + * For a given object path, decomposes the object path into object id and + * requested part, if those parts exist. The caller is responsible for freeing + * the returned value. The *item pointer points to that allocated value and must + * not be freed separately. * - * For a given object path, decomposes the object path into object id, network, - * and BSSID parts, if those parts exist. + * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and + * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1" + * getting returned and *items set to point to "0". */ -char *wpas_dbus_new_decompose_object_path(const char *path, - int p2p_persistent_group, - char **network, - char **bssid) +char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep, + char **item) { const unsigned int dev_path_prefix_len = os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); char *obj_path_only; - char *next_sep; + char *pos; + size_t sep_len; - /* Be a bit paranoid about path */ - if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", - dev_path_prefix_len)) - return NULL; + *item = NULL; + + /* Verify that this starts with our interface prefix */ + if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", + dev_path_prefix_len) != 0) + return NULL; /* not our path */ /* Ensure there's something at the end of the path */ if ((path + dev_path_prefix_len)[0] == '\0') @@ -997,39 +970,20 @@ char *wpas_dbus_new_decompose_object_path(const char *path, if (obj_path_only == NULL) return NULL; - next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/'); - if (next_sep != NULL) { - const char *net_part = os_strstr( - next_sep, p2p_persistent_group ? - WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" : - WPAS_DBUS_NEW_NETWORKS_PART "/"); - const char *bssid_part = os_strstr( - next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/"); - - if (network && net_part) { - /* Deal with a request for a configured network */ - const char *net_name = net_part + - os_strlen(p2p_persistent_group ? - WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART - "/" : - WPAS_DBUS_NEW_NETWORKS_PART "/"); - *network = NULL; - if (os_strlen(net_name)) - *network = os_strdup(net_name); - } else if (bssid && bssid_part) { - /* Deal with a request for a scanned BSSID */ - const char *bssid_name = bssid_part + - os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/"); - if (os_strlen(bssid_name)) - *bssid = os_strdup(bssid_name); - else - *bssid = NULL; - } + pos = obj_path_only + dev_path_prefix_len; + pos = os_strchr(pos, '/'); + if (pos == NULL) + return obj_path_only; /* no next item on the path */ - /* Cut off interface object path before "/" */ - *next_sep = '\0'; - } + /* Separate network interface prefix from the path */ + *pos++ = '\0'; + + sep_len = os_strlen(sep); + if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/') + return obj_path_only; /* no match */ + /* return a pointer to the requested item */ + *item = pos + sep_len + 1; return obj_path_only; } diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h index 6d31ad53..6e2c1f19 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/wpa_supplicant/dbus/dbus_new_helpers.h @@ -12,13 +12,13 @@ #include <dbus/dbus.h> -typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message, - void *user_data); -typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg); +typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message, + void *user_data); +typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg); -typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter, - DBusError *error, - void *user_data); +typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter, + DBusError *error, + void *user_data); struct wpa_dbus_object_desc { DBusConnection *connection; @@ -137,10 +137,8 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, DBusMessage * wpa_dbus_introspect(DBusMessage *message, struct wpa_dbus_object_desc *obj_dsc); -char *wpas_dbus_new_decompose_object_path(const char *path, - int p2p_persistent_group, - char **network, - char **bssid); +char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep, + char **item); DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message, DBusError *error, diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c index 3b090c02..e0dd9e2e 100644 --- a/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/wpa_supplicant/dbus/dbus_new_introspect.c @@ -96,6 +96,7 @@ static void extract_interfaces_methods( { const struct wpa_dbus_method_desc *dsc; struct interfaces *iface; + for (dsc = methods; dsc && dsc->dbus_method; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -110,6 +111,7 @@ static void extract_interfaces_signals( { const struct wpa_dbus_signal_desc *dsc; struct interfaces *iface; + for (dsc = signals; dsc && dsc->dbus_signal; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -124,6 +126,7 @@ static void extract_interfaces_properties( { const struct wpa_dbus_property_desc *dsc; struct interfaces *iface; + for (dsc = properties; dsc && dsc->dbus_property; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -154,14 +157,14 @@ static void extract_interfaces(struct dl_list *list, static void add_interfaces(struct dl_list *list, struct wpabuf *xml) { struct interfaces *iface, *n; + dl_list_for_each_safe(iface, n, list, struct interfaces, list) { if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) { wpabuf_put_buf(xml, iface->xml); wpabuf_put_str(xml, "</interface>"); } else { - wpa_printf(MSG_DEBUG, "dbus: Not enough room for " - "add_interfaces inspect data: tailroom %u, " - "add %u", + wpa_printf(MSG_DEBUG, + "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u", (unsigned int) wpabuf_tailroom(xml), (unsigned int) wpabuf_len(iface->xml)); } @@ -229,6 +232,7 @@ static void add_wpas_interfaces(struct wpabuf *xml, struct wpa_dbus_object_desc *obj_dsc) { struct dl_list ifaces; + dl_list_init(&ifaces); extract_interfaces(&ifaces, obj_dsc); add_interfaces(&ifaces, xml); @@ -270,6 +274,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, reply = dbus_message_new_method_return(message); if (reply) { const char *intro_str = wpabuf_head(xml); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str, DBUS_TYPE_INVALID); } diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c index 85d8a787..28991322 100644 --- a/wpa_supplicant/dbus/dbus_old.c +++ b/wpa_supplicant/dbus/dbus_old.c @@ -92,9 +92,9 @@ char * wpas_dbus_decompose_object_path(const char *path, char **network, */ DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE, - "wpa_supplicant knows nothing about " - "this interface."); + return dbus_message_new_error( + message, WPAS_ERROR_INVALID_IFACE, + "wpa_supplicant knows nothing about this interface."); } @@ -216,8 +216,12 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, if (!msg_interface) goto out; + wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); + iface_obj_path = wpas_dbus_decompose_object_path(path, &network, - &bssid); + &bssid); if (iface_obj_path == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; @@ -227,7 +231,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, * wpa_supplicant structure it's supposed to (which is wpa_s) */ if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, - iface_obj_path) != wpa_s) { + iface_obj_path) != wpa_s) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } @@ -235,6 +239,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { /* A method for one of this interface's configured networks */ int nid = strtoul(network, NULL, 10); + if (errno != EINVAL) reply = wpas_dispatch_network_method(message, wpa_s, nid); @@ -275,14 +280,14 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, reply = wpas_dbus_iface_remove_blobs(message, wpa_s); #endif /* CONFIG_NO_CONFIG_BLOBS */ #ifdef CONFIG_WPS - else if (!os_strcmp(method, "wpsPbc")) + else if (os_strcmp(method, "wpsPbc") == 0) reply = wpas_dbus_iface_wps_pbc(message, wpa_s); - else if (!os_strcmp(method, "wpsPin")) + else if (os_strcmp(method, "wpsPin") == 0) reply = wpas_dbus_iface_wps_pin(message, wpa_s); - else if (!os_strcmp(method, "wpsReg")) + else if (os_strcmp(method, "wpsReg") == 0) reply = wpas_dbus_iface_wps_reg(message, wpa_s); #endif /* CONFIG_WPS */ - else if (!os_strcmp(method, "flush")) + else if (os_strcmp(method, "flush") == 0) reply = wpas_dbus_iface_flush(message, wpa_s); } @@ -328,6 +333,10 @@ static DBusHandlerResult wpas_message_handler(DBusConnection *connection, if (!method || !path || !ctrl_iface || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); + /* Validate the method interface */ if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -381,8 +390,8 @@ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) WPAS_DBUS_IFACE_INTERFACE, "ScanResultsAvailable"); if (_signal == NULL) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " - "results signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to send scan results signal"); return; } dbus_connection_send(iface->con, _signal, NULL); @@ -426,29 +435,21 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, "StateChange"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "could not create dbus signal; likely out of " - "memory"); + "dbus: %s: could not create dbus signal; likely out of memory", + __func__); return; } new_state_str = wpa_supplicant_state_txt(new_state); old_state_str = wpa_supplicant_state_txt(old_state); - if (new_state_str == NULL || old_state_str == NULL) { - wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "Could not convert state strings"); - goto out; - } if (!dbus_message_append_args(_signal, - DBUS_TYPE_STRING, &new_state_str, - DBUS_TYPE_STRING, &old_state_str, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_STRING, &new_state_str, + DBUS_TYPE_STRING, &old_state_str, + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "Not enough memory to construct state change " - "signal"); + "dbus: %s: Not enough memory to construct state change signal", + __func__); goto out; } @@ -480,18 +481,18 @@ void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) WPAS_DBUS_IFACE_INTERFACE, "Scanning"); if (_signal == NULL) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " - "results signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to send scan results signal"); return; } if (dbus_message_append_args(_signal, - DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID)) { dbus_connection_send(iface->con, _signal, NULL); } else { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct " - "signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to construct signal"); } dbus_message_unref(_signal); } @@ -516,19 +517,18 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, "WpsCred"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_wps_cred: " - "Could not create dbus signal; likely out of " - "memory"); + "dbus: %s: Could not create dbus signal; likely out of memory", + __func__); return; } if (!dbus_message_append_args(_signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cred->cred_attr, cred->cred_attr_len, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_wps_cred: " - "Not enough memory to construct signal"); + "dbus: %s: Not enough memory to construct signal", + __func__); goto out; } @@ -567,9 +567,8 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, "Certification"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_certification: " - "Could not create dbus signal; likely out of " - "memory"); + "dbus: %s: Could not create dbus signal; likely out of memory", + __func__); return; } @@ -578,15 +577,15 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, cert_hex_len = cert ? wpabuf_len(cert) : 0; if (!dbus_message_append_args(_signal, - DBUS_TYPE_INT32,&depth, + DBUS_TYPE_INT32, &depth, DBUS_TYPE_STRING, &subject, - DBUS_TYPE_STRING, &hash, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + DBUS_TYPE_STRING, &hash, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cert_hex, cert_hex_len, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_certification: " - "Not enough memory to construct signal"); + "dbus: %s: Not enough memory to construct signal", + __func__); goto out; } @@ -618,8 +617,7 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) if (!dbus_connection_register_object_path(iface->con, WPAS_DBUS_PATH, &wpas_vtable, iface)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler"); + wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); return -1; } @@ -633,12 +631,13 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) case DBUS_REQUEST_NAME_REPLY_EXISTS: case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "already registered"); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: already registered"); break; default: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "%s %s", error.name, error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: %s %s", + error.name, error.message); break; } dbus_error_free(&error); @@ -687,8 +686,9 @@ int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) /* Register the message handler for the interface functions */ if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable, wpa_s)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler for interface %s", wpa_s->ifname); + wpa_printf(MSG_ERROR, + "dbus: Could not set up message handler for interface %s", + wpa_s->ifname); return -1; } diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h index e6682310..451a9f82 100644 --- a/wpa_supplicant/dbus/dbus_old.h +++ b/wpa_supplicant/dbus/dbus_old.h @@ -82,7 +82,7 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, const struct wpabuf *cert); char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid); + char **bssid); int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); @@ -104,7 +104,12 @@ wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) { } -#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0) +static inline void +wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state) +{ +} static inline void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c index 048158f2..504de2af 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/wpa_supplicant/dbus/dbus_old_handlers.c @@ -37,9 +37,9 @@ DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, { DBusMessage *reply; - reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, - "Did not receive correct message " - "arguments."); + reply = dbus_message_new_error( + message, WPAS_ERROR_INVALID_OPTS, + "Did not receive correct message arguments."); if (arg != NULL) dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); @@ -112,28 +112,28 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "driver") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(driver); driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; } else if (!strcmp(entry.key, "driver-params") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(driver_param); driver_param = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver_param == NULL) goto error; } else if (!strcmp(entry.key, "config-file") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(confname); confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) goto error; } else if (!strcmp(entry.key, "bridge-ifname") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(bridge_ifname); bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); @@ -151,13 +151,13 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, * an error if we already control it. */ if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error(message, - WPAS_ERROR_EXISTS_ERROR, - "wpa_supplicant already " - "controls this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_EXISTS_ERROR, + "wpa_supplicant already controls this interface."); } else { struct wpa_supplicant *wpa_s; struct wpa_interface iface; + os_memset(&iface, 0, sizeof(iface)); iface.ifname = ifname; iface.driver = driver; @@ -165,17 +165,17 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, iface.confname = confname; iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ - if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + wpa_s = wpa_supplicant_add_iface(global, &iface); + if (wpa_s) { const char *path = wpa_s->dbus_path; + reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); + &path, DBUS_TYPE_INVALID); } else { - reply = dbus_message_new_error(message, - WPAS_ERROR_ADD_ERROR, - "wpa_supplicant " - "couldn't grab this " - "interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "wpa_supplicant couldn't grab this interface."); } } @@ -226,10 +226,9 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_new_success_reply(message); } else { - reply = dbus_message_new_error(message, - WPAS_ERROR_REMOVE_ERROR, - "wpa_supplicant couldn't " - "remove this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_REMOVE_ERROR, + "wpa_supplicant couldn't remove this interface."); } out: @@ -256,8 +255,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, struct wpa_supplicant *wpa_s; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &ifname, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_STRING, &ifname, + DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } @@ -271,8 +270,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); out: return reply; @@ -298,10 +297,10 @@ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, 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)) { + DBUS_TYPE_INT32, &debug_level, + DBUS_TYPE_BOOLEAN, &debug_timestamp, + DBUS_TYPE_BOOLEAN, &debug_show_keys, + DBUS_TYPE_INVALID)) { return wpas_dbus_new_invalid_opts_error(message, NULL); } @@ -409,84 +408,56 @@ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, { DBusMessage *reply; DBusMessageIter iter, iter_dict; - const u8 *ie; + const u8 *wpa_ie, *rsn_ie, *wps_ie; /* Dump the properties into a dbus message */ reply = dbus_message_new_method_return(message); - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto error; + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict) || + !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", (const char *) bss->bssid, - ETH_ALEN)) - goto error; - - ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", - (const char *) (ie + 2), - ie[1])) - goto error; - } - - ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", - (const char *) ie, - ie[1] + 2)) - goto error; - } - - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", - (const char *) ie, - ie[1] + 2)) - goto error; - } - - ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", - (const char *) ie, - ie[1] + 2)) - goto error; + ETH_ALEN) || + !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", + (const char *) bss->ssid, + bss->ssid_len) || + (wpa_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", + (const char *) wpa_ie, + wpa_ie[1] + 2)) || + (rsn_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", + (const char *) rsn_ie, + rsn_ie[1] + 2)) || + (wps_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", + (const char *) wps_ie, + wps_ie[1] + 2)) || + (bss->freq && + !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) || + !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", + bss->caps) || + (!(bss->flags & WPA_BSS_QUAL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) || + (!(bss->flags & WPA_BSS_NOISE_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) || + (!(bss->flags & WPA_BSS_LEVEL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) || + !wpa_dbus_dict_append_int32(&iter_dict, "maxrate", + wpa_bss_get_max_rate(bss) * 500000) || + !wpa_dbus_dict_close_write(&iter, &iter_dict)) { + if (reply) + dbus_message_unref(reply); + reply = dbus_message_new_error( + message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning BSSID properties."); } - if (bss->freq) { - if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", - bss->freq)) - goto error; - } - if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", - bss->caps)) - goto error; - if (!(bss->flags & WPA_BSS_QUAL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) - goto error; - if (!(bss->flags & WPA_BSS_NOISE_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) - goto error; - if (!(bss->flags & WPA_BSS_LEVEL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) - goto error; - if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", - wpa_bss_get_max_rate(bss) * 500000)) - goto error; - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) - goto error; - return reply; - -error: - if (reply) - dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning " - "BSSID properties."); } @@ -546,6 +517,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = {"CCMP", "TKIP", "NONE"}; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "pairwise", args, ARRAY_SIZE(args))) @@ -555,28 +527,17 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) - goto error; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "NONE")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "NONE")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -589,6 +550,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, const char *args[] = { "CCMP", "TKIP", "WEP104", "WEP40" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "group", args, ARRAY_SIZE(args))) @@ -601,31 +563,19 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, &iter_array)) goto error; - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP104")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP40")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP104")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP40")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -648,38 +598,23 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "NONE")) - goto error; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "IEEE8021X")) - goto error; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-EAP")) - goto error; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-PSK")) - goto error; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-NONE")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "NONE") || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "IEEE8021X") || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-EAP")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-PSK")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-NONE")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -690,6 +625,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = { "RSN", "WPA" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "proto", args, ARRAY_SIZE(args))) @@ -699,24 +635,16 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "RSN")) - goto error; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "RSN")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -727,6 +655,7 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = { "OPEN", "SHARED", "LEAP" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "auth_alg", args, ARRAY_SIZE(args))) @@ -736,28 +665,17 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "OPEN")) - goto error; - } - - if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "SHARED")) - goto error; - } - - if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "LEAP")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.auth & WPA_DRIVER_AUTH_OPEN) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "OPEN")) || + ((capa.auth & WPA_DRIVER_AUTH_SHARED) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "SHARED")) || + ((capa.auth & WPA_DRIVER_AUTH_LEAP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "LEAP")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -772,9 +690,9 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, error: if (reply) dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning " - "interface capabilities."); + return dbus_message_new_error( + message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning interface capabilities."); } @@ -795,10 +713,9 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - reply = dbus_message_new_error(message, - WPAS_ERROR_ADD_NETWORK_ERROR, - "wpa_supplicant could not add " - "a network on this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_NETWORK_ERROR, + "wpa_supplicant could not add a network on this interface."); goto out; } wpas_notify_network_added(wpa_s, ssid); @@ -838,15 +755,15 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, struct wpa_ssid *ssid; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /* Extract the network ID */ iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); - if (iface == NULL) { + if (iface == NULL || net_id == NULL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -866,17 +783,17 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, wpas_notify_network_removed(wpa_s, ssid); + if (ssid == wpa_s->current_ssid) + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { - reply = dbus_message_new_error(message, - WPAS_ERROR_REMOVE_NETWORK_ERROR, - "error removing the specified " - "on this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_REMOVE_NETWORK_ERROR, + "error removing the specified on this interface."); goto out; } - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); reply = wpas_dbus_new_success_reply(message); out: @@ -886,7 +803,7 @@ out: } -static const char *dont_quote[] = { +static const char const *dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", "bssid", NULL @@ -896,8 +813,9 @@ static const char *dont_quote[] = { static dbus_bool_t should_quote_opt(const char *key) { int i = 0; + while (dont_quote[i] != NULL) { - if (strcmp(key, dont_quote[i]) == 0) + if (os_strcmp(key, dont_quote[i]) == 0) return FALSE; i++; } @@ -968,7 +886,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "\"%s\"", entry.str_value); - if (ret < 0 || (size_t) ret != (size - 1)) + if (os_snprintf_error(size, ret)) goto error; } else { value = os_strdup(entry.str_value); @@ -981,7 +899,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "%u", entry.uint32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else if (entry.type == DBUS_TYPE_INT32) { value = os_zalloc(size); @@ -989,7 +907,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "%d", entry.int32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else goto error; @@ -1102,7 +1020,8 @@ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, goto out; } /* Ensure the object path really points to this interface */ - if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { + if (network == NULL || + os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -1212,19 +1131,19 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "opensc_engine_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(opensc_engine_path); opensc_engine_path = os_strdup(entry.str_value); if (opensc_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_engine_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(pkcs11_engine_path); pkcs11_engine_path = os_strdup(entry.str_value); if (pkcs11_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_module_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { os_free(pkcs11_module_path); pkcs11_module_path = os_strdup(entry.str_value); if (pkcs11_module_path == NULL) @@ -1304,8 +1223,8 @@ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, DBUS_TYPE_INVALID); } else { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to return " - "scanning state"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to return scanning state"); } return reply; @@ -1378,7 +1297,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, blob->len = entry.array_len; os_memcpy(blob->data, (u8 *) entry.bytearray_value, entry.array_len); - if (blob->name == NULL || blob->data == NULL) { + if (blob->name == NULL) { wpa_config_free_blob(blob); reply = dbus_message_new_error( message, WPAS_ERROR_ADD_ERROR, @@ -1417,8 +1336,8 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, dbus_message_iter_init(message, &iter); - if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || - (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) return wpas_dbus_new_invalid_opts_error(message, NULL); dbus_message_iter_recurse(&iter, &array); @@ -1428,8 +1347,7 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, dbus_message_iter_get_basic(&array, &name); if (!os_strlen(name)) err_msg = "Invalid blob name."; - - if (wpa_config_remove_blob(wpa_s->conf, name) != 0) + else if (wpa_config_remove_blob(wpa_s->conf, name) != 0) err_msg = "Error removing blob."; else wpas_notify_blob_removed(wpa_s, name); diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h index 825bc6d2..e60ad06a 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.h +++ b/wpa_supplicant/dbus/dbus_old_handlers.h @@ -58,13 +58,13 @@ DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, struct wpa_ssid *ssid); DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_set_smartcard_modules( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -76,7 +76,7 @@ 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); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c index bb793824..3cf9dc3f 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c @@ -36,7 +36,7 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) + if (os_strcmp(arg_bssid, "any") == 0) ret = wpas_wps_start_pbc(wpa_s, NULL, 0); else if (!hwaddr_aton(arg_bssid, bssid)) ret = wpas_wps_start_pbc(wpa_s, bssid, 0); @@ -46,10 +46,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, } if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_PBC_ERROR, - "Could not start PBC " - "negotiation"); + return dbus_message_new_error( + message, WPAS_ERROR_WPS_PBC_ERROR, + "Could not start PBC negotiation"); } return wpas_dbus_new_success_reply(message); @@ -73,12 +72,13 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, char *pin = NULL; u8 bssid[ETH_ALEN], *_bssid = NULL; int ret = 0; + char npin[9]; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) + if (os_strcmp(arg_bssid, "any") == 0) _bssid = NULL; else if (!hwaddr_aton(arg_bssid, bssid)) _bssid = bssid; @@ -104,15 +104,12 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, if (reply == NULL) return NULL; - if (ret == 0) { - dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, - DBUS_TYPE_INVALID); - } else { - char npin[9]; + if (ret > 0) { os_snprintf(npin, sizeof(npin), "%08d", ret); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin, - DBUS_TYPE_INVALID); + pin = npin; } + dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, + DBUS_TYPE_INVALID); return reply; } @@ -138,9 +135,7 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) - ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL); - else if (!hwaddr_aton(arg_bssid, bssid)) + if (!hwaddr_aton(arg_bssid, bssid)) ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL); else { return wpas_dbus_new_invalid_opts_error(message, diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 94c94b12..7f627fdd 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -67,9 +67,6 @@ CONFIG_DRIVER_NL80211=y # wpa_supplicant. # CONFIG_USE_NDISUIO=y -# Driver interface for development testing -#CONFIG_DRIVER_TEST=y - # Driver interface for wired Ethernet drivers CONFIG_DRIVER_WIRED=y diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 182060d1..e7bf4e07 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -469,7 +469,7 @@ <para>Enable DBus control interface. If enabled, interface definitions may be omitted. (This is only available if <command>wpa_supplicant</command> was built with - the <literal>CONFIG_DBUS</literal> option.)</para>0 + the <literal>CONFIG_DBUS</literal> option.)</para> </listitem> </varlistentry> diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 649de9bd..8dc48d38 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -65,6 +65,28 @@ static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->init_mesh) + return wpa_s->driver->init_mesh(wpa_s->drv_priv); + return -1; +} + +static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_driver_mesh_join_params *params) +{ + if (wpa_s->driver->join_mesh) + return wpa_s->driver->join_mesh(wpa_s->drv_priv, params); + return -1; +} + +static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->leave_mesh) + return wpa_s->driver->leave_mesh(wpa_s->drv_priv); + return -1; +} + static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { @@ -222,16 +244,6 @@ static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) return NULL; } -static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s, - const u8 *dst, u16 proto, - const u8 *data, size_t data_len) -{ - if (wpa_s->driver->send_eapol) - return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto, - data, data_len); - return -1; -} - static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s, int state) { @@ -288,16 +300,6 @@ static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s, - u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len) -{ - if (wpa_s->driver->send_ft_action) - return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action, - target_ap, ies, ies_len); - return -1; -} - static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s, struct wpa_driver_ap_params *params) { @@ -587,6 +589,45 @@ static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings); } +static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid, + const u8 *address, u8 user_priority, + u16 admitted_time) +{ + if (!wpa_s->driver->add_tx_ts) + return -1; + return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address, + user_priority, admitted_time); +} + +static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid, + const u8 *address) +{ + if (!wpa_s->driver->del_tx_ts) + return -1; + return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address); +} + +static inline int wpa_drv_tdls_enable_channel_switch( + struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *freq_params) +{ + if (!wpa_s->driver->tdls_enable_channel_switch) + return -1; + return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr, + oper_class, + freq_params); +} + +static inline int +wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s, + const u8 *addr) +{ + if (!wpa_s->driver->tdls_disable_channel_switch) + return -1; + return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv, + addr); +} + static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, enum wnm_oper oper, const u8 *peer, u8 *buf, u16 *buf_len) diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index e19782f7..aa9ab50c 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -461,7 +461,7 @@ static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, len = os_snprintf(buf, buflen, WPA_CTRL_REQ "%s-%d:%s needed for SSID ", field_name, ssid->id, txt); - if (len < 0 || (size_t) len >= buflen) { + if (os_snprintf_error(buflen, len)) { os_free(buf); return; } @@ -568,6 +568,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers; ctx->eap_param_needed = eapol_test_eap_param_needed; ctx->cert_cb = eapol_test_cert_cb; ctx->cert_in_cb = 1; @@ -928,7 +929,11 @@ static void wpa_init_conf(struct eapol_test_data *e, *pos++ = a[3]; } #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - inet_aton(authsrv, &as->addr.u.v4); + if (inet_aton(authsrv, &as->addr.u.v4) < 0) { + wpa_printf(MSG_ERROR, "Invalid IP address '%s'", + authsrv); + assert(0); + } #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ as->addr.af = AF_INET; as->port = port; diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 985fa6e7..8464ed41 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -42,6 +42,9 @@ #include "scan.h" #include "offchannel.h" #include "interworking.h" +#include "mesh.h" +#include "mesh_mpm.h" +#include "wmm_ac.h" #ifndef CONFIG_NO_SCAN_PROCESSING @@ -199,20 +202,12 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) bssid_changed = !is_zero_ether_addr(wpa_s->bssid); os_memset(wpa_s->bssid, 0, ETH_ALEN); os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); -#ifdef CONFIG_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ + sme_clear_on_disassoc(wpa_s); #ifdef CONFIG_P2P os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN); #endif /* CONFIG_P2P */ wpa_s->current_bss = NULL; wpa_s->assoc_freq = 0; -#ifdef CONFIG_IEEE80211R -#ifdef CONFIG_SME - if (wpa_s->sme.ft_ies) - sme_update_ft_ies(wpa_s, NULL, NULL, 0); -#endif /* CONFIG_SME */ -#endif /* CONFIG_IEEE80211R */ if (bssid_changed) wpas_notify_bssid_changed(wpa_s); @@ -225,6 +220,8 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpa_s->current_ssid = NULL; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->key_mgmt = 0; + + wpas_rrm_reset(wpa_s); } @@ -582,42 +579,6 @@ static int freq_allowed(int *freqs, int freq) } -int ht_supported(const struct hostapd_hw_modes *mode) -{ - if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) { - /* - * The driver did not indicate whether it supports HT. Assume - * it does to avoid connection issues. - */ - return 1; - } - - /* - * IEEE Std 802.11n-2009 20.1.1: - * An HT non-AP STA shall support all EQM rates for one spatial stream. - */ - return mode->mcs_set[0] == 0xff; -} - - -int vht_supported(const struct hostapd_hw_modes *mode) -{ - if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) { - /* - * The driver did not indicate whether it supports VHT. Assume - * it does to avoid connection issues. - */ - return 1; - } - - /* - * A VHT non-AP STA shall support MCS 0-7 for one spatial stream. - * TODO: Verify if this complies with the standard - */ - return (mode->vht_mcs_set[0] & 0x3) != 3; -} - - static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { const struct hostapd_hw_modes *mode = NULL, *modes; @@ -1136,7 +1097,8 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->mode == IEEE80211_MODE_IBSS || - ssid->mode == IEEE80211_MODE_AP) + ssid->mode == IEEE80211_MODE_AP || + ssid->mode == IEEE80211_MODE_MESH) return ssid; } } @@ -1346,6 +1308,9 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, return 0; } + if (wnm_scan_process(wpa_s, 1) > 0) + goto scan_work_done; + if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; @@ -1416,6 +1381,13 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, */ return 1; } else { +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, + "Avoiding join because we already joined a mesh group"); + return 0; + } +#endif /* CONFIG_MESH */ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { @@ -2067,6 +2039,15 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IBSS_RSN */ wpas_wps_notify_assoc(wpa_s, bssid); + + if (data) { + wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &data->assoc_info.wmm_params); + + if (wpa_s->reassoc_same_bss) + wmm_ac_restore_tspecs(wpa_s); + } } @@ -2801,7 +2782,8 @@ static void wpa_supplicant_update_channel_list( static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, - const u8 *frame, size_t len, int freq) + const u8 *frame, size_t len, int freq, + int rssi) { const struct ieee80211_mgmt *mgmt; const u8 *payload; @@ -2820,6 +2802,11 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, " Category=%u DataLen=%d freq=%d MHz", MAC2STR(mgmt->sa), category, (int) plen, freq); + if (category == WLAN_ACTION_WMM) { + wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen); + return; + } + #ifdef CONFIG_IEEE80211R if (category == WLAN_ACTION_FT) { ft_rx_action(wpa_s, payload, plen); @@ -2877,8 +2864,24 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_INTERWORKING */ + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { + wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); + return; + } + + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) { + wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa, + payload + 1, plen - 1, + rssi); + return; + } + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); + if (wpa_s->ifmsh) + mesh_mpm_action_rx(wpa_s, mgmt, len); } @@ -2934,6 +2937,24 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, } +static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, + "Connection authorized by device, previous state %d", + wpa_s->wpa_state); + if (wpa_s->wpa_state == WPA_ASSOCIATED) { + wpa_supplicant_cancel_auth_timeout(wpa_s); + wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); + eapol_sm_notify_portValid(wpa_s->eapol, TRUE); + eapol_sm_notify_eap_success(wpa_s->eapol, TRUE); + } + wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr); + wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck, + data->assoc_info.ptk_kek); +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -2974,6 +2995,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_ASSOC: wpa_supplicant_event_assoc(wpa_s, data); + if (data && data->assoc_info.authorized) + wpa_supplicant_event_assoc_auth(wpa_s, data); break; case EVENT_DISASSOC: wpas_event_disassoc(wpa_s, @@ -3084,10 +3107,24 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } break; case EVENT_AUTH_TIMED_OUT: + /* It is possible to get this event from earlier connection */ + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_MESH) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore AUTH_TIMED_OUT in mesh configuration"); + break; + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: + /* It is possible to get this event from earlier connection */ + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_MESH) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore ASSOC_TIMED_OUT in mesh configuration"); + break; + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_timed_out(wpa_s, data); break; @@ -3228,7 +3265,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #endif /* CONFIG_P2P */ #ifdef CONFIG_IBSS_RSN - if (stype == WLAN_FC_STYPE_AUTH && + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_IBSS && + stype == WLAN_FC_STYPE_AUTH && data->rx_mgmt.frame_len >= 30) { wpa_supplicant_event_ibss_auth(wpa_s, data); break; @@ -3239,7 +3278,13 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpas_event_rx_mgmt_action( wpa_s, data->rx_mgmt.frame, data->rx_mgmt.frame_len, - data->rx_mgmt.freq); + data->rx_mgmt.freq, + data->rx_mgmt.ssi_signal); + break; + } + + if (wpa_s->ifmsh) { + mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt); break; } @@ -3475,6 +3520,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->connect_failed_reason.code); #endif /* CONFIG_AP */ break; + case EVENT_NEW_PEER_CANDIDATE: +#ifdef CONFIG_MESH + if (!wpa_s->ifmsh || !data) + break; + wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer, + data->mesh_peer.ies, + data->mesh_peer.ie_len); +#endif /* CONFIG_MESH */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff --git a/wpa_supplicant/examples/wps-ap-cli b/wpa_supplicant/examples/wps-ap-cli index 7c6b0aa8..cc2cff2e 100755 --- a/wpa_supplicant/examples/wps-ap-cli +++ b/wpa_supplicant/examples/wps-ap-cli @@ -14,11 +14,13 @@ pbc() enter_pin() { echo "Enter a PIN from a station to be enrolled to the network." - read -p "Enrollee PIN: " pin + echo -n "Enrollee PIN: " + read pin cpin=`$CLI wps_check_pin "$pin" | tail -1` if [ "$cpin" = "FAIL-CHECKSUM" ]; then echo "Checksum digit is not valid" - read -p "Do you want to use this PIN (y/n)? " resp + echo -n "Do you want to use this PIN (y/n)? " + read resp case "$resp" in y*) cpin=`echo "$pin" | sed "s/[^1234567890]//g"` @@ -50,7 +52,8 @@ main_menu() echo "3: Show current configuration" echo "0: Exit wps-ap-cli" - read -p "Command: " cmd + echo -n "Command: " + read cmd case "$cmd" in 1) diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 3a89674f..10ecce7b 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -442,6 +442,7 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, u16 comeback_delay, resp_len; const u8 *pos, *adv_proto; int prot, pmf; + unsigned int left; if (gas == NULL || len < 4) return -1; @@ -543,17 +544,17 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, resp_len = WPA_GET_LE16(pos); pos += 2; - if (pos + resp_len > data + len) { + left = data + len - pos; + if (resp_len > left) { wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " "response from " MACSTR, MAC2STR(sa)); return 0; } - if (pos + resp_len < data + len) { + if (resp_len < left) { wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " "after Query Response from " MACSTR, - (unsigned int) (data + len - pos - resp_len), - MAC2STR(sa)); + left - resp_len, MAC2STR(sa)); } if (action == WLAN_PA_GAS_COMEBACK_RESP) diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index 257aa6d1..9eb50646 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -562,6 +562,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *end = pos + len; u16 len2; const u8 *pos2; + u8 uri_len, osu_method_len, osu_nai_len; wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len); prov = os_realloc_array(wpa_s->osu_prov, @@ -585,7 +586,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } len2 = WPA_GET_LE16(pos); pos += 2; - if (pos + len2 > end) { + if (len2 > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Friendly Name Duples"); return; @@ -607,22 +608,34 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } /* OSU Server URI */ - if (pos + 1 > end || pos + 1 + pos[0] > end) { + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Not enough room for OSU Server URI length"); + return; + } + uri_len = *pos++; + if (uri_len > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server " "URI"); return; } - os_memcpy(prov->server_uri, pos + 1, pos[0]); - pos += 1 + pos[0]; + os_memcpy(prov->server_uri, pos, uri_len); + pos += uri_len; /* OSU Method list */ - if (pos + 1 > end || pos + 1 + pos[0] > end) { + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " + "list length"); + return; + } + osu_method_len = pos[0]; + if (osu_method_len > end - pos - 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " "list"); return; } pos2 = pos + 1; - pos += 1 + pos[0]; + pos += 1 + osu_method_len; while (pos2 < pos) { if (*pos2 < 32) prov->osu_methods |= BIT(*pos2); @@ -637,7 +650,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } len2 = WPA_GET_LE16(pos); pos += 2; - if (pos + len2 > end) { + if (len2 > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " "Available"); return; @@ -648,6 +661,8 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, /* Icons Available */ while (pos2 < pos) { struct osu_icon *icon = &prov->icon[prov->icon_count]; + u8 flen; + if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); break; @@ -660,31 +675,43 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, os_memcpy(icon->lang, pos2, 3); pos2 += 3; - if (pos2 + 1 + pos2[0] > pos) { + flen = pos2[0]; + if (flen > pos - pos2 - 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); break; } - os_memcpy(icon->icon_type, pos2 + 1, pos2[0]); - pos2 += 1 + pos2[0]; + os_memcpy(icon->icon_type, pos2 + 1, flen); + pos2 += 1 + flen; - if (pos2 + 1 + pos2[0] > pos) { + if (pos2 + 1 > pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " + "Filename length"); + break; + } + flen = pos2[0]; + if (flen > pos - pos2 - 1) { wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " "Filename"); break; } - os_memcpy(icon->filename, pos2 + 1, pos2[0]); - pos2 += 1 + pos2[0]; + os_memcpy(icon->filename, pos2 + 1, flen); + pos2 += 1 + flen; prov->icon_count++; } /* OSU_NAI */ - if (pos + 1 > end || pos + 1 + pos[0] > end) { + if (pos + 1 > end) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); return; } - os_memcpy(prov->osu_nai, pos + 1, pos[0]); - pos += 1 + pos[0]; + osu_nai_len = pos[0]; + if (osu_nai_len > end - pos - 1) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); + return; + } + os_memcpy(prov->osu_nai, pos + 1, osu_nai_len); + pos += 1 + osu_nai_len; /* OSU Service Description Length */ if (pos + 2 > end) { @@ -694,7 +721,7 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } len2 = WPA_GET_LE16(pos); pos += 2; - if (pos + len2 > end) { + if (len2 > end - pos) { wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " "Service Description Duples"); return; @@ -705,15 +732,18 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, /* OSU Service Description Duples */ while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) { struct osu_lang_string *f; - if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) { + u8 descr_len; + + descr_len = pos2[0]; + if (descr_len > pos - pos2 - 1 || descr_len < 3) { wpa_printf(MSG_DEBUG, "Invalid OSU Service " "Description"); break; } f = &prov->serv_desc[prov->serv_desc_count++]; os_memcpy(f->lang, pos2 + 1, 3); - os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3); - pos2 += 1 + pos2[0]; + os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3); + pos2 += 1 + descr_len; } wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, @@ -778,7 +808,7 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) num_providers--; len = WPA_GET_LE16(pos); pos += 2; - if (pos + len > end) + if (len > (unsigned int) (end - pos)) break; hs20_osu_add_prov(wpa_s, bss, osu_ssid, osu_ssid_len, pos, len); @@ -801,6 +831,10 @@ static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed"); + if (!wpa_s->fetch_osu_waiting_scan) { + wpa_printf(MSG_DEBUG, "OSU fetch have been canceled"); + return; + } wpa_s->network_select = 0; wpa_s->fetch_all_anqp = 1; wpa_s->fetch_osu_info = 1; @@ -849,6 +883,7 @@ int hs20_fetch_osu(struct wpa_supplicant *wpa_s) void hs20_start_osu_scan(struct wpa_supplicant *wpa_s) { + wpa_s->fetch_osu_waiting_scan = 1; wpa_s->num_osu_scans++; wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->scan_res_handler = hs20_osu_scan_res_handler; @@ -860,6 +895,7 @@ void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Cancel OSU fetch"); interworking_stop_fetch_anqp(wpa_s); + wpa_s->fetch_osu_waiting_scan = 0; wpa_s->network_select = 0; wpa_s->fetch_osu_info = 0; wpa_s->fetch_osu_icon_in_progress = 0; diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 3083dd80..d0ae135b 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -72,7 +72,7 @@ static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, if (wpa_s->l2) return l2_packet_send(wpa_s->l2, dest, proto, buf, len); - return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); + return -1; } @@ -230,7 +230,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(peer->supp, psk, PMK_LEN); + wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL); peer->supp_ie_len = sizeof(peer->supp_ie); if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, @@ -283,7 +283,7 @@ static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data, data_len); - return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len); + return -1; } diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 19b6e38d..116df052 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -508,20 +508,25 @@ static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) struct nai_realm *realm; const u8 *pos, *end; u16 i, num; + size_t left; - if (anqp == NULL || wpabuf_len(anqp) < 2) + if (anqp == NULL) + return NULL; + left = wpabuf_len(anqp); + if (left < 2) return NULL; pos = wpabuf_head_u8(anqp); - end = pos + wpabuf_len(anqp); + end = pos + left; num = WPA_GET_LE16(pos); wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num); pos += 2; + left -= 2; - if (num * 5 > end - pos) { + if (num > left / 5) { wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not " "enough data (%u octets) for that many realms", - num, (unsigned int) (end - pos)); + num, (unsigned int) left); return NULL; } @@ -2525,6 +2530,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) if (found == 0) { if (wpa_s->fetch_osu_info) { if (wpa_s->num_prov_found == 0 && + wpa_s->fetch_osu_waiting_scan && wpa_s->num_osu_scans < 3) { wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again"); hs20_start_osu_scan(wpa_s); @@ -2808,7 +2814,9 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, end = pos + wpabuf_len(resp); while (pos < end) { - if (pos + 4 > end) { + unsigned int left = end - pos; + + if (left < 4) { wpa_printf(MSG_DEBUG, "ANQP: Invalid element"); break; } @@ -2816,7 +2824,8 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, pos += 2; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) { + left -= 4; + if (left < slen) { wpa_printf(MSG_DEBUG, "ANQP: Invalid element length " "for Info ID %u", info_id); break; diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index e5964684..13e97694 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -327,14 +327,6 @@ int main(int argc, char *argv[]) exitcode = -1; break; } -#ifdef CONFIG_P2P - if (wpa_s->global->p2p == NULL && - (wpa_s->drv_flags & - WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && - wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < - 0) - exitcode = -1; -#endif /* CONFIG_P2P */ } if (exitcode == 0) diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c new file mode 100644 index 00000000..7a4f3de0 --- /dev/null +++ b/wpa_supplicant/mesh.c @@ -0,0 +1,540 @@ +/* + * WPA Supplicant - Basic mesh mode routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "ap/sta_info.h" +#include "ap/hostapd.h" +#include "ap/ieee802_11.h" +#include "config_ssid.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "notify.h" +#include "ap.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" +#include "mesh.h" + + +static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s) +{ + wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); + wpa_s->ifmsh = NULL; + wpa_s->current_ssid = NULL; + os_free(wpa_s->mesh_rsn); + wpa_s->mesh_rsn = NULL; + /* TODO: leave mesh (stop beacon). This will happen on link down + * anyway, so it's not urgent */ +} + + +void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, + struct hostapd_iface *ifmsh) +{ + if (!ifmsh) + return; + + if (ifmsh->mconf) { + mesh_mpm_deinit(wpa_s, ifmsh); + if (ifmsh->mconf->ies) { + ifmsh->mconf->ies = NULL; + /* We cannot free this struct + * because wpa_authenticator on + * hostapd side is also using it + * for now just set to NULL and + * let hostapd code free it. + */ + } + os_free(ifmsh->mconf); + ifmsh->mconf = NULL; + } + + /* take care of shared data */ + hostapd_interface_deinit(ifmsh); + hostapd_interface_free(ifmsh); +} + + +static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) +{ + struct mesh_conf *conf; + + conf = os_zalloc(sizeof(struct mesh_conf)); + if (!conf) + return NULL; + + os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len); + conf->meshid_len = ssid->ssid_len; + + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) + conf->security |= MESH_CONF_SEC_AUTH | + MESH_CONF_SEC_AMPE; + else + conf->security |= MESH_CONF_SEC_NONE; + + /* defaults */ + conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; + conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME; + conf->mesh_cc_id = 0; + conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET; + conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0; + conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries; + conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout; + conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout; + conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout; + + return conf; +} + + +static void wpas_mesh_copy_groups(struct hostapd_data *bss, + struct wpa_supplicant *wpa_s) +{ + int num_groups; + size_t groups_size; + + for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0; + num_groups++) + ; + + groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]); + bss->conf->sae_groups = os_malloc(groups_size); + if (bss->conf->sae_groups) + os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups, + groups_size); +} + + +static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct hostapd_iface *ifmsh; + struct hostapd_data *bss; + struct hostapd_config *conf; + struct mesh_conf *mconf; + int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; + static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; + size_t len; + int rate_len; + + if (!wpa_s->conf->user_mpm) { + /* not much for us to do here */ + wpa_msg(wpa_s, MSG_WARNING, + "user_mpm is not enabled in configuration"); + return 0; + } + + wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh)); + if (!ifmsh) + return -ENOMEM; + + ifmsh->drv_flags = wpa_s->drv_flags; + ifmsh->num_bss = 1; + ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss, + sizeof(struct hostapd_data *)); + if (!ifmsh->bss) + goto out_free; + + ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data)); + if (!bss) + goto out_free; + + os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); + bss->driver = wpa_s->driver; + bss->drv_priv = wpa_s->drv_priv; + bss->iface = ifmsh; + bss->mesh_sta_free_cb = mesh_mpm_free_sta; + wpa_s->assoc_freq = ssid->frequency; + wpa_s->current_ssid = ssid; + + /* setup an AP config for auth processing */ + conf = hostapd_config_defaults(); + if (!conf) + goto out_free; + + bss->conf = *conf->bss; + bss->conf->start_disabled = 1; + bss->conf->mesh = MESH_ENABLED; + bss->iconf = conf; + ifmsh->conf = conf; + + ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; + os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); + + mconf = mesh_config_create(ssid); + if (!mconf) + goto out_free; + ifmsh->mconf = mconf; + + /* need conf->hw_mode for supported rates. */ + if (ssid->frequency == 0) { + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + conf->channel = 1; + } else { + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + } + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", + ssid->frequency); + goto out_free; + } + + if (ssid->mesh_basic_rates == NULL) { + /* + * XXX: Hack! This is so an MPM which correctly sets the ERP + * mandatory rates as BSSBasicRateSet doesn't reject us. We + * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but + * this is way easier. This also makes our BSSBasicRateSet + * advertised in beacons match the one in peering frames, sigh. + */ + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + conf->basic_rates = os_malloc(sizeof(basic_rates_erp)); + if (!conf->basic_rates) + goto out_free; + os_memcpy(conf->basic_rates, basic_rates_erp, + sizeof(basic_rates_erp)); + } + } else { + rate_len = 0; + while (1) { + if (ssid->mesh_basic_rates[rate_len] < 1) + break; + rate_len++; + } + conf->basic_rates = os_calloc(rate_len + 1, sizeof(int)); + if (conf->basic_rates == NULL) + goto out_free; + os_memcpy(conf->basic_rates, ssid->mesh_basic_rates, + rate_len * sizeof(int)); + conf->basic_rates[rate_len] = -1; + } + + if (hostapd_setup_interface(ifmsh)) { + wpa_printf(MSG_ERROR, + "Failed to initialize hostapd interface for mesh"); + return -1; + } + + if (wpa_drv_init_mesh(wpa_s)) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver"); + return -1; + } + + if (mconf->security != MESH_CONF_SEC_NONE) { + if (ssid->passphrase == NULL) { + wpa_printf(MSG_ERROR, + "mesh: Passphrase for SAE not configured"); + goto out_free; + } + + bss->conf->wpa = ssid->proto; + bss->conf->wpa_key_mgmt = ssid->key_mgmt; + + if (wpa_s->conf->sae_groups && + wpa_s->conf->sae_groups[0] > 0) { + wpas_mesh_copy_groups(bss, wpa_s); + } else { + bss->conf->sae_groups = + os_malloc(sizeof(default_groups)); + if (!bss->conf->sae_groups) + goto out_free; + os_memcpy(bss->conf->sae_groups, default_groups, + sizeof(default_groups)); + } + + len = os_strlen(ssid->passphrase); + bss->conf->ssid.wpa_passphrase = + dup_binstr(ssid->passphrase, len); + + wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf); + if (!wpa_s->mesh_rsn) + goto out_free; + } + + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); + + return 0; +out_free: + wpa_supplicant_mesh_deinit(wpa_s); + return -ENOMEM; +} + + +void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *ies, size_t ie_len) +{ + struct ieee802_11_elems elems; + + wpa_msg(wpa_s, MSG_INFO, + "new peer notification for " MACSTR, MAC2STR(addr)); + + if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { + wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR, + MAC2STR(addr)); + return; + } + wpa_mesh_new_mesh_peer(wpa_s, addr, &elems); +} + + +void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie) +{ + /* EID + 0-length (wildcard) mesh-id */ + size_t ielen = 2; + + if (wpabuf_resize(extra_ie, ielen) == 0) { + wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID); + wpabuf_put_u8(*extra_ie, 0); + } +} + + +int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_driver_mesh_join_params params; + int ret = 0; + + if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) { + ret = -ENOENT; + goto out; + } + + wpa_supplicant_mesh_deinit(wpa_s); + + os_memset(¶ms, 0, sizeof(params)); + params.meshid = ssid->ssid; + params.meshid_len = ssid->ssid_len; + params.freq = ssid->frequency; + if (ssid->beacon_int > 0) + params.beacon_int = ssid->beacon_int; + else if (wpa_s->conf->beacon_int > 0) + params.beacon_int = wpa_s->conf->beacon_int; + params.max_peer_links = wpa_s->conf->max_peer_links; +#ifdef CONFIG_IEEE80211N + params.ht_mode = ssid->mesh_ht_mode; +#endif /* CONFIG_IEEE80211N */ + + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; + params.flags |= WPA_DRIVER_MESH_FLAG_AMPE; + wpa_s->conf->user_mpm = 1; + } + + if (wpa_s->conf->user_mpm) { + params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; + params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + } else { + params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; + params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + } + + if (wpa_supplicant_mesh_init(wpa_s, ssid)) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); + ret = -1; + goto out; + } + + if (wpa_s->ifmsh) { + params.ies = wpa_s->ifmsh->mconf->ies; + params.ie_len = wpa_s->ifmsh->mconf->ie_len; + params.basic_rates = wpa_s->ifmsh->basic_rates; + } + + wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ret = wpa_drv_join_mesh(wpa_s, ¶ms); + if (ret) + wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret); + + /* hostapd sets the interface down until we associate */ + wpa_drv_set_operstate(wpa_s, 1); + +out: + return ret; +} + + +int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + + wpa_msg(wpa_s, MSG_INFO, "leaving mesh"); + + /* Need to send peering close messages first */ + wpa_supplicant_mesh_deinit(wpa_s); + + ret = wpa_drv_leave_mesh(wpa_s); + if (ret) + wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret); + + wpa_drv_set_operstate(wpa_s, 1); + + return ret; +} + + +static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) +{ + struct ieee802_11_elems elems; + char *mesh_id, *pos = buf; + u8 *bss_basic_rate_set; + int bss_basic_rate_set_len, ret, i; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) + return -1; + + if (elems.mesh_id_len < 1) + return 0; + + mesh_id = os_malloc(elems.mesh_id_len + 1); + if (mesh_id == NULL) + return -1; + + os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len); + mesh_id[elems.mesh_id_len] = '\0'; + ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id); + os_free(mesh_id); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (elems.mesh_config_len > 6) { + ret = os_snprintf(pos, end - pos, + "active_path_selection_protocol_id=0x%02x\n" + "active_path_selection_metric_id=0x%02x\n" + "congestion_control_mode_id=0x%02x\n" + "synchronization_method_id=0x%02x\n" + "authentication_protocol_id=0x%02x\n" + "mesh_formation_info=0x%02x\n" + "mesh_capability=0x%02x\n", + elems.mesh_config[0], elems.mesh_config[1], + elems.mesh_config[2], elems.mesh_config[3], + elems.mesh_config[4], elems.mesh_config[5], + elems.mesh_config[6]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + bss_basic_rate_set = os_malloc(elems.supp_rates_len + + elems.ext_supp_rates_len); + if (bss_basic_rate_set == NULL) + return -1; + + bss_basic_rate_set_len = 0; + for (i = 0; i < elems.supp_rates_len; i++) { + if (elems.supp_rates[i] & 0x80) { + bss_basic_rate_set[bss_basic_rate_set_len++] = + (elems.supp_rates[i] & 0x7f) * 5; + } + } + for (i = 0; i < elems.ext_supp_rates_len; i++) { + if (elems.ext_supp_rates[i] & 0x80) { + bss_basic_rate_set[bss_basic_rate_set_len++] = + (elems.ext_supp_rates[i] & 0x7f) * 5; + } + } + if (bss_basic_rate_set_len > 0) { + ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", + bss_basic_rate_set[0]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (i = 1; i < bss_basic_rate_set_len; i++) { + ret = os_snprintf(pos, end - pos, " %d", + bss_basic_rate_set[i]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + os_free(bss_basic_rate_set); + + return pos - buf; +} + + +int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + return mesh_attr_text(ies, ies_len, buf, end); +} + + +static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + int res; + + res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr, + wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res) || + (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ)) { + /* Try to avoid going over the IFNAMSIZ length limit */ + res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res)) + return -1; + } + wpa_s->mesh_if_idx++; + return 0; +} + + +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + struct wpa_interface iface; + struct wpa_supplicant *mesh_wpa_s; + u8 addr[ETH_ALEN]; + + if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0) + return -1; + + if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr, + NULL) < 0) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new mesh interface"); + return -1; + } + wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr " + MACSTR, ifname, MAC2STR(addr)); + + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = ifname; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + + mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface); + if (!mesh_wpa_s) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new wpa_supplicant interface"); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + return -1; + } + mesh_wpa_s->mesh_if_created = 1; + mesh_wpa_s->parent = wpa_s; + return 0; +} diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h new file mode 100644 index 00000000..3cb7f1b1 --- /dev/null +++ b/wpa_supplicant/mesh.h @@ -0,0 +1,44 @@ +/* + * WPA Supplicant - Basic mesh mode routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_H +#define MESH_H + +int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s); +void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, + struct hostapd_iface *ifmsh); +int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end); +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len); + +#ifdef CONFIG_MESH + +void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *ies, size_t ie_len); +void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie); + +#else /* CONFIG_MESH */ + +static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, + const u8 *addr, + const u8 *ies, size_t ie_len) +{ +} + +static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie) +{ +} + +#endif /* CONFIG_MESH */ + +#endif /* MESH_H */ diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c new file mode 100644 index 00000000..e7c53eac --- /dev/null +++ b/wpa_supplicant/mesh_mpm.c @@ -0,0 +1,1030 @@ +/* + * WPA Supplicant - Basic mesh peer management + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ieee802_11.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" + +struct mesh_peer_mgmt_ie { + const u8 *proto_id; + const u8 *llid; + const u8 *plid; + const u8 *reason; + const u8 *pmk; +}; + +static void plink_timer(void *eloop_ctx, void *user_data); + + +enum plink_event { + PLINK_UNDEFINED, + OPN_ACPT, + OPN_RJCT, + OPN_IGNR, + CNF_ACPT, + CNF_RJCT, + CNF_IGNR, + CLS_ACPT, + CLS_IGNR +}; + +static const char * const mplstate[] = { + [PLINK_LISTEN] = "LISTEN", + [PLINK_OPEN_SENT] = "OPEN_SENT", + [PLINK_OPEN_RCVD] = "OPEN_RCVD", + [PLINK_CNF_RCVD] = "CNF_RCVD", + [PLINK_ESTAB] = "ESTAB", + [PLINK_HOLDING] = "HOLDING", + [PLINK_BLOCKED] = "BLOCKED" +}; + +static const char * const mplevent[] = { + [PLINK_UNDEFINED] = "UNDEFINED", + [OPN_ACPT] = "OPN_ACPT", + [OPN_RJCT] = "OPN_RJCT", + [OPN_IGNR] = "OPN_IGNR", + [CNF_ACPT] = "CNF_ACPT", + [CNF_RJCT] = "CNF_RJCT", + [CNF_IGNR] = "CNF_IGNR", + [CLS_ACPT] = "CLS_ACPT", + [CLS_IGNR] = "CLS_IGNR" +}; + + +static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, + u8 action_field, + const u8 *ie, size_t len, + struct mesh_peer_mgmt_ie *mpm_ie) +{ + os_memset(mpm_ie, 0, sizeof(*mpm_ie)); + + /* remove optional PMK at end */ + if (len >= 16) { + len -= 16; + mpm_ie->pmk = ie + len - 16; + } + + if ((action_field == PLINK_OPEN && len != 4) || + (action_field == PLINK_CONFIRM && len != 6) || + (action_field == PLINK_CLOSE && len != 6 && len != 8)) { + wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie"); + return -1; + } + + /* required fields */ + if (len < 4) + return -1; + mpm_ie->proto_id = ie; + mpm_ie->llid = ie + 2; + ie += 4; + len -= 4; + + /* close reason is always present at end for close */ + if (action_field == PLINK_CLOSE) { + if (len < 2) + return -1; + mpm_ie->reason = ie + len - 2; + len -= 2; + } + + /* plid, present for confirm, and possibly close */ + if (len) + mpm_ie->plid = ie; + + return 0; +} + + +static int plink_free_count(struct hostapd_data *hapd) +{ + if (hapd->max_plinks > hapd->num_plinks) + return hapd->max_plinks - hapd->num_plinks; + return 0; +} + + +static u16 copy_supp_rates(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + if (!elems->supp_rates) { + wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR, + MAC2STR(sta->addr)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { + wpa_msg(wpa_s, MSG_ERROR, + "Invalid supported rates element length " MACSTR + " %d+%d", MAC2STR(sta->addr), elems->supp_rates_len, + elems->ext_supp_rates_len); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + + return WLAN_STATUS_SUCCESS; +} + + +/* return true if elems from a neighbor match this MBSS */ +static Boolean matches_local(struct wpa_supplicant *wpa_s, + struct ieee802_11_elems *elems) +{ + struct mesh_conf *mconf = wpa_s->ifmsh->mconf; + + if (elems->mesh_config_len < 5) + return FALSE; + + return (mconf->meshid_len == elems->mesh_id_len && + os_memcmp(mconf->meshid, elems->mesh_id, + elems->mesh_id_len) == 0 && + mconf->mesh_pp_id == elems->mesh_config[0] && + mconf->mesh_pm_id == elems->mesh_config[1] && + mconf->mesh_cc_id == elems->mesh_config[2] && + mconf->mesh_sp_id == elems->mesh_config[3] && + mconf->mesh_auth_id == elems->mesh_config[4]); +} + + +/* check if local link id is already used with another peer */ +static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid) +{ + struct sta_info *sta; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->my_lid == llid) + return TRUE; + } + + return FALSE; +} + + +/* generate an llid for a link and set to initial state */ +static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + u16 llid; + + do { + if (os_get_random((u8 *) &llid, sizeof(llid)) < 0) + continue; + } while (!llid || llid_in_use(wpa_s, llid)); + + sta->my_lid = llid; + sta->peer_lid = 0; + sta->plink_state = PLINK_LISTEN; +} + + +static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum plink_action_field type, + u16 close_reason) +{ + struct wpabuf *buf; + struct hostapd_iface *ifmsh = wpa_s->ifmsh; + struct hostapd_data *bss = ifmsh->bss[0]; + struct mesh_conf *conf = ifmsh->mconf; + u8 supp_rates[2 + 2 + 32]; +#ifdef CONFIG_IEEE80211N + u8 ht_capa_oper[2 + 26 + 2 + 22]; +#endif /* CONFIG_IEEE80211N */ + u8 *pos, *cat; + u8 ie_len, add_plid = 0; + int ret; + int ampe = conf->security & MESH_CONF_SEC_AMPE; + size_t buf_len; + + if (!sta) + return; + + buf_len = 2 + /* capability info */ + 2 + /* AID */ + 2 + 8 + /* supported rates */ + 2 + (32 - 8) + + 2 + 32 + /* mesh ID */ + 2 + 7 + /* mesh config */ + 2 + 23 + /* peering management */ + 2 + 96 + /* AMPE */ + 2 + 16; /* MIC */ +#ifdef CONFIG_IEEE80211N + if (type != PLINK_CLOSE && + wpa_s->current_ssid->mesh_ht_mode > CHAN_NO_HT) { + buf_len += 2 + 26 + /* HT capabilities */ + 2 + 22; /* HT operation */ + } +#endif /* CONFIG_IEEE80211N */ + buf = wpabuf_alloc(buf_len); + if (!buf) + return; + + cat = wpabuf_mhead_u8(buf); + wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED); + wpabuf_put_u8(buf, type); + + if (type != PLINK_CLOSE) { + u8 info; + + /* capability info */ + wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0); + + /* aid */ + if (type == PLINK_CONFIRM) + wpabuf_put_le16(buf, sta->peer_lid); + + /* IE: supp + ext. supp rates */ + pos = hostapd_eid_supp_rates(bss, supp_rates); + pos = hostapd_eid_ext_supp_rates(bss, pos); + wpabuf_put_data(buf, supp_rates, pos - supp_rates); + + /* IE: Mesh ID */ + wpabuf_put_u8(buf, WLAN_EID_MESH_ID); + wpabuf_put_u8(buf, conf->meshid_len); + wpabuf_put_data(buf, conf->meshid, conf->meshid_len); + + /* IE: mesh conf */ + wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG); + wpabuf_put_u8(buf, 7); + wpabuf_put_u8(buf, conf->mesh_pp_id); + wpabuf_put_u8(buf, conf->mesh_pm_id); + wpabuf_put_u8(buf, conf->mesh_cc_id); + wpabuf_put_u8(buf, conf->mesh_sp_id); + wpabuf_put_u8(buf, conf->mesh_auth_id); + info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1; + /* TODO: Add Connected to Mesh Gate/AS subfields */ + wpabuf_put_u8(buf, info); + /* always forwarding & accepting plinks for now */ + wpabuf_put_u8(buf, 0x1 | 0x8); + } else { /* Peer closing frame */ + /* IE: Mesh ID */ + wpabuf_put_u8(buf, WLAN_EID_MESH_ID); + wpabuf_put_u8(buf, conf->meshid_len); + wpabuf_put_data(buf, conf->meshid, conf->meshid_len); + } + + /* IE: Mesh Peering Management element */ + ie_len = 4; + if (ampe) + ie_len += PMKID_LEN; + switch (type) { + case PLINK_OPEN: + break; + case PLINK_CONFIRM: + ie_len += 2; + add_plid = 1; + break; + case PLINK_CLOSE: + ie_len += 2; + add_plid = 1; + ie_len += 2; /* reason code */ + break; + } + + wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT); + wpabuf_put_u8(buf, ie_len); + /* peering protocol */ + if (ampe) + wpabuf_put_le16(buf, 1); + else + wpabuf_put_le16(buf, 0); + wpabuf_put_le16(buf, sta->my_lid); + if (add_plid) + wpabuf_put_le16(buf, sta->peer_lid); + if (type == PLINK_CLOSE) + wpabuf_put_le16(buf, close_reason); + if (ampe) { + if (sta->sae == NULL) { + wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session"); + goto fail; + } + mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta, + wpabuf_put(buf, PMKID_LEN)); + } + +#ifdef CONFIG_IEEE80211N + if (type != PLINK_CLOSE && + wpa_s->current_ssid->mesh_ht_mode > CHAN_NO_HT) { + pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); + pos = hostapd_eid_ht_operation(bss, pos); + wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); + } +#endif /* CONFIG_IEEE80211N */ + + if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { + wpa_msg(wpa_s, MSG_INFO, + "Mesh MPM: failed to add AMPE and MIC IE"); + goto fail; + } + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, + sta->addr, wpa_s->own_addr, wpa_s->own_addr, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret < 0) + wpa_msg(wpa_s, MSG_INFO, + "Mesh MPM: failed to send peering frame"); + +fail: + wpabuf_free(buf); +} + + +/* configure peering state in ours and driver's station entry */ +static void +wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, struct sta_info *sta, + enum mesh_plink_state state) +{ + struct hostapd_sta_add_params params; + int ret; + + sta->plink_state = state; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = sta->addr; + params.plink_state = state; + params.set = 1; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", + MAC2STR(sta->addr), mplstate[state]); + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR + ": %d", MAC2STR(sta->addr), ret); + } +} + + +static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + + if (sta->mpm_close_reason == WLAN_REASON_MESH_CLOSE_RCVD) { + ap_free_sta(hapd, sta); + return; + } + + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_LISTEN); + sta->my_lid = sta->peer_lid = sta->mpm_close_reason = 0; + sta->mpm_retries = 0; +} + + +static void plink_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct sta_info *sta = user_data; + u16 reason = 0; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + + switch (sta->plink_state) { + case PLINK_OPEN_RCVD: + case PLINK_OPEN_SENT: + /* retry timer */ + if (sta->mpm_retries < conf->dot11MeshMaxRetries) { + eloop_register_timeout( + conf->dot11MeshRetryTimeout / 1000, + (conf->dot11MeshRetryTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); + sta->mpm_retries++; + break; + } + reason = WLAN_REASON_MESH_MAX_RETRIES; + /* fall through on else */ + + case PLINK_CNF_RCVD: + /* confirm timer */ + if (!reason) + reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; + sta->plink_state = PLINK_HOLDING; + eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); + break; + case PLINK_HOLDING: + /* holding timer */ + mesh_mpm_fsm_restart(wpa_s, sta); + break; + default: + break; + } +} + + +/* initiate peering with station */ +static void +mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, + enum mesh_plink_state next_state) +{ + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000, + (conf->dot11MeshRetryTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); + wpa_mesh_set_plink_state(wpa_s, sta, next_state); +} + + +int mesh_mpm_plink_close(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + int reason = WLAN_REASON_MESH_PEERING_CANCELLED; + + if (sta) { + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); + wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(plink_timer, wpa_s, sta); + return 0; + } + + return 1; +} + + +void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) +{ + struct hostapd_data *hapd = ifmsh->bss[0]; + + /* notify peers we're leaving */ + ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s); + + hapd->num_plinks = 0; + hostapd_free_stas(hapd); +} + + +/* for mesh_rsn to indicate this peer has completed authentication, and we're + * ready to start AMPE */ +void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct hostapd_sta_add_params params; + struct sta_info *sta; + int ret; + + sta = ap_get_sta(data, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer"); + return; + } + + /* TODO: Should do nothing if this STA is already authenticated, but + * the AP code already sets this flag. */ + sta->flags |= WLAN_STA_AUTH; + + mesh_rsn_init_ampe_sta(wpa_s, sta); + + os_memset(¶ms, 0, sizeof(params)); + params.addr = sta->addr; + params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED; + params.set = 1; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR, + MAC2STR(sta->addr)); + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, + "Driver failed to set " MACSTR ": %d", + MAC2STR(sta->addr), ret); + } + + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); + + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); +} + + +void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + struct ieee802_11_elems *elems) +{ + struct hostapd_sta_add_params params; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct sta_info *sta; + struct wpa_ssid *ssid = wpa_s->current_ssid; + int ret = 0; + + sta = ap_get_sta(data, addr); + if (!sta) { + sta = ap_sta_add(data, addr); + if (!sta) + return; + } + + /* initialize sta */ + if (copy_supp_rates(wpa_s, sta, elems)) + return; + + mesh_mpm_init_link(wpa_s, sta); + +#ifdef CONFIG_IEEE80211N + copy_sta_ht_capab(data, sta, elems->ht_capabilities, + elems->ht_capabilities_len); + update_ht_state(data, sta); +#endif /* CONFIG_IEEE80211N */ + + /* insert into driver */ + os_memset(¶ms, 0, sizeof(params)); + params.supp_rates = sta->supported_rates; + params.supp_rates_len = sta->supported_rates_len; + params.addr = addr; + params.plink_state = sta->plink_state; + params.aid = sta->peer_lid; + params.listen_interval = 100; + params.ht_capabilities = sta->ht_capabilities; + params.flags |= WPA_STA_WMM; + params.flags_mask |= WPA_STA_AUTHENTICATED; + if (conf->security == MESH_CONF_SEC_NONE) { + params.flags |= WPA_STA_AUTHORIZED; + params.flags |= WPA_STA_AUTHENTICATED; + } else { + sta->flags |= WLAN_STA_MFP; + params.flags |= WPA_STA_MFP; + } + + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, + "Driver failed to insert " MACSTR ": %d", + MAC2STR(addr), ret); + return; + } + + if (ssid && ssid->no_auto_peer) { + wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " + MACSTR " because of no_auto_peer", MAC2STR(addr)); + if (data->mesh_pending_auth) { + struct os_reltime age; + const struct ieee80211_mgmt *mgmt; + struct hostapd_frame_info fi; + + mgmt = wpabuf_head(data->mesh_pending_auth); + os_reltime_age(&data->mesh_pending_auth_time, &age); + if (age.sec < 2 && + os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "mesh: Process pending Authentication frame from %u.%06u seconds ago", + (unsigned int) age.sec, + (unsigned int) age.usec); + os_memset(&fi, 0, sizeof(fi)); + ieee802_11_mgmt( + data, + wpabuf_head(data->mesh_pending_auth), + wpabuf_len(data->mesh_pending_auth), + &fi); + } + wpabuf_free(data->mesh_pending_auth); + data->mesh_pending_auth = NULL; + } + return; + } + + if (conf->security == MESH_CONF_SEC_NONE) + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); + else + mesh_rsn_auth_sae_sta(wpa_s, sta); +} + + +void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt) +{ + struct hostapd_frame_info fi; + + os_memset(&fi, 0, sizeof(fi)); + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame, + rx_mgmt->frame_len, &fi); +} + + +static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + u8 seq[6] = {}; + + wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established", + MAC2STR(sta->addr)); + + if (conf->security & MESH_CONF_SEC_AMPE) { + wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, + seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); + wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, + seq, sizeof(seq), + sta->mgtk, sizeof(sta->mgtk)); + wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, + seq, sizeof(seq), + sta->mgtk, sizeof(sta->mgtk)); + + wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); + wpa_hexdump_key(MSG_DEBUG, "mgtk:", + sta->mgtk, sizeof(sta->mgtk)); + } + + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); + hapd->num_plinks++; + + sta->flags |= WLAN_STA_ASSOC; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + + /* Send ctrl event */ + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, + MAC2STR(sta->addr)); +} + + +static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, + enum plink_event event) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + u16 reason = 0; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", + MAC2STR(sta->addr), mplstate[sta->plink_state], + mplevent[event]); + + switch (sta->plink_state) { + case PLINK_LISTEN: + switch (event) { + case CLS_ACPT: + mesh_mpm_fsm_restart(wpa_s, sta); + break; + case OPN_ACPT: + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, + 0); + break; + default: + break; + } + break; + case PLINK_OPEN_SENT: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + /* retry timer is left untouched */ + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + case CNF_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); + eloop_register_timeout( + conf->dot11MeshConfirmTimeout / 1000, + (conf->dot11MeshConfirmTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + break; + default: + break; + } + break; + case PLINK_OPEN_RCVD: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + case CNF_ACPT: + if (conf->security & MESH_CONF_SEC_AMPE) + mesh_rsn_derive_mtk(wpa_s, sta); + mesh_mpm_plink_estab(wpa_s, sta); + break; + default: + break; + } + break; + case PLINK_CNF_RCVD: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_plink_estab(wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + default: + break; + } + break; + case PLINK_ESTAB: + switch (event) { + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + reason = WLAN_REASON_MESH_CLOSE_RCVD; + + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + + wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR + " closed with reason %d", + MAC2STR(sta->addr), reason); + + wpa_msg_ctrl(wpa_s, MSG_INFO, + MESH_PEER_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + + hapd->num_plinks--; + + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + default: + break; + } + break; + case PLINK_HOLDING: + switch (event) { + case CLS_ACPT: + mesh_mpm_fsm_restart(wpa_s, sta); + break; + case OPN_ACPT: + case CNF_ACPT: + case OPN_RJCT: + case CNF_RJCT: + reason = sta->mpm_close_reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + default: + break; + } + break; + default: + wpa_msg(wpa_s, MSG_DEBUG, + "Unsupported MPM event %s for state %s", + mplevent[event], mplstate[sta->plink_state]); + break; + } +} + + +void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 action_field; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *mconf = wpa_s->ifmsh->mconf; + struct sta_info *sta; + u16 plid = 0, llid = 0; + enum plink_event event; + struct ieee802_11_elems elems; + struct mesh_peer_mgmt_ie peer_mgmt_ie; + const u8 *ies; + size_t ie_len; + int ret; + + if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) + return; + + action_field = mgmt->u.action.u.slf_prot_action.action; + if (action_field != PLINK_OPEN && + action_field != PLINK_CONFIRM && + action_field != PLINK_CLOSE) + return; + + ies = mgmt->u.action.u.slf_prot_action.variable; + ie_len = (const u8 *) mgmt + len - + mgmt->u.action.u.slf_prot_action.variable; + + /* at least expect mesh id and peering mgmt */ + if (ie_len < 2 + 2) { + wpa_printf(MSG_DEBUG, + "MPM: Ignore too short action frame %u ie_len %u", + action_field, (unsigned int) ie_len); + return; + } + wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field); + + if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) { + wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x", + WPA_GET_LE16(ies)); + ies += 2; /* capability */ + ie_len -= 2; + } + if (action_field == PLINK_CONFIRM) { + wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); + ies += 2; /* aid */ + ie_len -= 2; + } + + /* check for mesh peering, mesh id and mesh config IEs */ + if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs"); + return; + } + if (!elems.peer_mgmt) { + wpa_printf(MSG_DEBUG, + "MPM: No Mesh Peering Management element"); + return; + } + if (action_field != PLINK_CLOSE) { + if (!elems.mesh_id || !elems.mesh_config) { + wpa_printf(MSG_DEBUG, + "MPM: No Mesh ID or Mesh Configuration element"); + return; + } + + if (!matches_local(wpa_s, &elems)) { + wpa_printf(MSG_DEBUG, + "MPM: Mesh ID or Mesh Configuration element do not match local MBSS"); + return; + } + } + + ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field, + elems.peer_mgmt, + elems.peer_mgmt_len, + &peer_mgmt_ie); + if (ret) { + wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame"); + return; + } + + /* the sender's llid is our plid and vice-versa */ + plid = WPA_GET_LE16(peer_mgmt_ie.llid); + if (peer_mgmt_ie.plid) + llid = WPA_GET_LE16(peer_mgmt_ie.plid); + wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); + + sta = ap_get_sta(hapd, mgmt->sa); + if (!sta) { + wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer"); + return; + } + +#ifdef CONFIG_SAE + /* peer is in sae_accepted? */ + if (sta->sae && sta->sae->state != SAE_ACCEPTED) { + wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer"); + return; + } +#endif /* CONFIG_SAE */ + + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); + + if ((mconf->security & MESH_CONF_SEC_AMPE) && + mesh_rsn_process_ampe(wpa_s, sta, &elems, + &mgmt->u.action.category, + ies, ie_len)) { + wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); + return; + } + + if (sta->plink_state == PLINK_BLOCKED) { + wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED"); + return; + } + + /* Now we will figure out the appropriate event... */ + switch (action_field) { + case PLINK_OPEN: + if (plink_free_count(hapd) == 0) { + event = OPN_IGNR; + wpa_printf(MSG_INFO, + "MPM: Peer link num over quota(%d)", + hapd->max_plinks); + } else if (sta->peer_lid && sta->peer_lid != plid) { + event = OPN_IGNR; + } else { + sta->peer_lid = plid; + event = OPN_ACPT; + } + break; + case PLINK_CONFIRM: + if (plink_free_count(hapd) == 0) { + event = CNF_IGNR; + wpa_printf(MSG_INFO, + "MPM: Peer link num over quota(%d)", + hapd->max_plinks); + } else if (sta->my_lid != llid || + (sta->peer_lid && sta->peer_lid != plid)) { + event = CNF_IGNR; + } else { + if (!sta->peer_lid) + sta->peer_lid = plid; + event = CNF_ACPT; + } + break; + case PLINK_CLOSE: + if (sta->plink_state == PLINK_ESTAB) + /* Do not check for llid or plid. This does not + * follow the standard but since multiple plinks + * per cand are not supported, it is necessary in + * order to avoid a livelock when MP A sees an + * establish peer link to MP B but MP B does not + * see it. This can be caused by a timeout in + * B's peer link establishment or B being + * restarted. + */ + event = CLS_ACPT; + else if (sta->peer_lid != plid) + event = CLS_IGNR; + else if (peer_mgmt_ie.plid && sta->my_lid != llid) + event = CLS_IGNR; + else + event = CLS_ACPT; + break; + default: + /* + * This cannot be hit due to the action_field check above, but + * compilers may not be able to figure that out and can warn + * about uninitialized event below. + */ + return; + } + mesh_mpm_fsm(wpa_s, sta, event); +} + + +/* called by ap_free_sta */ +void mesh_mpm_free_sta(struct sta_info *sta) +{ + eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); + eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); +} diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h new file mode 100644 index 00000000..2f7f6a78 --- /dev/null +++ b/wpa_supplicant/mesh_mpm.h @@ -0,0 +1,40 @@ +/* + * WPA Supplicant - Basic mesh peer management + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_MPM_H +#define MESH_MPM_H + +/* notify MPM of new mesh peer to be inserted in MPM and driver */ +void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + struct ieee802_11_elems *elems); +void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); +void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr); +void mesh_mpm_free_sta(struct sta_info *sta); + +#ifdef CONFIG_MESH + +void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); +void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt); + +#else /* CONFIG_MESH */ + +static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ +} + +static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, + struct rx_mgmt *rx_mgmt) +{ +} + +#endif /* CONFIG_MESH */ + +#endif /* MESH_MPM_H */ diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c new file mode 100644 index 00000000..e6ae7c38 --- /dev/null +++ b/wpa_supplicant/mesh_rsn.c @@ -0,0 +1,615 @@ +/* + * WPA Supplicant - Mesh RSN routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "rsn_supp/wpa.h" +#include "ap/hostapd.h" +#include "ap/wpa_auth.h" +#include "ap/sta_info.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "wpas_glue.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" + +#define MESH_AUTH_TIMEOUT 10 +#define MESH_AUTH_RETRY 3 + +void mesh_auth_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct sta_info *sta = user_data; + + if (sta->sae->state != SAE_ACCEPTED) { + wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR + " (attempt %d) ", + MAC2STR(sta->addr), sta->sae_auth_retry); + if (sta->sae_auth_retry < MESH_AUTH_RETRY) { + mesh_rsn_auth_sae_sta(wpa_s, sta); + } else { + /* block the STA if exceeded the number of attempts */ + sta->plink_state = PLINK_BLOCKED; + sta->sae->state = SAE_NOTHING; + } + sta->sae_auth_retry++; + } +} + + +static void auth_logger(void *ctx, const u8 *addr, logger_level level, + const char *txt) +{ + if (addr) + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "AUTH: %s", txt); +} + + +static const u8 *auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) +{ + struct mesh_rsn *mesh_rsn = ctx; + struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; + struct sta_info *sta = ap_get_sta(hapd, addr); + + wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", + __func__, MAC2STR(addr), prev_psk); + + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } + + return NULL; +} + + +static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len) +{ + struct mesh_rsn *mesh_rsn = ctx; + u8 seq[6]; + + os_memset(seq, 0, sizeof(seq)); + + if (addr) { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR + " key_idx=%d)", + __func__, alg, MAC2STR(addr), idx); + } else { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", + __func__, alg, idx); + } + wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); + + return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx, + 1, seq, 6, key, key_len); +} + + +static int auth_start_ampe(void *ctx, const u8 *addr) +{ + struct mesh_rsn *mesh_rsn = ctx; + struct hostapd_data *hapd; + struct sta_info *sta; + + if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH) + return -1; + + hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; + sta = ap_get_sta(hapd, addr); + if (sta) + eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta); + + mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr); + return 0; +} + + +static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) +{ + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + u8 seq[6] = {}; + + wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); + + os_memset(&conf, 0, sizeof(conf)); + conf.wpa = 2; + conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; + conf.wpa_pairwise = WPA_CIPHER_CCMP; + conf.rsn_pairwise = WPA_CIPHER_CCMP; + conf.wpa_group = WPA_CIPHER_CCMP; + conf.eapol_version = 0; + conf.wpa_group_rekey = -1; + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = rsn; + cb.logger = auth_logger; + cb.get_psk = auth_get_psk; + cb.set_key = auth_set_key; + cb.start_ampe = auth_start_ampe; + + rsn->auth = wpa_init(addr, &conf, &cb); + if (rsn->auth == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); + return -1; + } + + /* TODO: support rekeying */ + if (random_get_bytes(rsn->mgtk, 16) < 0) { + wpa_deinit(rsn->auth); + return -1; + } + + /* group mgmt */ + wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1, + seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + + /* group privacy / data frames */ + wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1, + seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + + return 0; +} + + +static void mesh_rsn_deinit(struct mesh_rsn *rsn) +{ + os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); + wpa_deinit(rsn->auth); +} + + +struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, + struct mesh_conf *conf) +{ + struct mesh_rsn *mesh_rsn; + struct hostapd_data *bss = wpa_s->ifmsh->bss[0]; + const u8 *ie; + size_t ie_len; + + mesh_rsn = os_zalloc(sizeof(*mesh_rsn)); + if (mesh_rsn == NULL) + return NULL; + mesh_rsn->wpa_s = wpa_s; + + if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { + mesh_rsn_deinit(mesh_rsn); + return NULL; + } + + bss->wpa_auth = mesh_rsn->auth; + + ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); + conf->ies = (u8 *) ie; + conf->ie_len = ie_len; + + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + + return mesh_rsn; +} + + +static int index_within_array(const int *array, int idx) +{ + int i; + + for (i = 0; i < idx; i++) { + if (array[i] == -1) + return 0; + } + + return 1; +} + + +static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s, + struct sae_data *sae) +{ + int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups; + + /* Configuration may have changed, so validate current index */ + if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index)) + return -1; + + for (;;) { + int group = groups[wpa_s->mesh_rsn->sae_group_index]; + + if (group <= 0) + break; + if (sae_set_group(sae, group) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", + sae->group); + return 0; + } + wpa_s->mesh_rsn->sae_group_index++; + } + + return -1; +} + + +struct wpabuf * +mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct sta_info *sta) +{ + struct wpabuf *buf; + int len; + + if (ssid->passphrase == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) { + wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group"); + return NULL; + } + + if (sae_prepare_commit(wpa_s->own_addr, sta->addr, + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase), sta->sae) < 0) { + wpa_msg(wpa_s, MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + + len = wpa_s->mesh_rsn->sae_token ? + wpabuf_len(wpa_s->mesh_rsn->sae_token) : 0; + buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); + if (buf == NULL) + return NULL; + + sae_write_commit(sta->sae, buf, wpa_s->mesh_rsn->sae_token); + + return buf; +} + + +static void mesh_rsn_send_auth(struct wpa_supplicant *wpa_s, + const u8 *dst, const u8 *src, + u16 auth_transaction, u16 resp, + struct wpabuf *data) +{ + struct ieee80211_mgmt *auth; + u8 *buf; + size_t len, ielen = 0; + + if (data) + ielen = wpabuf_len(data); + len = IEEE80211_HDRLEN + sizeof(auth->u.auth) + ielen; + buf = os_zalloc(len); + if (buf == NULL) + return; + + auth = (struct ieee80211_mgmt *) buf; + auth->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(auth->da, dst, ETH_ALEN); + os_memcpy(auth->sa, src, ETH_ALEN); + os_memcpy(auth->bssid, src, ETH_ALEN); + + auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE); + auth->u.auth.auth_transaction = host_to_le16(auth_transaction); + auth->u.auth.status_code = host_to_le16(resp); + + if (data) + os_memcpy(auth->u.auth.variable, wpabuf_head(data), ielen); + + wpa_msg(wpa_s, MSG_DEBUG, "authentication frame: STA=" MACSTR + " auth_transaction=%d resp=%d (IE len=%lu)", + MAC2STR(dst), auth_transaction, resp, (unsigned long) ielen); + if (wpa_drv_send_mlme(wpa_s, buf, len, 0) < 0) + wpa_printf(MSG_INFO, "send_auth_reply: send_mlme failed: %s", + strerror(errno)); + + os_free(buf); +} + + +/* initiate new SAE authentication with sta */ +int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpabuf *buf; + unsigned int rnd; + + if (!ssid) { + wpa_msg(wpa_s, MSG_DEBUG, + "AUTH: No current_ssid known to initiate new SAE"); + return -1; + } + + if (!sta->sae) { + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return -1; + } + + buf = mesh_rsn_build_sae_commit(wpa_s, ssid, sta); + if (!buf) + return -1; + + wpa_msg(wpa_s, MSG_DEBUG, + "AUTH: started authentication with SAE peer: " MACSTR, + MAC2STR(sta->addr)); + + sta->sae->state = SAE_COMMITTED; + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + + mesh_rsn_send_auth(wpa_s, sta->addr, wpa_s->own_addr, + 1, WLAN_STATUS_SUCCESS, buf); + + rnd = rand() % MESH_AUTH_TIMEOUT; + eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer, + wpa_s, sta); + wpabuf_free(buf); + + return 0; +} + + +void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) +{ + /* don't expect wpa auth to cache the pmkid for now */ + rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, + sta->addr, pmkid, + wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); +} + + +static void +mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) +{ + u8 *myaddr = rsn->wpa_s->own_addr; + u8 *peer = sta->addr; + u8 *addr1 = peer, *addr2 = myaddr; + u8 context[AES_BLOCK_SIZE]; + + /* SAE */ + RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + + if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { + addr1 = myaddr; + addr2 = peer; + } + os_memcpy(context + 4, addr1, ETH_ALEN); + os_memcpy(context + 10, addr2, ETH_ALEN); + + sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", + context, sizeof(context), sta->aek, sizeof(sta->aek)); +} + + +/* derive mesh temporal key from pmk */ +int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) +{ + u8 *ptr; + u8 *min, *max; + u16 min_lid, max_lid; + size_t nonce_len = sizeof(sta->my_nonce); + size_t lid_len = sizeof(sta->my_lid); + u8 *myaddr = wpa_s->own_addr; + u8 *peer = sta->addr; + /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */ + u8 context[64 + 4 + 4 + 12]; + + ptr = context; + if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) { + min = sta->my_nonce; + max = sta->peer_nonce; + } else { + min = sta->peer_nonce; + max = sta->my_nonce; + } + os_memcpy(ptr, min, nonce_len); + os_memcpy(ptr + nonce_len, max, nonce_len); + ptr += 2 * nonce_len; + + if (sta->my_lid < sta->peer_lid) { + min_lid = host_to_le16(sta->my_lid); + max_lid = host_to_le16(sta->peer_lid); + } else { + min_lid = host_to_le16(sta->peer_lid); + max_lid = host_to_le16(sta->my_lid); + } + os_memcpy(ptr, &min_lid, lid_len); + os_memcpy(ptr + lid_len, &max_lid, lid_len); + ptr += 2 * lid_len; + + /* SAE */ + RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + ptr += 4; + + if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { + min = myaddr; + max = peer; + } else { + min = peer; + max = myaddr; + } + os_memcpy(ptr, min, ETH_ALEN); + os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN); + + sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), + "Temporal Key Derivation", context, sizeof(context), + sta->mtk, sizeof(sta->mtk)); + return 0; +} + + +void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) +{ + if (random_get_bytes(sta->my_nonce, 32) < 0) { + wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); + /* TODO: How to handle this more cleanly? */ + } + os_memset(sta->peer_nonce, 0, 32); + mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); +} + + +/* insert AMPE and encrypted MIC at @ie. + * @mesh_rsn: mesh RSN context + * @sta: STA we're sending to + * @cat: pointer to category code in frame header. + * @buf: wpabuf to add encrypted AMPE and MIC to. + * */ +int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, + const u8 *cat, struct wpabuf *buf) +{ + struct ieee80211_ampe_ie *ampe; + u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); + u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload; + const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; + const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; + int ret = 0; + + if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) { + wpa_printf(MSG_ERROR, "protect frame: buffer too small"); + return -EINVAL; + } + + ampe_ie = os_zalloc(2 + sizeof(*ampe)); + if (!ampe_ie) { + wpa_printf(MSG_ERROR, "protect frame: out of memory"); + return -ENOMEM; + } + + mic_ie = os_zalloc(2 + AES_BLOCK_SIZE); + if (!mic_ie) { + wpa_printf(MSG_ERROR, "protect frame: out of memory"); + ret = -ENOMEM; + goto free; + } + + /* IE: AMPE */ + ampe_ie[0] = WLAN_EID_AMPE; + ampe_ie[1] = sizeof(*ampe); + ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); + + RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, + wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP)); + os_memcpy(ampe->local_nonce, sta->my_nonce, 32); + os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32); + /* incomplete: see 13.5.4 */ + /* TODO: static mgtk for now since we don't support rekeying! */ + os_memcpy(ampe->mgtk, rsn->mgtk, 16); + /* TODO: Populate Key RSC */ + /* expire in 13 decades or so */ + os_memset(ampe->key_expiration, 0xff, 4); + + /* IE: MIC */ + mic_ie[0] = WLAN_EID_MIC; + mic_ie[1] = AES_BLOCK_SIZE; + wpabuf_put_data(buf, mic_ie, 2); + /* MIC field is output ciphertext */ + + /* encrypt after MIC */ + mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) + + AES_BLOCK_SIZE); + + if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3, + aad, aad_len, mic_payload)) { + wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); + ret = -ENOMEM; + goto free; + } + +free: + os_free(ampe_ie); + os_free(mic_ie); + + return ret; +} + + +int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, + struct ieee802_11_elems *elems, const u8 *cat, + const u8 *start, size_t elems_len) +{ + int ret = 0; + struct ieee80211_ampe_ie *ampe; + u8 null_nonce[32] = {}; + u8 ampe_eid; + u8 ampe_ie_len; + u8 *ampe_buf, *crypt = NULL; + size_t crypt_len; + const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; + const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, + (elems->mic - 2) - cat }; + + if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); + return -1; + } + + ampe_buf = (u8 *) elems->mic + elems->mic_len; + if ((int) elems_len < ampe_buf - start) + return -1; + + crypt_len = elems_len - (elems->mic - start); + if (crypt_len < 2) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); + return -1; + } + + /* crypt is modified by siv_decrypt */ + crypt = os_zalloc(crypt_len); + if (!crypt) { + wpa_printf(MSG_ERROR, "Mesh RSN: out of memory"); + ret = -ENOMEM; + goto free; + } + + os_memcpy(crypt, elems->mic, crypt_len); + + if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, + aad, aad_len, ampe_buf)) { + wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); + ret = -1; + goto free; + } + + ampe_eid = *ampe_buf++; + ampe_ie_len = *ampe_buf++; + + if (ampe_eid != WLAN_EID_AMPE || + ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); + ret = -1; + goto free; + } + + ampe = (struct ieee80211_ampe_ie *) ampe_buf; + if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 && + os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); + ret = -1; + goto free; + } + os_memcpy(sta->peer_nonce, ampe->local_nonce, + sizeof(ampe->local_nonce)); + os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk)); + + /* todo parse mgtk expiration */ +free: + os_free(crypt); + return ret; +} diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h new file mode 100644 index 00000000..b1471b2d --- /dev/null +++ b/wpa_supplicant/mesh_rsn.h @@ -0,0 +1,36 @@ +/* + * WPA Supplicant - Mesh RSN routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_RSN_H +#define MESH_RSN_H + +struct mesh_rsn { + struct wpa_supplicant *wpa_s; + struct wpa_authenticator *auth; + u8 mgtk[16]; +#ifdef CONFIG_SAE + struct wpabuf *sae_token; + int sae_group_index; +#endif /* CONFIG_SAE */ +}; + +struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, + struct mesh_conf *conf); +int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta); +int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta); +void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid); +void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, + struct sta_info *sta); +int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, + const u8 *cat, struct wpabuf *buf); +int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, + struct ieee802_11_elems *elems, const u8 *cat, + const u8 *start, size_t elems_len); +void mesh_auth_timer(void *eloop_ctx, void *user_data); + +#endif /* MESH_RSN_H */ diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 617ce849..df1ce9e0 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -48,6 +48,9 @@ void wpas_notify_supplicant_deinitialized(struct wpa_global *global) int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return 0; + if (wpas_dbus_register_iface(wpa_s)) return -1; @@ -60,6 +63,9 @@ int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* unregister interface in old DBus ctrl iface */ wpas_dbus_unregister_iface(wpa_s); @@ -72,6 +78,9 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_state_change(wpa_s, new_state, old_state); @@ -101,30 +110,45 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON); } void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK); } void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN); } void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS); } void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE); } @@ -132,6 +156,9 @@ void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s) void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_enabled_changed(wpa_s, ssid); } @@ -139,6 +166,9 @@ void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_selected(wpa_s, ssid->id); } @@ -148,12 +178,18 @@ void wpas_notify_network_request(struct wpa_supplicant *wpa_s, enum wpa_ctrl_req_type rtype, const char *default_txt) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt); } void wpas_notify_scanning(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_scanning(wpa_s); @@ -164,12 +200,18 @@ void wpas_notify_scanning(struct wpa_supplicant *wpa_s) void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_scan_done(wpa_s, success); } void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_scan_results(wpa_s); @@ -180,6 +222,9 @@ void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, const struct wps_credential *cred) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS /* notify the old DBus API */ wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); @@ -192,6 +237,9 @@ void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, struct wps_event_m2d *m2d) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_m2d(wpa_s, m2d); #endif /* CONFIG_WPS */ @@ -201,6 +249,9 @@ void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_fail(wpa_s, fail); #endif /* CONFIG_WPS */ @@ -209,6 +260,9 @@ void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_success(wpa_s); #endif /* CONFIG_WPS */ @@ -218,6 +272,9 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) void wpas_notify_network_added(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + /* * Networks objects created during any P2P activities should not be * exposed out. They might/will confuse certain non-P2P aware @@ -250,12 +307,18 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + if (wpa_s->next_ssid == ssid) wpa_s->next_ssid = NULL; if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) wpas_dbus_unregister_network(wpa_s, ssid->id); + if (network_is_persistent_group(ssid)) + wpas_notify_persistent_group_removed(wpa_s, ssid); + wpas_p2p_network_removed(wpa_s, ssid); } @@ -263,6 +326,9 @@ void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[], unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_register_bss(wpa_s, bssid, id); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR, id, MAC2STR(bssid)); @@ -272,6 +338,9 @@ void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[], unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_unregister_bss(wpa_s, bssid, id); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR, id, MAC2STR(bssid)); @@ -281,6 +350,9 @@ void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id); } @@ -288,6 +360,9 @@ void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL, id); } @@ -296,6 +371,9 @@ void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY, id); } @@ -304,6 +382,9 @@ void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id); } @@ -311,6 +392,9 @@ void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id); } @@ -318,6 +402,9 @@ void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id); } @@ -325,6 +412,9 @@ void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id); #endif /* CONFIG_WPS */ @@ -334,6 +424,9 @@ void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id); } @@ -341,24 +434,36 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id); } void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id); } void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_blob_added(wpa_s, name); } void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_blob_removed(wpa_s, name); } @@ -546,7 +651,8 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, * Create 'peer-joined' signal on group object -- will also * check P2P itself. */ - wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr); + if (p2p_dev_addr) + wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr); #endif /* CONFIG_P2P */ /* Notify listeners a new station has been authorized */ @@ -563,7 +669,8 @@ static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, * Create 'peer-disconnected' signal on group object if this * is a P2P group. */ - wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr); + if (p2p_dev_addr) + wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr); #endif /* CONFIG_P2P */ /* Notify listeners a station has been deauthorized */ diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c index 17689c5c..7a863476 100644 --- a/wpa_supplicant/offchannel.c +++ b/wpa_supplicant/offchannel.c @@ -31,8 +31,7 @@ wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) */ iface = wpa_s->global->ifaces; while (iface) { - if (os_memcmp(wpa_s->pending_action_src, - iface->own_addr, ETH_ALEN) == 0) + if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0) break; iface = iface->next; } @@ -85,6 +84,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) wpa_s->off_channel_freq, iface->assoc_freq); if (without_roc && wpa_s->off_channel_freq == 0) { + unsigned int duration = 200; /* * We may get here if wpas_send_action() found us to be * on the correct channel, but remain-on-channel cancel @@ -92,9 +92,18 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) */ wpa_printf(MSG_DEBUG, "Off-channel: Schedule " "remain-on-channel to send Action frame"); +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, + "TESTING: Increase ROC duration %u -> %u", + duration, + duration + wpa_s->extra_roc_dur); + duration += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_drv_remain_on_channel( - wpa_s, wpa_s->pending_action_freq, 200) < - 0) { + wpa_s, wpa_s->pending_action_freq, + duration) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to " "request driver to remain on " "channel (%u MHz) for Action Frame " @@ -190,11 +199,13 @@ void offchannel_send_action_tx_status( data, data_len, result); } +#ifdef CONFIG_P2P if (wpa_s->p2p_long_listen > 0) { /* Continue the listen */ wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); } +#endif /* CONFIG_P2P */ } @@ -262,8 +273,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, struct wpa_supplicant *iface; int ret; - iface = wpas_get_tx_interface(wpa_s, - wpa_s->pending_action_src); + iface = wpas_get_tx_interface(wpa_s, src); wpa_s->action_tx_wait_time = wait_time; ret = wpa_drv_send_action( @@ -315,6 +325,13 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, wait_time = wpa_s->max_remain_on_chan; else if (wait_time == 0) wait_time = 20; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", + wait_time, wait_time + wpa_s->extra_roc_dur); + wait_time += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver " "to remain on channel (%u MHz) for Action " diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 640154c8..42e50141 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -119,7 +119,7 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, int group_added); -static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); +static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); static void wpas_stop_listen(void *ctx); static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); @@ -269,9 +269,11 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) work->ctx = NULL; if (ret) { radio_work_done(work); + p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); return; } + p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; wpa_s->own_scan_requested = 1; @@ -279,6 +281,22 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) } +static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s, + int freq) +{ + if (wpa_s->global->p2p_24ghz_social_channels && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * Search all social channels regardless of whether these have + * been disabled for P2P operating channel use to avoid missing + * peers. + */ + return 1; + } + return p2p_supported_freq(wpa_s->global->p2p, freq); +} + + static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, unsigned int num_req_dev_types, const u8 *req_dev_types, const u8 *dev_id, u16 pw_id) @@ -348,8 +366,8 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, if (params->freqs == NULL) goto fail; for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { - if (p2p_supported_freq(wpa_s->global->p2p, - social_channels_freq[i])) + if (wpas_p2p_search_social_channel( + wpa_s, social_channels_freq[i])) params->freqs[num_channels++] = social_channels_freq[i]; } @@ -363,8 +381,8 @@ static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq, if (params->freqs == NULL) goto fail; for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { - if (p2p_supported_freq(wpa_s->global->p2p, - social_channels_freq[i])) + if (wpas_p2p_search_social_channel( + wpa_s, social_channels_freq[i])) params->freqs[num_channels++] = social_channels_freq[i]; } @@ -426,6 +444,37 @@ static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, } +static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, + "P2P: Complete previously requested removal of %s", + wpa_s->ifname); + wpas_p2p_disconnect(wpa_s); +} + + +static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s, + struct wpa_supplicant *calling_wpa_s) +{ + if (calling_wpa_s == wpa_s && wpa_s && + wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + /* + * The calling wpa_s instance is going to be removed. Do that + * from an eloop callback to keep the instance available until + * the caller has returned. This my be needed, e.g., to provide + * control interface responses on the per-interface socket. + */ + if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect, + wpa_s, NULL) < 0) + return -1; + return 0; + } + + return wpas_p2p_disconnect(wpa_s); +} + + static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, enum p2p_group_removal_reason removal_reason) { @@ -467,8 +516,17 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); - if (os_strcmp(gtype, "client") == 0) + if (os_strcmp(gtype, "client") == 0) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal, + wpa_s, NULL)) { + wpa_printf(MSG_DEBUG, + "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal"); + removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE; + eloop_cancel_timeout(wpas_p2p_psk_failure_removal, + wpa_s, NULL); + } + } if (wpa_s->cross_connect_in_use) { wpa_s->cross_connect_in_use = 0; @@ -536,6 +594,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, global = wpa_s->global; ifname = os_strdup(wpa_s->ifname); type = wpas_p2p_if_type(wpa_s->p2p_group_interface); + eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL); wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); wpa_s = global->ifaces; if (wpa_s && ifname) @@ -553,6 +612,10 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, os_free(wpa_s->go_params); wpa_s->go_params = NULL; + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = NULL; + wpa_s->p2p_group_common_freqs_num = 0; + wpa_s->waiting_presence_resp = 0; wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); @@ -882,6 +945,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, wpa_s->p2p_in_provisioning = 0; } wpa_s->p2p_in_invitation = 0; + wpa_s->group_formation_reported = 1; if (!success) { wpa_msg_global(wpa_s->parent, MSG_INFO, @@ -1169,6 +1233,7 @@ static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *res) { + wpa_s->group_formation_reported = 0; wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR " dev_addr " MACSTR " wps_method %d", MAC2STR(res->peer_interface_addr), @@ -1239,6 +1304,40 @@ static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, } +static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):", + wpa_s->p2p_group_common_freqs_num); + + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) + wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d", + i, wpa_s->p2p_group_common_freqs[i]); +} + + +static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params) +{ + unsigned int i, len = int_array_len(wpa_s->go_params->freq_list); + + wpa_s->p2p_group_common_freqs_num = 0; + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int)); + if (!wpa_s->p2p_group_common_freqs) + return; + + for (i = 0; i < len; i++) { + if (!wpa_s->go_params->freq_list[i]) + break; + wpa_s->p2p_group_common_freqs[i] = + wpa_s->go_params->freq_list[i]; + } + wpa_s->p2p_group_common_freqs_num = i; +} + + static void p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; @@ -1246,6 +1345,9 @@ static void p2p_go_configured(void *ctx, void *data) struct wpa_ssid *ssid; int network_id = -1; + p2p_go_save_group_common_freqs(wpa_s, params); + p2p_go_dump_common_freqs(wpa_s); + ssid = wpa_s->current_ssid; if (ssid && ssid->mode == WPAS_MODE_P2P_GO) { wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning"); @@ -1257,6 +1359,7 @@ static void p2p_go_configured(void *ctx, void *data) params->passphrase, wpa_s->global->p2p_dev_addr, params->persistent_group, ""); + wpa_s->group_formation_reported = 1; os_get_reltime(&wpa_s->global->p2p_go_wait_client); if (params->persistent_group) { @@ -1340,6 +1443,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, } wpa_s->show_group_started = 0; + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->group_formation_reported = 0; wpa_config_set_network_defaults(ssid); ssid->temporary = 1; @@ -1359,6 +1464,15 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_RSN; ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->group_cipher = WPA_CIPHER_CCMP; + if (params->freq > 56160) { + /* + * Enable GCMP instead of CCMP as pairwise_cipher and + * group_cipher in 60 GHz. + */ + ssid->pairwise_cipher = WPA_CIPHER_GCMP; + ssid->group_cipher = WPA_CIPHER_GCMP; + } if (os_strlen(params->passphrase) > 0) { ssid->passphrase = os_strdup(params->passphrase); if (ssid->passphrase == NULL) { @@ -1443,8 +1557,12 @@ static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s, os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx); if (os_strlen(ifname) >= IFNAMSIZ && os_strlen(wpa_s->ifname) < IFNAMSIZ) { + int res; + /* Try to avoid going over the IFNAMSIZ length limit */ - os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx); + res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx); + if (os_snprintf_error(len, res) && len) + ifname[len - 1] = '\0'; } } @@ -1721,7 +1839,7 @@ static void wpas_dev_found(void *ctx, const u8 *addr, wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR " p2p_dev_addr=" MACSTR " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x%s%s%s", + "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d", MAC2STR(addr), MAC2STR(info->p2p_device_addr), wps_dev_type_bin2str(info->pri_dev_type, devtype, sizeof(devtype)), @@ -1729,7 +1847,8 @@ static void wpas_dev_found(void *ctx, const u8 *addr, info->dev_capab, info->group_capab, wfd_dev_info_hex ? " wfd_dev_info=0x" : "", wfd_dev_info_hex ? wfd_dev_info_hex : "", - info->vendor_elems ? " vendor_elems=1" : ""); + info->vendor_elems ? " vendor_elems=1" : "", + new_device); os_free(wfd_dev_info_hex); #endif /* CONFIG_NO_STDOUT_DEBUG */ @@ -1790,6 +1909,7 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) { struct wpa_supplicant *wpa_s = work->wpa_s; struct wpas_p2p_listen_work *lwork = work->ctx; + unsigned int duration; if (deinit) { if (work->started) { @@ -1814,8 +1934,16 @@ static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) wpa_s->pending_listen_freq = lwork->freq; wpa_s->pending_listen_duration = lwork->duration; - if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, lwork->duration) < 0) - { + duration = lwork->duration; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", + duration, duration + wpa_s->extra_roc_dur); + duration += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " "to remain on channel (%u MHz) for Listen " "state", lwork->freq); @@ -2824,6 +2952,7 @@ static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, u8 empty_dev_type[8]; unsigned int generated_pin = 0; struct wpa_supplicant *group = NULL; + int res; if (group_id) { for (group = wpa_s->global->ifaces; group; group = group->next) @@ -2842,15 +2971,17 @@ static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, os_memset(empty_dev_type, 0, sizeof(empty_dev_type)); pri_dev_type = empty_dev_type; } - os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR - " pri_dev_type=%s name='%s' config_methods=0x%x " - "dev_capab=0x%x group_capab=0x%x%s%s", - MAC2STR(dev_addr), - wps_dev_type_bin2str(pri_dev_type, devtype, - sizeof(devtype)), - dev_name, supp_config_methods, dev_capab, group_capab, - group ? " group=" : "", - group ? group->ifname : ""); + res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR + " pri_dev_type=%s name='%s' config_methods=0x%x " + "dev_capab=0x%x group_capab=0x%x%s%s", + MAC2STR(dev_addr), + wps_dev_type_bin2str(pri_dev_type, devtype, + sizeof(devtype)), + dev_name, supp_config_methods, dev_capab, group_capab, + group ? " group=" : "", + group ? group->ifname : ""); + if (os_snprintf_error(sizeof(params), res)) + wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated"); params[sizeof(params) - 1] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) { @@ -2886,10 +3017,14 @@ static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) } if (wpa_s->pending_pd_use == AUTO_PD_JOIN || - wpa_s->pending_pd_use == AUTO_PD_GO_NEG) - os_snprintf(params, sizeof(params), " peer_go=%d", - wpa_s->pending_pd_use == AUTO_PD_JOIN); - else + wpa_s->pending_pd_use == AUTO_PD_GO_NEG) { + int res; + + res = os_snprintf(params, sizeof(params), " peer_go=%d", + wpa_s->pending_pd_use == AUTO_PD_JOIN); + if (os_snprintf_error(sizeof(params), res)) + params[sizeof(params) - 1] = '\0'; + } else params[0] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) @@ -3369,6 +3504,8 @@ static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, { int i, cla = 0; + wpa_s->global->p2p_24ghz_social_channels = 1; + os_memset(cli_chan, 0, sizeof(*cli_chan)); wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " @@ -3439,7 +3576,7 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, enum chan_allowed { - NOT_ALLOWED, PASSIVE_ONLY, ALLOWED + NOT_ALLOWED, NO_IR, ALLOWED }; static int has_channel(struct wpa_global *global, @@ -3461,10 +3598,8 @@ static int has_channel(struct wpa_global *global, (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_RADAR)) return NOT_ALLOWED; - if (mode->channels[i].flag & - (HOSTAPD_CHAN_PASSIVE_SCAN | - HOSTAPD_CHAN_NO_IBSS)) - return PASSIVE_ONLY; + if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) + return NO_IR; return ALLOWED; } } @@ -3553,8 +3688,8 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, res = has_channel(wpa_s->global, mode, adj_chan, &flags); if (res == NOT_ALLOWED) return NOT_ALLOWED; - if (res == PASSIVE_ONLY) - ret = PASSIVE_ONLY; + if (res == NO_IR) + ret = NO_IR; if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) return NOT_ALLOWED; @@ -3592,8 +3727,8 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) return NOT_ALLOWED; - if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY) - return PASSIVE_ONLY; + if (res == NO_IR || res2 == NO_IR) + return NO_IR; return res; } @@ -3622,6 +3757,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); if (mode == NULL) continue; + if (mode->mode == HOSTAPD_MODE_IEEE80211G) + wpa_s->global->p2p_24ghz_social_channels = 1; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { enum chan_allowed res; res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); @@ -3635,7 +3772,7 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, } reg->channel[reg->channels] = ch; reg->channels++; - } else if (res == PASSIVE_ONLY && + } else if (res == NO_IR && wpa_s->conf->p2p_add_cli_chan) { if (cli_reg == NULL) { wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)", @@ -3795,8 +3932,10 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, char force_name[100]; int ret; - os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", - wpa_s->ifname); + ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", + wpa_s->ifname); + if (os_snprintf_error(sizeof(ifname), ret)) + return -1; force_name[0] = '\0'; wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL, @@ -3833,6 +3972,7 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, return -1; } p2pdev_wpa_s->parent = wpa_s; + wpa_s->p2p_dev = p2pdev_wpa_s; wpa_s->pending_interface_name[0] = '\0'; return 0; @@ -4079,6 +4219,10 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->p2p_oob_dev_pw); wpa_s->p2p_oob_dev_pw = NULL; + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = NULL; + wpa_s->p2p_group_common_freqs_num = 0; + /* TODO: remove group interface from the driver if this wpa_s instance * is on top of a P2P group interface */ } @@ -4837,8 +4981,10 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin)); else if (wps_method == WPS_PIN_DISPLAY) { ret = wps_generate_pin(); - os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d", - ret); + res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), + "%08d", ret); + if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res)) + wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", wpa_s->p2p_pin); } else @@ -5007,6 +5153,7 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) { struct wpa_global *global = wpa_s->global; + struct wpa_supplicant *calling_wpa_s = wpa_s; if (os_strcmp(ifname, "*") == 0) { struct wpa_supplicant *prev; @@ -5018,7 +5165,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) NOT_P2P_GROUP_INTERFACE || (prev->current_ssid && prev->current_ssid->p2p_group)) - wpas_p2p_disconnect(prev); + wpas_p2p_disconnect_safely(prev, calling_wpa_s); } return 0; } @@ -5028,7 +5175,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) break; } - return wpas_p2p_disconnect(wpa_s); + return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s); } @@ -5438,13 +5585,21 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, wpa_s->p2p_fallback_to_go_neg = 0; - if (force_freq > 0) { - freq = wpas_p2p_select_go_freq(wpa_s, force_freq); - if (freq < 0) - return -1; + if (ssid->mode == WPAS_MODE_P2P_GO) { + if (force_freq > 0) { + freq = wpas_p2p_select_go_freq(wpa_s, force_freq); + if (freq < 0) + return -1; + } else { + freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); + if (freq < 0 || + (freq > 0 && !freq_included(channels, freq))) + freq = 0; + } } else { - freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); - if (freq < 0 || (freq > 0 && !freq_included(channels, freq))) + freq = neg_freq; + if (freq < 0 || + (freq > 0 && !freq_included(channels, freq))) freq = 0; } @@ -5478,6 +5633,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, if (wpa_s == NULL) return -1; + p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS); + wpa_s->p2p_first_connection_timeout = connection_timeout; wpas_start_wps_go(wpa_s, ¶ms, 0); @@ -5757,7 +5914,29 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, } -static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) +static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); + + if (wpa_s->p2p_scan_work) { + struct wpa_radio_work *work = wpa_s->p2p_scan_work; + wpa_s->p2p_scan_work = NULL; + radio_work_done(work); + } + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + /* + * Indicate that results have been processed so that the P2P module can + * continue pending tasks. + */ + p2p_scan_res_handled(wpa_s->global->p2p); +} + + +static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) { wpas_p2p_clear_pending_action_tx(wpa_s); wpa_s->p2p_long_listen = 0; @@ -5767,14 +5946,17 @@ static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) if (wpa_s->global->p2p) p2p_stop_find(wpa_s->global->p2p); - return 0; + if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) { + wpa_printf(MSG_DEBUG, + "P2P: Do not consider the scan results after stop_find"); + wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search; + } } void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) { - if (wpas_p2p_stop_find_oper(wpa_s) > 0) - return; + wpas_p2p_stop_find_oper(wpa_s); wpas_p2p_remove_pending_group_interface(wpa_s); } @@ -6100,11 +6282,16 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) ip_addr[0] = '\0'; if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) { - os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u " - "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u", - ip[0], ip[1], ip[2], ip[3], - ip[4], ip[5], ip[6], ip[7], - ip[8], ip[9], ip[10], ip[11]); + int res; + + res = os_snprintf(ip_addr, sizeof(ip_addr), + " ip_addr=%u.%u.%u.%u " + "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u", + ip[0], ip[1], ip[2], ip[3], + ip[4], ip[5], ip[6], ip[7], + ip[8], ip[9], ip[10], ip[11]); + if (os_snprintf_error(sizeof(ip_addr), res)) + ip_addr[0] = '\0'; } wpas_p2p_group_started(wpa_s, 0, ssid, freq, @@ -6546,7 +6733,8 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) continue; - if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + iface != wpa_s->parent) continue; wpa_s->cross_connect_enabled = 1; @@ -6885,6 +7073,20 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, * provisioning step. */ wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection"); + + if (!wpa_s->p2p_go_group_formation_completed && + !wpa_s->group_formation_reported) { + /* + * GO has not yet notified group formation success since + * the WPS step was not completed cleanly. Do that + * notification now since the P2P Client was able to + * connect and as such, must have received the + * credential from the WPS step. + */ + if (wpa_s->global->p2p) + p2p_wps_success_cb(wpa_s->global->p2p, addr); + wpas_group_formation_completed(wpa_s, 1); + } } if (!wpa_s->p2p_go_group_formation_completed) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection"); @@ -7573,7 +7775,7 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, } len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) { + if (len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC " "attributes"); return -1; @@ -7589,7 +7791,7 @@ static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, } len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) { + if (len > end - pos) { wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P " "attributes"); return -1; @@ -7921,8 +8123,6 @@ void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) { - if (wpa_s == wpa_s->parent) - wpas_p2p_group_remove(wpa_s, "*"); if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " "the management interface is being removed"); diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 8e23c188..9f5a83bd 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -29,7 +29,6 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int pd, int ht40, int vht); int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, struct wpa_ssid *ssid); -int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, int freq, int ht40, int vht); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, @@ -171,6 +170,7 @@ int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s); int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail); +int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); #else /* CONFIG_P2P */ @@ -294,6 +294,12 @@ static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, { } +static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, + const char *ifname) +{ + return 0; +} + #endif /* CONFIG_P2P */ #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index debceb91..cb2c8d63 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -22,6 +22,7 @@ #include "notify.h" #include "bss.h" #include "scan.h" +#include "mesh.h" static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) @@ -175,6 +176,8 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) if (ret) { wpa_supplicant_notify_scanning(wpa_s, 0); wpas_notify_scan_done(wpa_s, 0); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d", + ret); radio_work_done(work); return; } @@ -291,7 +294,7 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) } if (count == 0) return NULL; - ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter)); + ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter)); if (ssids == NULL) return NULL; @@ -319,7 +322,7 @@ static void wpa_supplicant_optimize_freqs( wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO " "preferred frequency %d MHz", wpa_s->go_params->freq); - params->freqs = os_zalloc(2 * sizeof(int)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->go_params->freq; } else if (wpa_s->p2p_in_provisioning < 8 && @@ -343,7 +346,7 @@ static void wpa_supplicant_optimize_freqs( wpa_s->p2p_invite_go_freq > 0) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation", wpa_s->p2p_invite_go_freq); - params->freqs = os_zalloc(2 * sizeof(int)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->p2p_invite_go_freq; } @@ -369,7 +372,7 @@ static void wpa_supplicant_optimize_freqs( */ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz " "that was used during provisioning", wpa_s->wps_freq); - params->freqs = os_zalloc(2 * sizeof(int)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->after_wps--; @@ -381,7 +384,7 @@ static void wpa_supplicant_optimize_freqs( /* Optimize provisioning scan based on already known channel */ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", wpa_s->wps_freq); - params->freqs = os_zalloc(2 * sizeof(int)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->known_wps_freq = 0; /* only do this once */ @@ -460,6 +463,8 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) } #endif /* CONFIG_P2P */ + wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie); + #endif /* CONFIG_WPS */ #ifdef CONFIG_HS20 @@ -528,7 +533,7 @@ static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, return; } - params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int)); + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; for (count = 0, i = 0; i < mode->num_channels; i++) { @@ -600,7 +605,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; - int ret; + int ret, p2p_in_prog; struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; struct wpa_driver_scan_params *scan_params; @@ -653,7 +658,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } - if (wpas_p2p_in_progress(wpa_s)) { + p2p_in_prog = wpas_p2p_in_progress(wpa_s); + if (p2p_in_prog && p2p_in_prog != 2) { wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress"); wpa_supplicant_req_scan(wpa_s, 5, 0); return; @@ -810,7 +816,9 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_s->last_scan_req == MANUAL_SCAN_REQ) wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids); - for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { + for (tssid = wpa_s->conf->ssid; + wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid; + tssid = tssid->next) { if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) && tssid->scan_freq) { @@ -930,6 +938,14 @@ ssid_list_set: } #endif /* CONFIG_P2P */ + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_scan) { + params.mac_addr = wpa_s->mac_addr_scan; + params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN; + } + } + scan_params = ¶ms; scan: @@ -1145,7 +1161,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) os_memset(¶ms, 0, sizeof(params)); /* If we can't allocate space for the filters, we just don't filter */ - params.filter_ssids = os_zalloc(wpa_s->max_match_sets * + params.filter_ssids = os_calloc(wpa_s->max_match_sets, sizeof(struct wpa_driver_scan_filter)); prev_state = wpa_s->wpa_state; @@ -1273,6 +1289,15 @@ scan: wpa_setband_scan_freqs(wpa_s, scan_params); + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_sched_scan) { + params.mac_addr = wpa_s->mac_addr_sched_scan; + params.mac_addr_mask = wpa_s->mac_addr_sched_scan + + ETH_ALEN; + } + } + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, wpa_s->sched_scan_interval); wpabuf_free(extra_ie); @@ -1918,6 +1943,23 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + if (src->mac_addr_rand) { + params->mac_addr_rand = src->mac_addr_rand; + + if (src->mac_addr && src->mac_addr_mask) { + u8 *mac_addr; + + mac_addr = os_malloc(2 * ETH_ALEN); + if (!mac_addr) + goto failed; + + os_memcpy(mac_addr, src->mac_addr, ETH_ALEN); + os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask, + ETH_ALEN); + params->mac_addr = mac_addr; + params->mac_addr_mask = mac_addr + ETH_ALEN; + } + } return params; failed: @@ -1938,6 +1980,13 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params) os_free((u8 *) params->extra_ies); os_free(params->freqs); os_free(params->filter_ssids); + + /* + * Note: params->mac_addr_mask points to same memory allocation and + * must not be freed separately. + */ + os_free((u8 *) params->mac_addr); + os_free(params); } @@ -2042,6 +2091,14 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) params.freqs = wpa_s->manual_sched_scan_freqs; } + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_pno) { + params.mac_addr = wpa_s->mac_addr_pno; + params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN; + } + } + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval); os_free(params.filter_ssids); if (ret == 0) @@ -2069,3 +2126,61 @@ int wpas_stop_pno(struct wpa_supplicant *wpa_s) return ret; } + + +void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, + unsigned int type) +{ + type &= MAC_ADDR_RAND_ALL; + wpa_s->mac_addr_rand_enable &= ~type; + + if (type & MAC_ADDR_RAND_SCAN) { + os_free(wpa_s->mac_addr_scan); + wpa_s->mac_addr_scan = NULL; + } + + if (type & MAC_ADDR_RAND_SCHED_SCAN) { + os_free(wpa_s->mac_addr_sched_scan); + wpa_s->mac_addr_sched_scan = NULL; + } + + if (type & MAC_ADDR_RAND_PNO) { + os_free(wpa_s->mac_addr_pno); + wpa_s->mac_addr_pno = NULL; + } +} + + +int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask) +{ + u8 *tmp = NULL; + + wpas_mac_addr_rand_scan_clear(wpa_s, type); + + if (addr) { + tmp = os_malloc(2 * ETH_ALEN); + if (!tmp) + return -1; + os_memcpy(tmp, addr, ETH_ALEN); + os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN); + } + + if (type == MAC_ADDR_RAND_SCAN) { + wpa_s->mac_addr_scan = tmp; + } else if (type == MAC_ADDR_RAND_SCHED_SCAN) { + wpa_s->mac_addr_sched_scan = tmp; + } else if (type == MAC_ADDR_RAND_PNO) { + wpa_s->mac_addr_pno = tmp; + } else { + wpa_printf(MSG_INFO, + "scan: Invalid MAC randomization type=0x%x", + type); + os_free(tmp); + return -1; + } + + wpa_s->mac_addr_rand_enable |= type; + return 0; +} diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index 946d2b35..7650f5a2 100644 --- a/wpa_supplicant/scan.h +++ b/wpa_supplicant/scan.h @@ -49,4 +49,10 @@ void wpa_scan_free_params(struct wpa_driver_scan_params *params); int wpas_start_pno(struct wpa_supplicant *wpa_s); int wpas_stop_pno(struct wpa_supplicant *wpa_s); +void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, + unsigned int type); +int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask); + #endif /* SCAN_H */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index e6163192..80c280a1 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -137,6 +137,60 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) #endif /* CONFIG_SAE */ +/** + * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Pointer to the bss which is the target of authentication attempt + */ +static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + const u8 rrm_ie_len = 5; + u8 *pos; + const u8 *rrm_ie; + + wpa_s->rrm.rrm_used = 0; + + wpa_printf(MSG_DEBUG, + "RRM: Determining whether RRM can be used - device support: 0x%x", + wpa_s->drv_rrm_flags); + + rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) { + wpa_printf(MSG_DEBUG, "RRM: No RRM in network"); + return; + } + + if (!(wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || + !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) { + wpa_printf(MSG_DEBUG, + "RRM: Insufficient RRM support in driver - do not use RRM"); + return; + } + + if (sizeof(wpa_s->sme.assoc_req_ie) < + wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) { + wpa_printf(MSG_INFO, + "RRM: Unable to use RRM, no room for RRM IE"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request"); + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + os_memset(pos, 0, 2 + rrm_ie_len); + *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *pos++ = rrm_ie_len; + + /* Set supported capabilites flags */ + if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) + *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + + wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; + wpa_s->rrm.rrm_used = 1; +} + + static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int start) @@ -199,17 +253,22 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, "0x%x", params.auth_alg); } #ifdef CONFIG_SAE + wpa_s->sme.sae_pmksa_caching = 0; if (wpa_key_mgmt_sae(ssid->key_mgmt)) { const u8 *rsn; struct wpa_ie_data ied; rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (rsn && - wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) { - if (wpa_key_mgmt_sae(ied.key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); - params.auth_alg = WPA_AUTH_ALG_SAE; - } + if (!rsn) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE enabled, but target BSS does not advertise RSN"); + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + wpa_key_mgmt_sae(ied.key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_SAE; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE enabled, but target BSS does not advertise SAE AKM for RSN"); } } #endif /* CONFIG_SAE */ @@ -390,7 +449,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_memcpy(pos, ext_capab, ext_capab_len); } + sme_auth_handle_rrm(wpa_s, bss); + #ifdef CONFIG_SAE + if (params.auth_alg == WPA_AUTH_ALG_SAE && + pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0) + { + wpa_dbg(wpa_s, MSG_DEBUG, + "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); + params.auth_alg = WPA_AUTH_ALG_OPEN; + wpa_s->sme.sae_pmksa_caching = 1; + } + if (params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) resp = sme_auth_build_sae_commit(wpa_s, ssid, @@ -398,7 +468,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, else resp = sme_auth_build_sae_confirm(wpa_s); if (resp == NULL) { - wpas_connect_work_done(wpa_s); + wpas_connection_failed(wpa_s, bss->bssid); return; } params.sae_data = wpabuf_head(resp); @@ -545,6 +615,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, u16 status_code, const u8 *data, size_t len) { + int *groups; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " "status code %u", auth_transaction, status_code); @@ -552,10 +624,32 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && wpa_s->sme.sae.state == SAE_COMMITTED && wpa_s->current_bss && wpa_s->current_ssid) { - wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token " - "requested"); + int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + u16 group; + + groups = wpa_s->conf->sae_groups; + if (!groups || groups[0] <= 0) + groups = default_groups; + + if (len < sizeof(le16)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Too short SAE anti-clogging token request"); + return -1; + } + group = WPA_GET_LE16(data); + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: SAE anti-clogging token requested (group %u)", + group); + if (sae_group_allowed(&wpa_s->sme.sae, groups, group) != + WLAN_STATUS_SUCCESS) { + wpa_dbg(wpa_s, MSG_ERROR, + "SME: SAE group %u of anti-clogging request is invalid", + group); + return -1; + } wpabuf_free(wpa_s->sme.sae_token); - wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len); + wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16), + len - sizeof(le16)); sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 1); return 0; @@ -579,7 +673,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; if (auth_transaction == 1) { - int *groups = wpa_s->conf->sae_groups; + groups = wpa_s->conf->sae_groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); if (wpa_s->current_bss == NULL || @@ -668,7 +762,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for " "4-way handshake"); - wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, + wpa_s->pending_bssid); } #endif /* CONFIG_SAE */ @@ -775,6 +870,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, #endif /* CONFIG_IEEE80211R */ params.mode = mode; params.mgmt_frame_protection = wpa_s->sme.mfp; + params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; @@ -882,6 +978,27 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); +#ifdef CONFIG_SAE + if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid && + wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); + if (wpa_s->current_bss) { + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, + WLAN_REASON_DEAUTH_LEAVING); + wpas_connect_work_done(wpa_s); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_connect(wpa_s, bss, ssid); + return; + } + } +#endif /* CONFIG_SAE */ + /* * For now, unconditionally terminate the previous authentication. In * theory, this should not be needed, but mac80211 gets quite confused @@ -982,6 +1099,21 @@ void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, } +void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_s->sme.prev_bssid_set = 0; +#ifdef CONFIG_SAE + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = NULL; + sae_clear_data(&wpa_s->sme.sae); +#endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE80211R + if (wpa_s->sme.ft_ies) + sme_update_ft_ies(wpa_s, NULL, NULL, 0); +#endif /* CONFIG_IEEE80211R */ +} + + void sme_deinit(struct wpa_supplicant *wpa_s) { os_free(wpa_s->sme.ft_ies); @@ -990,11 +1122,7 @@ void sme_deinit(struct wpa_supplicant *wpa_s) #ifdef CONFIG_IEEE80211W sme_stop_sa_query(wpa_s); #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_SAE - wpabuf_free(wpa_s->sme.sae_token); - wpa_s->sme.sae_token = NULL; - sae_clear_data(&wpa_s->sme.sae); -#endif /* CONFIG_SAE */ + sme_clear_on_disassoc(wpa_s); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); @@ -1136,28 +1264,72 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, } -static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, - enum hostapd_hw_mode band, - struct wpa_driver_scan_params *params) +static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) { - /* Include only supported channels for the specified band */ + /* Include only affected channels */ struct hostapd_hw_modes *mode; int count, i; + int start, end; - mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + HOSTAPD_MODE_IEEE80211G); if (mode == NULL) { /* No channels supported in this band - use empty list */ params->freqs = os_zalloc(sizeof(int)); return; } + if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN && + wpa_s->current_bss) { + const u8 *ie; + + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + u8 o; + + o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE; + else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW; + } + } + + start = wpa_s->assoc_freq - 10; + end = wpa_s->assoc_freq + 10; + switch (wpa_s->sme.ht_sec_chan) { + case HT_SEC_CHAN_UNKNOWN: + /* HT40+ possible on channels 1..9 */ + if (wpa_s->assoc_freq <= 2452) + start -= 20; + /* HT40- possible on channels 5-13 */ + if (wpa_s->assoc_freq >= 2432) + end += 20; + break; + case HT_SEC_CHAN_ABOVE: + end += 20; + break; + case HT_SEC_CHAN_BELOW: + start -= 20; + break; + } + wpa_printf(MSG_DEBUG, + "OBSS: assoc_freq %d possible affected range %d-%d", + wpa_s->assoc_freq, start, end); + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; for (count = 0, i = 0; i < mode->num_channels; i++) { + int freq; + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) continue; - params->freqs[count++] = mode->channels[i].freq; + freq = mode->channels[i].freq; + if (freq - 10 >= end || freq + 10 <= start) + continue; /* not affected */ + params->freqs[count++] = freq; } } @@ -1173,7 +1345,7 @@ static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) } os_memset(¶ms, 0, sizeof(params)); - wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms); + wpa_obss_scan_freqs_list(wpa_s, ¶ms); params.low_priority = 1; wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); @@ -1198,6 +1370,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); wpa_s->sme.sched_obss_scan = 0; + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN; if (!enable) return; diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index 04404c18..fd5c3b4e 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -33,6 +33,7 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, void sme_state_changed(struct wpa_supplicant *wpa_s); void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, const u8 *prev_pending_bssid); +void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s); void sme_deinit(struct wpa_supplicant *wpa_s); int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); @@ -94,6 +95,10 @@ sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, { } +static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) +{ +} + static inline void sme_deinit(struct wpa_supplicant *wpa_s) { } diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt index b84cccc9..4c9f98e9 100644 --- a/wpa_supplicant/todo.txt +++ b/wpa_supplicant/todo.txt @@ -5,8 +5,6 @@ To do: authentication has been completed (cache scard data based on serial#(?) and try to optimize next connection if the same card is present for next auth) -- on disconnect event, could try to associate with another AP if one is - present in scan results; would need to update scan results periodically.. - if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from ssid->proto fields to avoid detecting downgrade attacks when the driver is not reporting RSN IE, but msg 3/4 has one @@ -24,14 +22,12 @@ To do: RFC 3748 Sect. 4.2 - test compilation with gcc -W options (more warnings?) (Done once; number of unused function arguments still present) -- add proper support for using dot11RSNAConfigSATimeout -- ctrl_iface: get/set/remove blob +- ctrl_iface: get/remove blob - use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and web pages including the same information.. i.e., have this information only in one page; how to build a PDF file with all the SGML included? - EAP-POTP/RSA SecurID profile (RFC 4793) - document wpa_gui build and consider adding it to 'make install' -- test madwifi with pairwise=TKIP group=WEP104 - consider merging hostapd and wpa_supplicant PMKSA cache implementations - consider redesigning pending EAP requests (identity/password/otp from ctrl_iface) by moving the retrying of the previous request into EAP @@ -57,14 +53,11 @@ To do: - try to work around race in configuring PTK and sending msg 4/4 (some NDIS drivers with ndiswrapper end up not being able to complete 4-way handshake in some cases; extra delay before setting the key seems to help) -- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to - clear memory; this would be used to clear temporary buffers containing - private data (e.g., keys); the macro can be defined to NOP in order to save - space (i.e., no code should depend on the macro doing something) - make sure that TLS session cache is not shared between EAP types or if it is, that the cache entries are bound to only one EAP type; e.g., cache entry created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS -- consider moving eap_tls_build_ack() call into eap_tls_process_helper() +- consider moving eap_peer_tls_build_ack() call into + eap_peer_tls_process_helper() (it seems to be called always if helper returns 1) * could need to modify eap_{ttls,peap,fast}_decrypt to do same - add support for fetching full user cert chain from Windows certificate diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c index 6dc41dec..c363b21b 100644 --- a/wpa_supplicant/wifi_display.c +++ b/wpa_supplicant/wifi_display.c @@ -233,15 +233,31 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd) if (pos == NULL) return -1; *pos++ = '\0'; - subelem = atoi(cmd); - if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) - return -1; len = os_strlen(pos); if (len & 1) return -1; len /= 2; + if (os_strcmp(cmd, "all") == 0) { + int res; + + e = wpabuf_alloc(len); + if (e == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { + wpabuf_free(e); + return -1; + } + res = wifi_display_subelem_set_from_ies(global, e); + wpabuf_free(e); + return res; + } + + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + if (len == 0) { /* Clear subelement */ e = NULL; @@ -271,7 +287,7 @@ int wifi_display_subelem_set_from_ies(struct wpa_global *global, { int subelements[MAX_WFD_SUBELEMS] = {}; const u8 *pos, *end; - int len, subelem; + unsigned int len, subelem; struct wpabuf *e; wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu", @@ -292,7 +308,7 @@ int wifi_display_subelem_set_from_ies(struct wpa_global *global, wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d", *pos, len - 3); - if (pos + len > end) + if (len > (unsigned int) (end - pos)) break; subelem = *pos; @@ -325,6 +341,19 @@ int wifi_display_subelem_get(struct wpa_global *global, char *cmd, { int subelem; + if (os_strcmp(cmd, "all") == 0) { + struct wpabuf *ie; + int res; + + ie = wifi_display_get_wfd_ie(global); + if (ie == NULL) + return 0; + res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie), + wpabuf_len(ie)); + wpabuf_free(ie); + return res; + } + subelem = atoi(cmd); if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) return -1; diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c new file mode 100644 index 00000000..5625d366 --- /dev/null +++ b/wpa_supplicant/wmm_ac.c @@ -0,0 +1,995 @@ +/* + * Wi-Fi Multimedia Admission Control (WMM-AC) + * Copyright(c) 2014, Intel Mobile Communication GmbH. + * Copyright(c) 2014, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/list.h" +#include "utils/eloop.h" +#include "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "driver_i.h" +#include "wmm_ac.h" + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); + +static const enum wmm_ac up_to_ac[8] = { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO +}; + + +static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 1) & 0x0f; +} + + +static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 5) & 0x03; +} + + +static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[1] >> 3) & 0x07; +} + + +static u8 wmm_ac_direction_to_idx(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_UPLINK: + return TS_DIR_IDX_UPLINK; + case WMM_AC_DIR_DOWNLINK: + return TS_DIR_IDX_DOWNLINK; + case WMM_AC_DIR_BIDIRECTIONAL: + return TS_DIR_IDX_BIDI; + default: + wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); + return WMM_AC_DIR_UPLINK; + } +} + + +static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, + const struct wmm_tspec_element *tspec) +{ + struct wmm_tspec_element *_tspec; + int ret; + u16 admitted_time = le_to_host16(tspec->medium_time); + u8 up = wmm_ac_get_user_priority(tspec); + u8 ac = up_to_ac[up]; + u8 dir = wmm_ac_get_direction(tspec); + u8 tsid = wmm_ac_get_tsid(tspec); + enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); + + /* should have been verified before, but double-check here */ + if (wpa_s->tspecs[ac][idx]) { + wpa_printf(MSG_ERROR, + "WMM AC: tspec (ac=%d, dir=%d) already exists!", + ac, dir); + return -1; + } + + /* copy tspec */ + _tspec = os_malloc(sizeof(*_tspec)); + if (!_tspec) + return -1; + + /* store the admitted TSPEC */ + os_memcpy(_tspec, tspec, sizeof(*_tspec)); + + if (dir != WMM_AC_DIR_DOWNLINK) { + ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); + wpa_printf(MSG_DEBUG, + "WMM AC: Add TS: addr=" MACSTR + " TSID=%u admitted time=%u, ret=%d", + MAC2STR(addr), tsid, admitted_time, ret); + if (ret < 0) { + os_free(_tspec); + return -1; + } + } + + wpa_s->tspecs[ac][idx] = _tspec; + + wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED + "tsid=%d addr=" MACSTR " admitted_time=%d", + tsid, MAC2STR(addr), admitted_time); + + return 0; +} + + +static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, + enum ts_dir_idx dir) +{ + struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; + u8 tsid; + + if (!tspec) + return; + + tsid = wmm_ac_get_tsid(tspec); + wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); + + /* update the driver in case of uplink/bidi */ + if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) + wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED + "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); + + os_free(wpa_s->tspecs[ac][dir]); + wpa_s->tspecs[ac][dir] = NULL; +} + + +static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + + if (!req) + return; + + if (failed) + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED + "tsid=%u", wmm_ac_get_tsid(&req->tspec)); + + eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); + wpa_s->addts_request = NULL; + os_free(req); +} + + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wmm_ac_addts_request *addts_req = timeout_ctx; + + wpa_printf(MSG_DEBUG, + "Timeout getting ADDTS response (tsid=%d up=%d)", + wmm_ac_get_tsid(&addts_req->tspec), + wmm_ac_get_user_priority(&addts_req->tspec)); + + wmm_ac_del_req(wpa_s, 1); +} + + +static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, + const struct wmm_ac_addts_request *req) +{ + struct wpabuf *buf; + int ret; + + wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR, + MAC2STR(req->address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(req->tspec)); + if (!buf) { + wpa_printf(MSG_ERROR, "WMM AC: Allocation error"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ); + wpabuf_put_u8(buf, req->dialog_token); + wpabuf_put_u8(buf, 0); /* status code */ + wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) { + wpa_printf(MSG_WARNING, + "WMM AC: Failed to send ADDTS Request"); + } + + wpabuf_free(buf); + return ret; +} + + +static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s, + const struct wmm_tspec_element *tspec, + const u8 *address) +{ + struct wpabuf *buf; + int ret; + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(*tspec)); + if (!buf) + return -1; + + wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS); + wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */ + wpabuf_put_u8(buf, 0); /* Status Code (not used) */ + wpabuf_put_data(buf, tspec, sizeof(*tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) + wpa_printf(MSG_WARNING, "Failed to send DELTS frame"); + + wpabuf_free(buf); + return ret; +} + + +/* return the AC using the given TSPEC tid */ +static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid, + enum ts_dir_idx *dir) +{ + int ac; + enum ts_dir_idx idx; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx] && + wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) { + if (dir) + *dir = idx; + return ac; + } + } + } + + return -1; +} + + +static struct wmm_ac_addts_request * +wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params, + const u8 *address) +{ + struct wmm_ac_addts_request *addts_req; + struct wmm_tspec_element *tspec; + u8 ac = up_to_ac[params->user_priority]; + u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd; + + addts_req = os_zalloc(sizeof(*addts_req)); + if (!addts_req) + return NULL; + + tspec = &addts_req->tspec; + os_memcpy(addts_req->address, address, ETH_ALEN); + + /* The dialog token cannot be zero */ + if (++wpa_s->wmm_ac_last_dialog_token == 0) + wpa_s->wmm_ac_last_dialog_token++; + + addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token; + tspec->eid = WLAN_EID_VENDOR_SPECIFIC; + tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */ + tspec->oui[0] = 0x00; + tspec->oui[1] = 0x50; + tspec->oui[2] = 0xf2; + tspec->oui_type = WMM_OUI_TYPE; + tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; + tspec->version = WMM_VERSION; + + tspec->ts_info[0] = params->tsid << 1; + tspec->ts_info[0] |= params->direction << 5; + tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7; + tspec->ts_info[1] = uapsd << 2; + tspec->ts_info[1] |= params->user_priority << 3; + tspec->ts_info[2] = 0; + + tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size); + if (params->fixed_nominal_msdu) + tspec->nominal_msdu_size |= + host_to_le16(WMM_AC_FIXED_MSDU_SIZE); + + tspec->mean_data_rate = host_to_le32(params->mean_data_rate); + tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate); + tspec->surplus_bandwidth_allowance = + host_to_le16(params->surplus_bandwidth_allowance); + + return addts_req; +} + + +static int param_in_range(const char *name, long value, + long min_val, long max_val) +{ + if (value < min_val || (max_val >= 0 && value > max_val)) { + wpa_printf(MSG_DEBUG, + "WMM AC: param %s (%ld) is out of range (%ld-%ld)", + name, value, min_val, max_val); + return 0; + } + + return 1; +} + + +static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s, + u8 tsid, u8 ac, u8 dir) +{ + enum ts_dir_idx idx; + int cur_ac, existing_ts = 0, replace_ts = 0; + + cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (cur_ac >= 0) { + if (cur_ac != ac) { + wpa_printf(MSG_DEBUG, + "WMM AC: TSID %i already exists on different ac (%d)", + tsid, cur_ac); + return -1; + } + + /* same tsid - this tspec will replace the current one */ + replace_ts |= BIT(idx); + } + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx]) + existing_ts |= BIT(idx); + } + + switch (dir) { + case WMM_AC_DIR_UPLINK: + /* replace existing uplink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_DOWNLINK: + /* replace existing downlink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_BIDIRECTIONAL: + /* replace all existing tspecs */ + replace_ts |= existing_ts; + break; + default: + return -1; + } + + return replace_ts; +} + + +static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params) +{ + enum wmm_ac req_ac; + +#define PARAM_IN_RANGE(field, min_value, max_value) \ + param_in_range(#field, params->field, min_value, max_value) + + if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) || + !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) || + !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) || + !PARAM_IN_RANGE(mean_data_rate, 1, -1) || + !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) || + !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY, + -1)) + return 0; +#undef PARAM_IN_RANGE + + if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK || + params->direction == WMM_TSPEC_DIRECTION_DOWNLINK || + params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) { + wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d", + params->direction); + return 0; + } + + req_ac = up_to_ac[params->user_priority]; + + /* Requested accesss category must have acm */ + if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) { + wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac); + return 0; + } + + if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac, + params->direction) < 0) + return 0; + + return 1; +} + + +static struct wmm_ac_assoc_data * +wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len) +{ + struct ieee802_11_elems elems; + struct wmm_parameter_element *wmm_params; + struct wmm_ac_assoc_data *assoc_data; + int i; + + /* Parsing WMM Parameter Element */ + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); + return NULL; + } + + if (!elems.wmm) { + wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE"); + return NULL; + } + + if (elems.wmm_len != sizeof(*wmm_params)) { + wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); + return NULL; + } + + wmm_params = (struct wmm_parameter_element *)(elems.wmm); + + assoc_data = os_zalloc(sizeof(*assoc_data)); + if (!assoc_data) + return NULL; + + for (i = 0; i < WMM_AC_NUM; i++) + assoc_data->ac_params[i].acm = + !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM); + + wpa_printf(MSG_DEBUG, + "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u", + assoc_data->ac_params[WMM_AC_BE].acm, + assoc_data->ac_params[WMM_AC_BK].acm, + assoc_data->ac_params[WMM_AC_VI].acm, + assoc_data->ac_params[WMM_AC_VO].acm); + + return assoc_data; +} + + +static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params) +{ + struct wmm_ac_assoc_data *assoc_data; + u8 ac; + + if (wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_ERROR, "WMM AC: Already initialized"); + return -1; + } + + if (!ies) { + wpa_printf(MSG_ERROR, "WMM AC: Missing IEs"); + return -1; + } + + if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { + wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration"); + return -1; + } + + os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); + wpa_s->wmm_ac_last_dialog_token = 0; + wpa_s->addts_request = NULL; + + assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); + if (!assoc_data) + return -1; + + wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x", + wmm_params->uapsd_queues); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + assoc_data->ac_params[ac].uapsd = + !!(wmm_params->uapsd_queues & BIT(ac)); + } + + wpa_s->wmm_ac_assoc_info = assoc_data; + return 0; +} + + +static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) +{ + enum ts_dir_idx idx; + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (!(dir_bitmap & BIT(idx))) + continue; + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + } +} + + +static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) +{ + int i; + + for (i = 0; i < WMM_AC_NUM; i++) + wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); + + /* delete pending add_ts requset */ + wmm_ac_del_req(wpa_s, 1); + + os_free(wpa_s->wmm_ac_assoc_info); + wpa_s->wmm_ac_assoc_info = NULL; +} + + +void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params) +{ + if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params)) + return; + + wpa_printf(MSG_DEBUG, + "WMM AC: Valid WMM association, WMM AC is enabled"); +} + + +void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wmm_ac_assoc_info) + return; + + wmm_ac_deinit(wpa_s); + wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled"); +} + + +int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) +{ + struct wmm_tspec_element tspec; + int ac; + enum ts_dir_idx dir; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Failed to delete TS, WMM AC is disabled"); + return -1; + } + + ac = wmm_ac_find_tsid(wpa_s, tsid, &dir); + if (ac < 0) { + wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist"); + return -1; + } + + tspec = *wpa_s->tspecs[ac][dir]; + + wmm_ac_del_ts_idx(wpa_s, ac, dir); + + wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); + + return 0; +} + + +int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, + struct wmm_ac_ts_setup_params *params) +{ + struct wmm_ac_addts_request *addts_req; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Cannot add TS - missing assoc data"); + return -1; + } + + if (wpa_s->addts_request) { + wpa_printf(MSG_DEBUG, + "WMM AC: can't add TS - ADDTS request is already pending"); + return -1; + } + + /* + * we can setup downlink TS even without driver support. + * however, we need driver support for the other directions. + */ + if (params->direction != WMM_AC_DIR_DOWNLINK && + !wpa_s->wmm_ac_supported) { + wpa_printf(MSG_DEBUG, + "Cannot set uplink/bidi TS without driver support"); + return -1; + } + + if (!wmm_ac_ts_req_is_valid(wpa_s, params)) + return -1; + + wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR + " tsid=%u user priority=%u direction=%d)", + MAC2STR(wpa_s->bssid), params->tsid, + params->user_priority, params->direction); + + addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid); + if (!addts_req) + return -1; + + if (wmm_ac_send_addts_request(wpa_s, addts_req)) + goto err; + + /* save as pending and set ADDTS resp timeout to 1 second */ + wpa_s->addts_request = addts_req; + eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, + wpa_s, addts_req); + return 0; +err: + os_free(addts_req); + return -1; +} + + +static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, + const struct wmm_tspec_element *tspec) +{ + int ac; + u8 tsid; + enum ts_dir_idx idx; + + tsid = wmm_ac_get_tsid(tspec); + + wpa_printf(MSG_DEBUG, + "WMM AC: DELTS frame has been received TSID=%u addr=" + MACSTR, tsid, MAC2STR(sa)); + + ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (ac < 0) { + wpa_printf(MSG_DEBUG, + "WMM AC: Ignoring DELTS frame - TSID does not exist"); + return; + } + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + + wpa_printf(MSG_DEBUG, + "TS was deleted successfully (tsid=%u address=" MACSTR ")", + tsid, MAC2STR(sa)); +} + + +static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 resp_dialog_token, const u8 status_code, + const struct wmm_tspec_element *tspec) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + u8 ac, tsid, up, dir; + int replace_tspecs; + + tsid = wmm_ac_get_tsid(tspec); + dir = wmm_ac_get_direction(tspec); + up = wmm_ac_get_user_priority(tspec); + ac = up_to_ac[up]; + + /* make sure we have a matching addts request */ + if (!req || req->dialog_token != resp_dialog_token) { + wpa_printf(MSG_DEBUG, + "WMM AC: no req with dialog=%u, ignoring frame", + resp_dialog_token); + return; + } + + /* make sure the params are the same */ + if (os_memcmp(req->address, sa, ETH_ALEN) != 0 || + tsid != wmm_ac_get_tsid(&req->tspec) || + up != wmm_ac_get_user_priority(&req->tspec) || + dir != wmm_ac_get_direction(&req->tspec)) { + wpa_printf(MSG_DEBUG, + "WMM AC: ADDTS params do not match, ignoring frame"); + return; + } + + /* delete pending request */ + wmm_ac_del_req(wpa_s, 0); + + wpa_printf(MSG_DEBUG, + "ADDTS response status=%d tsid=%u up=%u direction=%u", + status_code, tsid, up, dir); + + if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { + wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); + goto err_msg; + } + + replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); + if (replace_tspecs < 0) + goto err_delts; + + wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); + + /* when replacing tspecs - delete first */ + wmm_ac_del_ts(wpa_s, ac, replace_tspecs); + + /* Creating a new traffic stream */ + wpa_printf(MSG_DEBUG, + "WMM AC: adding a new TS with TSID=%u address="MACSTR + " medium time=%u access category=%d dir=%d ", + tsid, MAC2STR(sa), + le_to_host16(tspec->medium_time), ac, dir); + + if (wmm_ac_add_ts(wpa_s, sa, tspec)) + goto err_delts; + + return; + +err_delts: + /* ask the ap to delete the tspec */ + wmm_ac_send_delts(wpa_s, tspec, sa); +err_msg: + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", + tsid); +} + + +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + u8 action; + u8 dialog_token; + u8 status_code; + struct ieee802_11_elems elems; + struct wmm_tspec_element *tspec; + + if (wpa_s->wmm_ac_assoc_info == NULL) { + wpa_printf(MSG_DEBUG, + "WMM AC: WMM AC is disabled, ignoring action frame"); + return; + } + + action = data[0]; + + if (action != WMM_ACTION_CODE_ADDTS_RESP && + action != WMM_ACTION_CODE_DELTS) { + wpa_printf(MSG_DEBUG, + "WMM AC: Unknown action (%d), ignoring action frame", + action); + return; + } + + /* WMM AC action frame */ + if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR + " is other than ours, ignoring frame", MAC2STR(da)); + return; + } + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR + " different other than our bssid", MAC2STR(da)); + return; + } + + if (len < 2 + sizeof(struct wmm_tspec_element)) { + wpa_printf(MSG_DEBUG, + "WMM AC: Short ADDTS response ignored (len=%lu)", + (unsigned long) len); + return; + } + + data++; + len--; + dialog_token = data[0]; + status_code = data[1]; + + if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { + wpa_printf(MSG_DEBUG, + "WMM AC: Could not parse WMM AC action from " MACSTR, + MAC2STR(sa)); + return; + } + + /* the struct also contains the type and value, so decrease it */ + if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { + wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); + return; + } + + tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); + + wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); + + switch (action) { + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, + tspec); + break; + case WMM_ACTION_CODE_DELTS: + wmm_ac_handle_delts(wpa_s, sa, tspec); + break; + default: + break; + } +} + + +static const char * get_ac_str(u8 ac) +{ + switch (ac) { + case WMM_AC_BE: + return "BE"; + case WMM_AC_BK: + return "BK"; + case WMM_AC_VI: + return "VI"; + case WMM_AC_VO: + return "VO"; + default: + return "N/A"; + } +} + + +static const char * get_direction_str(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_DOWNLINK: + return "Downlink"; + case WMM_AC_DIR_UPLINK: + return "Uplink"; + case WMM_AC_DIR_BIDIRECTIONAL: + return "Bi-directional"; + default: + return "N/A"; + } +} + + +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; + enum ts_dir_idx idx; + int pos = 0; + u8 ac, up; + + if (!assoc_info) { + return wpa_scnprintf(buf, buflen - pos, + "Not associated to a WMM AP, WMM AC is Disabled\n"); + } + + pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + int ts_count = 0; + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "%s: acm=%d uapsd=%d\n", + get_ac_str(ac), + assoc_info->ac_params[ac].acm, + assoc_info->ac_params[ac].uapsd); + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + struct wmm_tspec_element *tspec; + u8 dir, tsid; + const char *dir_str; + + tspec = wpa_s->tspecs[ac][idx]; + if (!tspec) + continue; + + ts_count++; + + dir = wmm_ac_get_direction(tspec); + dir_str = get_direction_str(dir); + tsid = wmm_ac_get_tsid(tspec); + up = wmm_ac_get_user_priority(tspec); + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\tTSID=%u UP=%u\n" + "\tAddress = "MACSTR"\n" + "\tWMM AC dir = %s\n" + "\tTotal admitted time = %u\n\n", + tsid, up, + MAC2STR(wpa_s->bssid), + dir_str, + le_to_host16(tspec->medium_time)); + } + + if (!ts_count) { + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\t(No Traffic Stream)\n\n"); + } + } + + return pos; +} + + +static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count = 0; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (wpa_s->tspecs[ac][dir]) + tspecs_count++; + } + } + + return tspecs_count; +} + + +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count; + + wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); + + if (!wpa_s->wmm_ac_assoc_info) + return; + + tspecs_count = wmm_ac_get_tspecs_count(wpa_s); + if (!tspecs_count) { + wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); + + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->last_tspecs = os_calloc(tspecs_count, + sizeof(*wpa_s->last_tspecs)); + if (!wpa_s->last_tspecs) { + wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); + return; + } + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (!wpa_s->tspecs[ac][dir]) + continue; + + wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = + *wpa_s->tspecs[ac][dir]; + } + } + + wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", + wpa_s->last_tspecs_count); +} + + +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->last_tspecs) { + wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); + os_free(wpa_s->last_tspecs); + wpa_s->last_tspecs = NULL; + wpa_s->last_tspecs_count = 0; + } +} + + +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) + return 0; + + wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", + wpa_s->last_tspecs_count); + + for (i = 0; i < wpa_s->last_tspecs_count; i++) + wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); + + return 0; +} diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h new file mode 100644 index 00000000..5171b168 --- /dev/null +++ b/wpa_supplicant/wmm_ac.h @@ -0,0 +1,176 @@ +/* + * Wi-Fi Multimedia Admission Control (WMM-AC) + * Copyright(c) 2014, Intel Mobile Communication GmbH. + * Copyright(c) 2014, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WMM_AC_H +#define WMM_AC_H + +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" + +struct wpa_supplicant; + +#define WMM_AC_ACCESS_POLICY_EDCA 1 +#define WMM_AC_FIXED_MSDU_SIZE BIT(15) + +#define WMM_AC_MAX_TID 7 +#define WMM_AC_MAX_USER_PRIORITY 7 +#define WMM_AC_MIN_SBA_UNITY 0x2000 +#define WMM_AC_MAX_NOMINAL_MSDU 32767 + +/** + * struct wmm_ac_assoc_data - WMM Admission Control Association Data + * + * This struct will store any relevant WMM association data needed by WMM AC. + * In case there is a valid WMM association, an instance of this struct will be + * created. In case there is no instance of this struct, the station is not + * associated to a valid WMM BSS and hence, WMM AC will not be used. + */ +struct wmm_ac_assoc_data { + struct { + /* + * acm - Admission Control Mandatory + * In case an access category is ACM, the traffic will have + * to be admitted by WMM-AC's admission mechanism before use. + */ + unsigned int acm:1; + + /* + * uapsd_queues - Unscheduled Automatic Power Save Delivery + * queues. + * Indicates whether ACs are configured for U-APSD (or legacy + * PS). Storing this value is necessary in order to set the + * Power Save Bit (PSB) in ADDTS request Action frames (if not + * given). + */ + unsigned int uapsd:1; + } ac_params[WMM_AC_NUM]; +}; + +/** + * wmm_ac_dir - WMM Admission Control Direction + */ +enum wmm_ac_dir { + WMM_AC_DIR_UPLINK = 0, + WMM_AC_DIR_DOWNLINK = 1, + WMM_AC_DIR_BIDIRECTIONAL = 3 +}; + +/** + * ts_dir_idx - indices of internally saved tspecs + * + * we can have multiple tspecs (downlink + uplink) per ac. + * save them in array, and use the enum to directly access + * the respective tspec slot (according to the direction). + */ +enum ts_dir_idx { + TS_DIR_IDX_UPLINK, + TS_DIR_IDX_DOWNLINK, + TS_DIR_IDX_BIDI, + + TS_DIR_IDX_COUNT +}; +#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1) + +/** + * struct wmm_ac_addts_request - ADDTS Request Information + * + * The last sent ADDTS request(s) will be saved as element(s) of this struct in + * order to be compared with the received ADDTS response in ADDTS response + * action frame handling and should be stored until that point. + * In case a new traffic stream will be created/replaced/updated, only its + * relevant traffic stream information will be stored as a wmm_ac_ts struct. + */ +struct wmm_ac_addts_request { + /* + * dialog token - Used to link the recived ADDTS response with this + * saved ADDTS request when ADDTS response is being handled + */ + u8 dialog_token; + + /* + * address - The alleged traffic stream's receiver/transmitter address + * Address and TID are used to identify the TS (TID is contained in + * TSPEC) + */ + u8 address[ETH_ALEN]; + + /* + * tspec - Traffic Stream Specification, will be used to compare the + * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response + * and act accordingly in ADDTS response handling + */ + struct wmm_tspec_element tspec; +}; + + +/** + * struct wmm_ac_ts_setup_params - TS setup parameters + * + * This struct holds parameters which should be provided + * to wmm_ac_ts_setup in order to setup a traffic stream + */ +struct wmm_ac_ts_setup_params { + /* + * tsid - Traffic ID + * TID and address are used to identify the TS + */ + int tsid; + + /* + * direction - Traffic Stream's direction + */ + enum wmm_ac_dir direction; + + /* + * user_priority - Traffic Stream's user priority + */ + int user_priority; + + /* + * nominal_msdu_size - Nominal MAC service data unit size + */ + int nominal_msdu_size; + + /* + * fixed_nominal_msdu - Whether the size is fixed + * 0 = Nominal MSDU size is not fixed + * 1 = Nominal MSDU size is fixed + */ + int fixed_nominal_msdu; + + /* + * surplus_bandwidth_allowance - Specifies excess time allocation + */ + int mean_data_rate; + + /* + * minimum_phy_rate - Specifies the minimum supported PHY rate in bps + */ + int minimum_phy_rate; + + /* + * surplus_bandwidth_allowance - Specifies excess time allocation + */ + int surplus_bandwidth_allowance; +}; + +void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params); +void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s); +int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, + struct wmm_ac_ts_setup_params *params); +int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid); +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len); +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s); +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s); +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s); + +#endif /* WMM_AC_H */ diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 4a792c4b..954de67c 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -10,6 +10,7 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" @@ -244,6 +245,7 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, /* multiple TFS Resp IE (assuming consecutive) */ u8 *tfsresp_ie_start = NULL; u8 *tfsresp_ie_end = NULL; + size_t left; if (len < 3) return; @@ -251,11 +253,12 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", frm[0], key_len_total); - pos += 3 + key_len_total; - if (pos > frm + len) { + left = len - 3; + if (key_len_total > left) { wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); return; } + pos += 3 + key_len_total; while (pos - frm < len) { u8 ie_len = *(pos + 1); if (pos + 2 + ie_len > frm + len) { @@ -309,13 +312,7 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) int i; for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { - os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info); - os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str); - os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can); - os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur); - os_free(wpa_s->wnm_neighbor_report_elements[i].bearing); os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); - os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap); os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); } @@ -334,12 +331,9 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); break; } - os_free(rep->tsf_info); - rep->tsf_info = os_zalloc(sizeof(struct tsf_info)); - if (rep->tsf_info == NULL) - break; - os_memcpy(rep->tsf_info->tsf_offset, pos, 2); - os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2); + rep->tsf_offset = WPA_GET_LE16(pos); + rep->beacon_int = WPA_GET_LE16(pos + 2); + rep->tsf_present = 1; break; case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: if (elen < 2) { @@ -347,12 +341,8 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, "country string"); break; } - os_free(rep->con_coun_str); - rep->con_coun_str = - os_zalloc(sizeof(struct condensed_country_string)); - if (rep->con_coun_str == NULL) - break; - os_memcpy(rep->con_coun_str->country_string, pos, 2); + os_memcpy(rep->country, pos, 2); + rep->country_present = 1; break; case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: if (elen < 1) { @@ -360,25 +350,13 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, "candidate"); break; } - os_free(rep->bss_tran_can); - rep->bss_tran_can = - os_zalloc(sizeof(struct bss_transition_candidate)); - if (rep->bss_tran_can == NULL) - break; - rep->bss_tran_can->preference = pos[0]; + rep->preference = pos[0]; + rep->preference_present = 1; break; case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: - if (elen < 10) { - wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination " - "duration"); - break; - } - os_free(rep->bss_term_dur); - rep->bss_term_dur = - os_zalloc(sizeof(struct bss_termination_duration)); - if (rep->bss_term_dur == NULL) - break; - os_memcpy(rep->bss_term_dur->duration, pos, 10); + rep->bss_term_tsf = WPA_GET_LE64(pos); + rep->bss_term_dur = WPA_GET_LE16(pos + 8); + rep->bss_term_present = 1; break; case WNM_NEIGHBOR_BEARING: if (elen < 8) { @@ -386,11 +364,10 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, "bearing"); break; } - os_free(rep->bearing); - rep->bearing = os_zalloc(sizeof(struct bearing)); - if (rep->bearing == NULL) - break; - os_memcpy(rep->bearing->bearing, pos, 8); + rep->bearing = WPA_GET_LE16(pos); + rep->distance = WPA_GET_LE32(pos + 2); + rep->rel_height = WPA_GET_LE16(pos + 2 + 4); + rep->bearing_present = 1; break; case WNM_NEIGHBOR_MEASUREMENT_PILOT: if (elen < 1) { @@ -412,12 +389,8 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, "capabilities"); break; } - os_free(rep->rrm_cap); - rep->rrm_cap = - os_zalloc(sizeof(struct rrm_enabled_capabilities)); - if (rep->rrm_cap == NULL) - break; - os_memcpy(rep->rrm_cap->capabilities, pos, 5); + os_memcpy(rep->rm_capab, pos, 5); + rep->rm_capab_present = 1; break; case WNM_NEIGHBOR_MULTIPLE_BSSID: if (elen < 1) { @@ -436,6 +409,22 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, } +static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) +{ + struct wpa_bss *bss = wpa_s->current_bss; + const char *country = NULL; + + if (bss) { + const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); + + if (elem && elem[1] >= 2) + country = (const char *) (elem + 2); + } + + return ieee80211_chan_to_freq(country, op_class, chan); +} + + static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, const u8 *pos, u8 len, struct neighbor_report *rep) @@ -448,7 +437,7 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, } os_memcpy(rep->bssid, pos, ETH_ALEN); - os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4); + rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); rep->regulatory_class = *(pos + 10); rep->channel_number = *(pos + 11); rep->phy_type = *(pos + 12); @@ -472,47 +461,78 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, left -= elen; pos += elen; } + + rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, + rep->channel_number); } -static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res, - struct neighbor_report *neigh_rep, - u8 num_neigh_rep, u8 *bssid_to_connect) +static struct wpa_bss * +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) { - u8 i, j; + u8 i; + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_bss *target; - if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss) + if (!bss) return 0; wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", - MAC2STR(wpa_s->bssid), wpa_s->current_bss->level); - - for (i = 0; i < num_neigh_rep; i++) { - for (j = 0; j < scan_res->num; j++) { - /* Check for a better RSSI AP */ - if (os_memcmp(scan_res->res[j]->bssid, - neigh_rep[i].bssid, ETH_ALEN) == 0 && - scan_res->res[j]->level > - wpa_s->current_bss->level) { - /* Got a BSSID with better RSSI value */ - os_memcpy(bssid_to_connect, neigh_rep[i].bssid, - ETH_ALEN); - wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR - " with better scan RSSI %d", - MAC2STR(scan_res->res[j]->bssid), - scan_res->res[j]->level); - return 1; - } - wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR - " RSSI %d", j, - MAC2STR(scan_res->res[j]->bssid), - scan_res->res[j]->level); + MAC2STR(wpa_s->bssid), bss->level); + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->preference_present && nei->preference == 0) { + wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, + MAC2STR(nei->bssid)); + continue; + } + + target = wpa_bss_get_bssid(wpa_s, nei->bssid); + if (!target) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) not found in scan results", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (bss->ssid_len != target->ssid_len || + os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { + /* + * TODO: Could consider allowing transition to another + * ESS if PMF was enabled for the association. + */ + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) in different ESS", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (target->level < bss->level && target->level < -80) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) does not have sufficient signal level (%d)", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1, + target->level); + continue; } + + wpa_printf(MSG_DEBUG, + "WNM: Found an acceptable preferred transition candidate BSS " + MACSTR " (RSSI %d)", + MAC2STR(nei->bssid), target->level); + return target; } - return 0; + return NULL; } @@ -524,10 +544,16 @@ static void wnm_send_bss_transition_mgmt_resp( u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t len; + int res; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " "to " MACSTR " dialog_token=%u status=%u delay=%d", MAC2STR(wpa_s->bssid), dialog_token, status, delay); + if (!wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Current BSS not known - drop response"); + return; + } mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); @@ -557,62 +583,203 @@ static void wnm_send_bss_transition_mgmt_resp( len = pos - (u8 *) &mgmt->u.action.category; - wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - &mgmt->u.action.category, len, 0); + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send BSS Transition Management Response"); + } } -void wnm_scan_response(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res) +int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) { - u8 bssid[ETH_ALEN]; + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; - if (scan_res == NULL) { - wpa_printf(MSG_ERROR, "Scan result is NULL"); - goto send_bss_resp_fail; + if (!wpa_s->wnm_neighbor_report_elements) + return 0; + + if (os_reltime_before(&wpa_s->wnm_cand_valid_until, + &wpa_s->scan_trigger_time)) { + wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); + wnm_deallocate_memory(wpa_s); + return 0; + } + + if (!wpa_s->current_bss || + os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, + ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); + return 0; } /* Compare the Neighbor Report and scan results */ - if (compare_scan_neighbor_results(wpa_s, scan_res, - wpa_s->wnm_neighbor_report_elements, - wpa_s->wnm_num_neighbor_report, - bssid) == 1) { - /* Associate to the network */ - struct wpa_bss *bss; - struct wpa_ssid *ssid = wpa_s->current_ssid; - - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (!bss) { - wpa_printf(MSG_DEBUG, "WNM: Target AP not found from " - "BSS table"); - goto send_bss_resp_fail; - } + bss = compare_scan_neighbor_results(wpa_s); + if (!bss) { + wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); + status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; + goto send_bss_resp_fail; + } - /* Send the BSS Management Response - Accept */ - if (wpa_s->wnm_reply) { - wnm_send_bss_transition_mgmt_resp(wpa_s, + /* Associate to the network */ + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; + wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, - 0, bssid); - } + 0, bss->bssid); + } - wpa_s->reassociate = 1; - wpa_supplicant_connect(wpa_s, bss, ssid); - wnm_deallocate_memory(wpa_s); - return; + if (bss == wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Already associated with the preferred candidate"); + return 1; } - /* Send reject response for all the failures */ -send_bss_resp_fail: + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); wnm_deallocate_memory(wpa_s); + return 1; + +send_bss_resp_fail: + if (!reply_on_fail) + return 0; + + /* Send reject response for all the failures */ + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, - WNM_BSS_TM_REJECT_UNSPECIFIED, - 0, NULL); + status, 0, NULL); + } + wnm_deallocate_memory(wpa_s); + + return 0; +} + + +static int cand_pref_compar(const void *a, const void *b) +{ + const struct neighbor_report *aa = a; + const struct neighbor_report *bb = b; + + if (!aa->preference_present && !bb->preference_present) + return 0; + if (!aa->preference_present) + return 1; + if (!bb->preference_present) + return -1; + if (bb->preference > aa->preference) + return 1; + if (bb->preference < aa->preference) + return -1; + return 0; +} + + +static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wnm_neighbor_report_elements) + return; + qsort(wpa_s->wnm_neighbor_report_elements, + wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), + cand_pref_compar); +} + + +static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); + if (!wpa_s->wnm_neighbor_report_elements) + return; + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + wpa_printf(MSG_DEBUG, "%u: " MACSTR + " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", + i, MAC2STR(nei->bssid), nei->bssid_info, + nei->regulatory_class, + nei->channel_number, nei->phy_type, + nei->preference_present ? nei->preference : -1, + nei->freq); } - return; +} + + +static int chan_supported(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int i; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + int j; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan; + + chan = &mode->channels[j]; + if (chan->freq == freq && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + return 1; + } + } + + return 0; +} + + +static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) +{ + int *freqs; + int num_freqs = 0; + unsigned int i; + + if (!wpa_s->wnm_neighbor_report_elements) + return; + + if (wpa_s->hw.modes == NULL) + return; + + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; + + freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); + if (freqs == NULL) + return; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->freq <= 0) { + wpa_printf(MSG_DEBUG, + "WNM: Unknown neighbor operating frequency for " + MACSTR " - scan all channels", + MAC2STR(nei->bssid)); + os_free(freqs); + return; + } + if (chan_supported(wpa_s, nei->freq)) + add_freq(freqs, &num_freqs, nei->freq); + } + + if (num_freqs == 0) { + os_free(freqs); + return; + } + + wpa_printf(MSG_DEBUG, + "WNM: Scan %d frequencies based on transition candidate list", + num_freqs); + wpa_s->next_scan_freqs = freqs; } @@ -620,20 +787,28 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { + unsigned int beacon_int; + u8 valid_int; + if (pos + 5 > end) return; + if (wpa_s->current_bss) + beacon_int = wpa_s->current_bss->beacon_int; + else + beacon_int = 100; /* best guess */ + wpa_s->wnm_dialog_token = pos[0]; wpa_s->wnm_mode = pos[1]; wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); - wpa_s->wnm_validity_interval = pos[4]; + valid_int = pos[4]; wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", wpa_s->wnm_dialog_token, wpa_s->wnm_mode, - wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval); + wpa_s->wnm_dissoc_timer, valid_int); pos += 5; @@ -648,7 +823,6 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; - unsigned int beacon_int; if (pos + 1 > end || pos + 1 + pos[0] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " @@ -659,11 +833,6 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, url[pos[0]] = '\0'; pos += 1 + pos[0]; - if (wpa_s->current_bss) - beacon_int = wpa_s->current_bss->beacon_int; - else - beacon_int = 100; /* best guess */ - wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", wpa_sm_pmf_enabled(wpa_s->wpa), wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); @@ -681,11 +850,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { + unsigned int valid_ms; + wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); - wpa_s->wnm_num_neighbor_report = 0; - os_free(wpa_s->wnm_neighbor_report_elements); - wpa_s->wnm_neighbor_report_elements = os_zalloc( - WNM_MAX_NEIGHBOR_REPORT * + wnm_deallocate_memory(wpa_s); + wpa_s->wnm_neighbor_report_elements = os_calloc( + WNM_MAX_NEIGHBOR_REPORT, sizeof(struct neighbor_report)); if (wpa_s->wnm_neighbor_report_elements == NULL) return; @@ -712,8 +882,34 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, pos += len; wpa_s->wnm_num_neighbor_report++; } + wnm_sort_cand_list(wpa_s); + wnm_dump_cand_list(wpa_s); + valid_ms = valid_int * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", + valid_ms); + os_get_reltime(&wpa_s->wnm_cand_valid_until); + wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; + wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; + wpa_s->wnm_cand_valid_until.sec += + wpa_s->wnm_cand_valid_until.usec / 1000000; + wpa_s->wnm_cand_valid_until.usec %= 1000000; + os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); + + if (wpa_s->last_scan_res_used > 0) { + struct os_reltime now; + + os_get_reltime(&now); + if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { + wpa_printf(MSG_DEBUG, + "WNM: Try to use recent scan results"); + if (wnm_scan_process(wpa_s, 0) > 0) + return; + wpa_printf(MSG_DEBUG, + "WNM: No match in previous scan results - try a new scan"); + } + } - wpa_s->scan_res_handler = wnm_scan_response; + wnm_set_scan_freqs(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } else if (reply) { enum bss_trans_mgmt_status_code status; diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index d2eb96dd..8de43480 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -9,37 +9,12 @@ #ifndef WNM_STA_H #define WNM_STA_H -struct tsf_info { - u8 tsf_offset[2]; - u8 beacon_interval[2]; -}; - -struct condensed_country_string { - u8 country_string[2]; -}; - -struct bss_transition_candidate { - u8 preference; -}; - -struct bss_termination_duration { - u8 duration[10]; -}; - -struct bearing { - u8 bearing[8]; -}; - struct measurement_pilot { u8 measurement_pilot; u8 subelem_len; u8 subelems[255]; }; -struct rrm_enabled_capabilities { - u8 capabilities[5]; -}; - struct multiple_bssid { u8 max_bssid_indicator; u8 subelem_len; @@ -48,18 +23,29 @@ struct multiple_bssid { struct neighbor_report { u8 bssid[ETH_ALEN]; - u8 bssid_information[4]; + u32 bssid_info; u8 regulatory_class; u8 channel_number; u8 phy_type; - struct tsf_info *tsf_info; - struct condensed_country_string *con_coun_str; - struct bss_transition_candidate *bss_tran_can; - struct bss_termination_duration *bss_term_dur; - struct bearing *bearing; + u8 preference; /* valid if preference_present=1 */ + u16 tsf_offset; /* valid if tsf_present=1 */ + u16 beacon_int; /* valid if tsf_present=1 */ + char country[2]; /* valid if country_present=1 */ + u8 rm_capab[5]; /* valid if rm_capab_present=1 */ + u16 bearing; /* valid if bearing_present=1 */ + u16 rel_height; /* valid if bearing_present=1 */ + u32 distance; /* valid if bearing_present=1 */ + u64 bss_term_tsf; /* valid if bss_term_present=1 */ + u16 bss_term_dur; /* valid if bss_term_present=1 */ + unsigned int preference_present:1; + unsigned int tsf_present:1; + unsigned int country_present:1; + unsigned int rm_capab_present:1; + unsigned int bearing_present:1; + unsigned int bss_term_present:1; struct measurement_pilot *meas_pilot; - struct rrm_enabled_capabilities *rrm_cap; struct multiple_bssid *mul_bssid; + int freq; }; @@ -69,11 +55,23 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); -void wnm_scan_response(struct wpa_supplicant *wpa_s, - struct wpa_scan_results *scan_res); - int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, u8 query_reason); void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); + +#ifdef CONFIG_WNM + +int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail); + +#else /* CONFIG_WNM */ + +static inline int wnm_scan_process(struct wpa_supplicant *wpa_s, + int reply_on_fail) +{ + return 0; +} + +#endif /* CONFIG_WNM */ + #endif /* WNM_STA_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index fe30b414..d2face01 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -333,7 +333,7 @@ static int wpa_cli_open_connection(const char *ifname, int attach) return -1; res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - if (res < 0 || res >= flen) { + if (os_snprintf_error(flen, res)) { os_free(cfile); return -1; } @@ -448,13 +448,13 @@ static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, end = buf + buflen; res = os_snprintf(pos, end - pos, "%s", cmd); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) goto fail; pos += res; for (i = 0; i < argc; i++) { res = os_snprintf(pos, end - pos, " %s", argv[i]); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) goto fail; pos += res; } @@ -584,7 +584,7 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) if (argc == 1) { res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long SET command.\n"); return -1; } @@ -610,7 +610,8 @@ static char ** wpa_cli_complete_set(const char *str, int pos) /* global configuration parameters */ "eapol_version", "ap_scan", "disable_scan_offload", "fast_reauth", "opensc_engine_path", "pkcs11_engine_path", - "pkcs11_module_path", "pcsc_reader", "pcsc_pin", + "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", "driver_param", "dot11RSNAConfigPMKLifetime", "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout", @@ -732,7 +733,7 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0"); else res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long BSS_FLUSH command.\n"); return -1; } @@ -907,7 +908,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) return -1; } - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_REG command.\n"); return -1; } @@ -1032,7 +1033,7 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, return -1; } - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_ER_CONFIG command.\n"); return -1; } @@ -1084,14 +1085,14 @@ static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long IDENTITY command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long IDENTITY command.\n"); return -1; } @@ -1117,14 +1118,14 @@ static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSWORD command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSWORD command.\n"); return -1; } @@ -1151,14 +1152,14 @@ static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc, pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long NEW_PASSWORD command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long NEW_PASSWORD command.\n"); return -1; } @@ -1184,14 +1185,14 @@ static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PIN command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PIN command.\n"); return -1; } @@ -1216,14 +1217,14 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long OTP command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long OTP command.\n"); return -1; } @@ -1249,14 +1250,14 @@ static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long SIM command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long SIM command.\n"); return -1; } @@ -1282,14 +1283,14 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSPHRASE command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSPHRASE command.\n"); return -1; } @@ -1625,7 +1626,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", argc > 5 ? argv[5] : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -1751,6 +1752,31 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_MESH + +static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv); +} + + +static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv); +} + + +static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv); +} + +#endif /* CONFIG_MESH */ + + #ifdef CONFIG_P2P static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1914,7 +1940,7 @@ static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s", argv[0], argv[1], argv[2], argv[3]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -1962,7 +1988,7 @@ static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "P2P_SERVICE_ADD %s %s %s", argv[0], argv[1], argv[2]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -1989,7 +2015,7 @@ static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "P2P_SERVICE_DEL %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2211,7 +2237,7 @@ static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s", argv[0], argc > 1 ? argv[1] : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2232,7 +2258,7 @@ static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2379,6 +2405,41 @@ static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv); +} + + +static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv); +} + + +static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WMM_AC_STATUS"); +} + + +static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv); +} + + +static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv); +} + + static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2463,6 +2524,26 @@ static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv); +} + + +static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ERP_FLUSH"); +} + + +static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2780,6 +2861,17 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = roam to the specified BSS" }, +#ifdef CONFIG_MESH + { "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL, + cli_cmd_flag_none, + "[ifname] = Create a new mesh interface" }, + { "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL, + cli_cmd_flag_none, + "<network id> = join a mesh network (disable others)" }, + { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL, + cli_cmd_flag_none, + "<ifname> = Remove mesh group interface" }, +#endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find, cli_cmd_flag_none, @@ -2920,6 +3012,25 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, "<addr> = tear down TDLS with <addr>" }, + { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, + cli_cmd_flag_none, + "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] " + "[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] " + "= add WMM-AC traffic stream" }, + { "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL, + cli_cmd_flag_none, + "<tsid> = delete WMM-AC traffic stream" }, + { "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL, + cli_cmd_flag_none, + "= show status for Wireless Multi-Media Admission-Control" }, + { "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL, + cli_cmd_flag_none, + "<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] " + "[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching " + "with TDLS peer" }, + { "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL, + cli_cmd_flag_none, + "<addr> = disable channel switching with TDLS peer <addr>" }, { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, @@ -2952,6 +3063,18 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none, "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command" }, + { "neighbor_rep_request", + wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, + "[ssid=<SSID>] = Trigger request to AP for neighboring AP report " + "(with optional given SSID, default: current SSID)" + }, + { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none, + "= flush ERP keys" }, + { "mac_rand_scan", + wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none, + "<scan|sched|pno|all> enable=<0/1> [addr=mac-address " + "mask=mac-address-mask] = scan MAC randomization" + }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3245,6 +3368,14 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_connected = 0; wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } + } else if (str_match(pos, MESH_GROUP_STARTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, MESH_GROUP_REMOVED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, MESH_PEER_CONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, MESH_PEER_DISCONNECTED)) { + wpa_cli_exec(action_file, ctrl_ifname, pos); } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { @@ -3594,7 +3725,7 @@ static void update_ifnames(struct wpa_ctrl *ctrl) break; *end = '\0'; ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos); - if (ret > 0 && ret < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), ret)) cli_txt_list_add(&ifnames, txt); pos = end + 1; } diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp index 6bba8d21..62761764 100644 --- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -36,6 +36,7 @@ WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags) : QMainWindow(parent), app(_app) { setupUi(this); + this->setWindowFlags(Qt::Dialog); #ifdef CONFIG_NATIVE_WINDOWS fileStopServiceAction = new QAction(this); @@ -129,6 +130,7 @@ WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags) udr = NULL; tray_icon = NULL; startInTray = false; + quietMode = false; ctrl_iface = NULL; ctrl_conn = NULL; monitor_conn = NULL; @@ -233,7 +235,7 @@ void WpaGui::parse_argv() { int c; for (;;) { - c = getopt(qApp->argc(), qApp->argv(), "i:p:t"); + c = getopt(qApp->argc(), qApp->argv(), "i:p:tq"); if (c < 0) break; switch (c) { @@ -248,6 +250,9 @@ void WpaGui::parse_argv() case 't': startInTray = true; break; + case 'q': + quietMode = true; + break; } } } @@ -491,6 +496,7 @@ void WpaGui::updateStatus() textSsid->clear(); textBssid->clear(); textIpAddress->clear(); + updateTrayToolTip(tr("no status information")); #ifdef CONFIG_NATIVE_WINDOWS static bool first = true; @@ -538,6 +544,7 @@ void WpaGui::updateStatus() } else if (strcmp(start, "ssid") == 0) { ssid_updated = true; textSsid->setText(pos); + updateTrayToolTip(pos + tr(" (associated)")); } else if (strcmp(start, "ip_address") == 0) { ipaddr_updated = true; textIpAddress->setText(pos); @@ -585,8 +592,10 @@ void WpaGui::updateStatus() textStatus->clear(); if (!auth_updated) textAuthentication->clear(); - if (!ssid_updated) + if (!ssid_updated) { textSsid->clear(); + updateTrayToolTip(tr("(not-associated)")); + } if (!bssid_updated) textBssid->clear(); if (!ipaddr_updated) @@ -1270,7 +1279,6 @@ void WpaGui::createTrayIcon(bool trayOnly) QApplication::setQuitOnLastWindowClosed(false); tray_icon = new QSystemTrayIcon(this); - tray_icon->setToolTip(qAppName() + tr(" - wpa_supplicant user interface")); if (QImageReader::supportedImageFormats().contains(QByteArray("svg"))) tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg")); else @@ -1332,7 +1340,7 @@ void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec, if (!QSystemTrayIcon::supportsMessages()) return; - if (isVisible() || !tray_icon || !tray_icon->isVisible()) + if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode) return; tray_icon->showMessage(qAppName(), msg, type, sec * 1000); @@ -1407,6 +1415,13 @@ void WpaGui::showTrayStatus() } +void WpaGui::updateTrayToolTip(const QString &msg) +{ + if (tray_icon) + tray_icon->setToolTip(msg); +} + + void WpaGui::closeEvent(QCloseEvent *event) { if (eh) { diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h index 340286c4..026eacb9 100644 --- a/wpa_supplicant/wpa_gui-qt4/wpagui.h +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h @@ -70,6 +70,7 @@ public slots: virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec, const QString &msg); virtual void showTrayStatus(); + virtual void updateTrayToolTip(const QString &msg); virtual void wpsDialog(); virtual void peersDialog(); virtual void tabChanged(int index); @@ -116,6 +117,7 @@ private: void createTrayIcon(bool); bool ackTrayIcon; bool startInTray; + bool quietMode; int openCtrlConnection(const char *ifname); diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index 5426177a..ac38d69d 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/wpa_supplicant/wpa_priv.c @@ -202,7 +202,9 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, if (assoc->ssid_len > 32) return; params.ssid_len = assoc->ssid_len; - params.freq = assoc->freq; + params.freq.mode = assoc->hwmode; + params.freq.freq = assoc->freq; + params.freq.channel = assoc->channel; if (assoc->wpa_ie_len) { params.wpa_ie = (u8 *) (assoc + 1); params.wpa_ie_len = assoc->wpa_ie_len; @@ -333,7 +335,7 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, msg.msg_namelen = sizeof(iface->l2_addr); if (sendmsg(iface->fd, &msg, 0) < 0) { - perror("sendmsg(l2 rx)"); + wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno)); } } @@ -465,7 +467,7 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom"); + wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno)); return; } @@ -613,7 +615,7 @@ wpa_priv_interface_init(const char *dir, const char *params) iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (iface->fd < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); wpa_priv_interface_deinit(iface); return NULL; } @@ -631,15 +633,16 @@ wpa_priv_interface_init(const char *dir, const char *params) "allow connections - assuming it was " "leftover from forced program termination"); if (unlink(iface->sock_name) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - iface->sock_name); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + iface->sock_name, strerror(errno)); goto fail; } if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("wpa-priv-iface-init: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "wpa-priv-iface-init: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -654,7 +657,7 @@ wpa_priv_interface_init(const char *dir, const char *params) } if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { - perror("chmod"); + wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno)); goto fail; } @@ -686,7 +689,8 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, msg.msg_namelen = sizeof(iface->drv_addr); if (sendmsg(iface->fd, &msg, 0) < 0) { - perror("sendmsg(wpas_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", + strerror(errno)); return -1; } @@ -901,7 +905,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, msg.msg_namelen = sizeof(iface->drv_addr); if (sendmsg(iface->fd, &msg, 0) < 0) - perror("sendmsg(wpas_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", + strerror(errno)); } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index eef3d214..e5dc43f7 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -52,6 +52,7 @@ #include "hs20_supplicant.h" #include "wnm_sta.h" #include "wpas_kay.h" +#include "mesh.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -105,9 +106,6 @@ const char *wpa_supplicant_full_license5 = "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ -struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers, - struct wpa_driver_capa *capa); - /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -134,6 +132,7 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, size_t keylen; enum wpa_alg alg; u8 seq[6] = { 0 }; + int ret; /* IBSS/WPA-None uses only one key (Group) for both receiving and * sending unicast and multicast packets. */ @@ -177,7 +176,9 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, /* TODO: should actually remember the previously used seq#, both for TX * and RX from each STA.. */ - return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); + ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); + os_memset(key, 0, sizeof(key)); + return ret; } @@ -300,11 +301,28 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; eapol_conf.external_sim = wpa_s->conf->external_sim; - eapol_conf.wps = wpa_s->key_mgmt == WPA_KEY_MGMT_WPS; + +#ifdef CONFIG_WPS + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE; + if (wpa_s->current_bss) { + struct wpabuf *ie; + ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss, + WPS_IE_VENDOR_TYPE); + if (ie) { + if (wps_is_20(ie)) + eapol_conf.wps |= + EAPOL_PEER_IS_WPS20_AP; + wpabuf_free(ie); + } + } + } +#endif /* CONFIG_WPS */ + eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); -#endif /* IEEE8021X_EAPOL */ ieee802_1x_alloc_kay_sm(wpa_s, ssid); +#endif /* IEEE8021X_EAPOL */ } @@ -393,6 +411,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) l2_packet_deinit(wpa_s->l2_br); wpa_s->l2_br = NULL; } +#ifdef CONFIG_TESTING_OPTIONS + l2_packet_deinit(wpa_s->l2_test); + wpa_s->l2_test = NULL; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { struct wpa_ssid *ssid; @@ -416,6 +438,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_tdls_deinit(wpa_s->wpa); #endif /* CONFIG_TDLS */ + wmm_ac_clear_saved_tspecs(wpa_s); pmksa_candidate_free(wpa_s->wpa); wpa_sm_deinit(wpa_s->wpa); wpa_s->wpa = NULL; @@ -465,6 +488,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->manual_sched_scan_freqs); wpa_s->manual_sched_scan_freqs = NULL; + wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL); + gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; @@ -504,6 +529,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->vendor_elem[i]); wpa_s->vendor_elem[i] = NULL; } + + wmm_ac_notify_disassoc(wpa_s); } @@ -736,6 +763,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) wpa_supplicant_start_autoscan(wpa_s); + if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED) + wmm_ac_notify_disassoc(wpa_s); + if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); @@ -845,7 +875,7 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) /* * TODO: should notify EAPOL SM about changes in opensc_engine_path, - * pkcs11_engine_path, pkcs11_module_path. + * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers. */ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* @@ -982,7 +1012,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0"); proto = WPA_PROTO_RSN; } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) && - wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 && + wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 && (ie.group_cipher & ssid->group_cipher) && (ie.pairwise_cipher & ssid->pairwise_cipher) && (ie.key_mgmt & ssid->key_mgmt)) { @@ -1000,6 +1030,40 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ssid->proto, ssid->pairwise_cipher, ssid->group_cipher, + ssid->key_mgmt); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), + bss_wpa ? " WPA" : "", + bss_rsn ? " RSN" : "", + bss_osen ? " OSEN" : ""); + if (bss_rsn) { + wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]); + if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Could not parse RSN element"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ie.pairwise_cipher, ie.group_cipher, + ie.key_mgmt); + } + } + if (bss_wpa) { + wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]); + if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Could not parse WPA element"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ie.pairwise_cipher, ie.group_cipher, + ie.key_mgmt); + } + } return -1; } else { if (ssid->proto & WPA_PROTO_OSEN) @@ -1073,6 +1137,10 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ if (0) { + } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT 802.1X with Suite B"); #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; @@ -1163,7 +1231,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { @@ -1172,7 +1240,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 4096, psk, PMK_LEN); wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } #endif /* CONFIG_NO_PBKDF2 */ #ifdef CONFIG_EXT_PASSWORD @@ -1208,7 +1277,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " "external passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } else #endif /* CONFIG_NO_PBKDF2 */ if (wpabuf_len(pw) == 2 * PMK_LEN) { @@ -1219,7 +1289,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } else { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " "PSK available"); @@ -1461,8 +1532,15 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, else rand_style = ssid->mac_addr; + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->reassoc_same_bss = 0; + if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + if (wpa_s->current_bss && wpa_s->current_bss == bss) { + wmm_ac_save_tspecs(wpa_s); + wpa_s->reassoc_same_bss = 1; + } } else if (rand_style > 0) { if (wpas_update_random_addr(wpa_s, rand_style) < 0) return; @@ -1510,6 +1588,31 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + if (ssid->mode == WPAS_MODE_MESH) { +#ifdef CONFIG_MESH + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) { + wpa_msg(wpa_s, MSG_INFO, + "Driver does not support mesh mode"); + return; + } + if (bss) + ssid->frequency = bss->freq; + if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh"); + return; + } + wpa_s->current_bss = bss; + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED + "ssid=\"%s\" id=%d", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->id); +#else /* CONFIG_MESH */ + wpa_msg(wpa_s, MSG_ERROR, + "mesh mode support not included in the build"); +#endif /* CONFIG_MESH */ + return; + } + #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), @@ -1593,7 +1696,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; wpa_s->eap_expected_failure = 0; - if (bss && !wpas_driver_bss_selection(wpa_s)) { + if (bss && + (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { #ifdef CONFIG_IEEE80211R const u8 *ie, *md = NULL; #endif /* CONFIG_IEEE80211R */ @@ -1856,8 +1960,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.fixed_bssid = 1; } - if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && - params.freq.freq == 0) { + /* Initial frequency for IBSS/mesh */ + if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) && + ssid->frequency > 0 && params.freq.freq == 0) { enum hostapd_hw_mode hw_mode; u8 channel; @@ -1906,6 +2011,23 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.psk = ssid->psk; } + if (wpa_s->conf->key_mgmt_offload) { + if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B) + params.req_key_mgmt_offload = + ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : ssid->proactive_key_caching; + else + params.req_key_mgmt_offload = 1; + + if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK || + params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) && + ssid->psk_set) + params.psk = ssid->psk; + } + params.drop_unencrypted = use_crypt; #ifdef CONFIG_IEEE80211W @@ -2050,6 +2172,7 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, { struct wpa_ssid *old_ssid; + wpas_connect_work_done(wpa_s); wpa_clear_keys(wpa_s, addr); old_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); @@ -2102,6 +2225,14 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, wpa_tdls_teardown_peers(wpa_s->wpa); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", + wpa_s->ifname); + wpa_supplicant_leave_mesh(wpa_s); + } +#endif /* CONFIG_MESH */ + if (addr) { wpa_drv_deauthenticate(wpa_s, addr, reason_code); os_memset(&event, 0, sizeof(event)); @@ -2267,12 +2398,17 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, if (ssid) { wpa_s->current_ssid = ssid; eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->connect_without_scan = + (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL; + } else { + wpa_s->connect_without_scan = NULL; } - wpa_s->connect_without_scan = NULL; + wpa_s->disconnected = 0; wpa_s->reassociate = 1; - if (wpa_supplicant_fast_associate(wpa_s) != 1) + if (wpa_s->connect_without_scan || + wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); if (ssid) @@ -2742,15 +2878,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) { - if (wpa_s->driver->send_eapol) { - const u8 *addr = wpa_drv_get_mac_addr(wpa_s); - if (addr) - os_memcpy(wpa_s->own_addr, addr, ETH_ALEN); - } else if ((!wpa_s->p2p_mgmt || - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { + if ((!wpa_s->p2p_mgmt || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { l2_packet_deinit(wpa_s->l2); wpa_s->l2 = l2_packet_init(wpa_s->ifname, wpa_drv_get_mac_addr(wpa_s), @@ -2854,12 +2984,14 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); interface_count = 0; } +#ifndef ANDROID if (!wpa_s->p2p_mgmt && wpa_supplicant_delayed_sched_scan(wpa_s, interface_count % 3, 100000)) wpa_supplicant_req_scan(wpa_s, interface_count % 3, 100000); +#endif /* ANDROID */ interface_count++; } else wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); @@ -3127,10 +3259,6 @@ void wpa_supplicant_apply_vht_overrides( { struct ieee80211_vht_capabilities *vhtcaps; struct ieee80211_vht_capabilities *vhtcaps_mask; -#ifdef CONFIG_HT_OVERRIDES - int max_ampdu; - const u32 max_ampdu_mask = VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX; -#endif /* CONFIG_HT_OVERRIDES */ if (!ssid) return; @@ -3148,9 +3276,12 @@ void wpa_supplicant_apply_vht_overrides( #ifdef CONFIG_HT_OVERRIDES /* if max ampdu is <= 3, we have to make the HT cap the same */ - if (ssid->vht_capa_mask & max_ampdu_mask) { - max_ampdu = (ssid->vht_capa & max_ampdu_mask) >> - find_first_bit(max_ampdu_mask); + if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) { + int max_ampdu; + + max_ampdu = (ssid->vht_capa & + VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >> + VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT; max_ampdu = max_ampdu < 3 ? max_ampdu : 3; wpa_set_ampdu_factor(wpa_s, @@ -3261,7 +3392,7 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, - struct wpa_driver_capa *capa) + const struct wpa_driver_capa *capa) { struct wowlan_triggers *triggers; int ret = 0; @@ -3430,6 +3561,11 @@ void radio_work_check_next(struct wpa_supplicant *wpa_s) if (dl_list_empty(&radio->work)) return; + if (wpa_s->ext_work_in_progress) { + wpa_printf(MSG_DEBUG, + "External radio work in progress - delay start of pending item"); + return; + } eloop_cancel_timeout(radio_start_next_work, radio, NULL); eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL); } @@ -3585,6 +3721,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { struct wpa_driver_capa capa; + int capa_res; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, @@ -3714,10 +3851,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); - if (wpa_drv_get_capa(wpa_s, &capa) == 0) { + capa_res = wpa_drv_get_capa(wpa_s, &capa); + if (capa_res == 0) { wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; wpa_s->drv_enc = capa.enc; + wpa_s->drv_smps_modes = capa.smps_modes; + wpa_s->drv_rrm_flags = capa.rrm_flags; wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; @@ -3730,6 +3870,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->extended_capa_len = capa.extended_capa_len; wpa_s->num_multichan_concurrent = capa.num_multichan_concurrent; + wpa_s->wmm_ac_supported = capa.wmm_ac_supported; + + if (capa.mac_addr_rand_scan_supported) + wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN; + if (wpa_s->sched_scan_supported && + capa.mac_addr_rand_sched_scan_supported) + wpa_s->mac_addr_rand_supported |= + (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO); } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; @@ -3804,7 +3952,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, * Note: We don't restore/remove the triggers on shutdown (it doesn't * have effect anyway when the interface is down). */ - if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0) + if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) return -1; #ifdef CONFIG_EAP_PROXY @@ -3828,6 +3976,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpas_init_ext_pw(wpa_s) < 0) return -1; + wpas_rrm_reset(wpa_s); + return 0; } @@ -3835,6 +3985,26 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, int notify, int terminate) { + struct wpa_global *global = wpa_s->global; + struct wpa_supplicant *iface, *prev; + + if (wpa_s == wpa_s->parent) + wpas_p2p_group_remove(wpa_s, "*"); + + iface = global->ifaces; + while (iface) { + if (iface == wpa_s || iface->parent != wpa_s) { + iface = iface->next; + continue; + } + wpa_printf(MSG_DEBUG, + "Remove remaining child interface %s from parent %s", + iface->ifname, wpa_s->ifname); + prev = iface; + iface = iface->next; + wpa_supplicant_remove_iface(global, prev, terminate); + } + wpa_s->disconnected = 1; if (wpa_s->drv_priv) { wpa_supplicant_deauthenticate(wpa_s, @@ -3864,6 +4034,13 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_s->ctrl_iface = NULL; } +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); + wpa_s->ifmsh = NULL; + } +#endif /* CONFIG_MESH */ + if (wpa_s->conf != NULL) { wpa_config_free(wpa_s->conf); wpa_s->conf = NULL; @@ -3923,14 +4100,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, return NULL; } - /* Notify the control interfaces about new iface */ - if (wpas_notify_iface_added(wpa_s)) { - wpa_supplicant_deinit_iface(wpa_s, 1, 0); - return NULL; - } + if (iface->p2p_mgmt == 0) { + /* Notify the control interfaces about new iface */ + if (wpas_notify_iface_added(wpa_s)) { + wpa_supplicant_deinit_iface(wpa_s, 1, 0); + return NULL; + } - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) - wpas_notify_network_added(wpa_s, ssid); + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpas_notify_network_added(wpa_s, ssid); + } wpa_s->next = global->ifaces; global->ifaces = wpa_s; @@ -3938,6 +4117,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); +#ifdef CONFIG_P2P + if (wpa_s->global->p2p == NULL && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) { + wpa_printf(MSG_INFO, + "P2P: Failed to enable P2P Device interface"); + /* Try to continue without. P2P will be disabled. */ + } +#endif /* CONFIG_P2P */ + return wpa_s; } @@ -3958,6 +4147,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, int terminate) { struct wpa_supplicant *prev; +#ifdef CONFIG_MESH + unsigned int mesh_if_created = wpa_s->mesh_if_created; + char *ifname = NULL; +#endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ prev = global->ifaces; @@ -3973,12 +4166,30 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); +#ifdef CONFIG_MESH + if (mesh_if_created) { + ifname = os_strdup(wpa_s->ifname); + if (ifname == NULL) { + wpa_dbg(wpa_s, MSG_ERROR, + "mesh: Failed to malloc ifname"); + return -1; + } + } +#endif /* CONFIG_MESH */ + if (global->p2p_group_formation == wpa_s) global->p2p_group_formation = NULL; if (global->p2p_invite_group == wpa_s) global->p2p_invite_group = NULL; wpa_supplicant_deinit_iface(wpa_s, 1, terminate); +#ifdef CONFIG_MESH + if (mesh_if_created) { + wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname); + os_free(ifname); + } +#endif /* CONFIG_MESH */ + return 0; } @@ -4063,7 +4274,10 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb); #endif /* CONFIG_NO_WPA_MSG */ - wpa_debug_open_file(params->wpa_debug_file_path); + if (params->wpa_debug_file_path) + wpa_debug_open_file(params->wpa_debug_file_path); + else + wpa_debug_setup_stdout(); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); if (params->wpa_debug_tracing) { @@ -4141,7 +4355,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_supplicant_deinit(global); return NULL; } - global->drv_priv = os_zalloc(global->drv_count * sizeof(void *)); + global->drv_priv = os_calloc(global->drv_count, sizeof(void *)); if (global->drv_priv == NULL) { wpa_supplicant_deinit(global); return NULL; @@ -4279,7 +4493,7 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) } -static void add_freq(int *freqs, int *num_freqs, int freq) +void add_freq(int *freqs, int *num_freqs, int freq) { int i; @@ -4300,7 +4514,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) int *freqs; int num_freqs = 0; - freqs = os_zalloc(sizeof(int) * (max_freqs + 1)); + freqs = os_calloc(max_freqs + 1, sizeof(int)); if (freqs == NULL) return NULL; @@ -4681,6 +4895,7 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, void wpas_request_connection(struct wpa_supplicant *wpa_s) { wpa_s->normal_scans = 0; + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->extra_blacklist_count = 0; wpa_s->disconnected = 0; @@ -4785,3 +5000,261 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, return num; } + + +static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) +{ + struct rrm_data *rrm = data; + + if (!rrm->notify_neighbor_rep) { + wpa_printf(MSG_ERROR, + "RRM: Unexpected neighbor report timeout"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); + rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL); + + rrm->notify_neighbor_rep = NULL; + rrm->neighbor_rep_cb_ctx = NULL; +} + + +/* + * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant + * @wpa_s: Pointer to wpa_supplicant + */ +void wpas_rrm_reset(struct wpa_supplicant *wpa_s) +{ + wpa_s->rrm.rrm_used = 0; + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + if (wpa_s->rrm.notify_neighbor_rep) + wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); + wpa_s->rrm.next_neighbor_rep_token = 1; +} + + +/* + * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report + * @wpa_s: Pointer to wpa_supplicant + * @report: Neighbor report buffer, prefixed by a 1-byte dialog token + * @report_len: Length of neighbor report buffer + */ +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len) +{ + struct wpabuf *neighbor_rep; + + wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); + if (report_len < 1) + return; + + if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { + wpa_printf(MSG_DEBUG, + "RRM: Discarding neighbor report with token %d (expected %d)", + report[0], wpa_s->rrm.next_neighbor_rep_token - 1); + return; + } + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + + if (!wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report"); + return; + } + + /* skipping the first byte, which is only an id (dialog token) */ + neighbor_rep = wpabuf_alloc(report_len - 1); + if (neighbor_rep == NULL) + return; + wpabuf_put_data(neighbor_rep, report + 1, report_len - 1); + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", + report[0]); + wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, + neighbor_rep); + wpa_s->rrm.notify_neighbor_rep = NULL; + wpa_s->rrm.neighbor_rep_cb_ctx = NULL; +} + + +/** + * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP + * @wpa_s: Pointer to wpa_supplicant + * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE + * is sent in the request. + * @cb: Callback function to be called once the requested report arrives, or + * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. + * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's + * the requester's responsibility to free it. + * In the latter case NULL will be sent in 'neighbor_rep'. + * @cb_ctx: Context value to send the callback function + * Returns: 0 in case of success, negative error code otherwise + * + * In case there is a previous request which has not been answered yet, the + * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. + * Request must contain a callback function. + */ +int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + void (*cb)(void *ctx, + struct wpabuf *neighbor_rep), + void *cb_ctx) +{ + struct wpabuf *buf; + const u8 *rrm_ie; + + if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) { + wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM."); + return -ENOTCONN; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection."); + return -EOPNOTSUPP; + } + + rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || + !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_DEBUG, + "RRM: No network support for Neighbor Report."); + return -EOPNOTSUPP; + } + + if (!cb) { + wpa_printf(MSG_DEBUG, + "RRM: Neighbor Report request must provide a callback."); + return -EINVAL; + } + + /* Refuse if there's a live request */ + if (wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_DEBUG, + "RRM: Currently handling previous Neighbor Report."); + return -EBUSY; + } + + /* 3 = action category + action code + dialog token */ + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to allocate Neighbor Report Request"); + return -ENOMEM; + } + + wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d", + (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), + wpa_s->rrm.next_neighbor_rep_token); + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); + wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); + if (ssid) { + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, ssid->ssid_len); + wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); + } + + wpa_s->rrm.next_neighbor_rep_token++; + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to send Neighbor Report Request"); + wpabuf_free(buf); + return -ECANCELED; + } + + wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; + wpa_s->rrm.notify_neighbor_rep = cb; + eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, + wpas_rrm_neighbor_rep_timeout_handler, + &wpa_s->rrm, NULL); + + wpabuf_free(buf); + return 0; +} + + +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi) +{ + struct wpabuf *buf; + const struct rrm_link_measurement_request *req; + struct rrm_link_measurement_report report; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not RRM network"); + return; + } + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) { + wpa_printf(MSG_INFO, + "RRM: Measurement report failed. TX power insertion not supported"); + return; + } + + req = (const struct rrm_link_measurement_request *) frame; + if (len < sizeof(*req)) { + wpa_printf(MSG_INFO, + "RRM: Link measurement report failed. Request too short"); + return; + } + + os_memset(&report, 0, sizeof(report)); + report.tpc.eid = WLAN_EID_TPC_REPORT; + report.tpc.len = 2; + report.rsni = 255; /* 255 indicates that RSNI is not available */ + report.dialog_token = req->dialog_token; + + /* + * It's possible to estimate RCPI based on RSSI in dBm. This + * calculation will not reflect the correct value for high rates, + * but it's good enough for Action frames which are transmitted + * with up to 24 Mbps rates. + */ + if (!rssi) + report.rcpi = 255; /* not available */ + else if (rssi < -110) + report.rcpi = 0; + else if (rssi > 0) + report.rcpi = 220; + else + report.rcpi = (rssi + 110) * 2; + + /* action_category + action_code */ + buf = wpabuf_alloc(2 + sizeof(report)); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Buffer allocation failed"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); + wpabuf_put_data(buf, &report, sizeof(report)); + wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:", + wpabuf_head(buf), wpabuf_len(buf)); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Send action failed"); + } + wpabuf_free(buf); +} diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 89da0daf..e78c0dd9 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -114,6 +114,19 @@ eapol_version=1 # networks are found, a new IBSS or AP mode network is created. ap_scan=1 +# MPM residency +# By default, wpa_supplicant implements the mesh peering manager (MPM) for an +# open mesh. However, if the driver can implement the MPM, you may set this to +# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is +# always used. +# 0: MPM lives in the driver +# 1: wpa_supplicant provides an MPM which handles peering (default) +#user_mpm=1 + +# Maximum number of peer links (0-255; default: 99) +# Maximum number of mesh peering currently maintained by the STA. +#max_peer_links=99 + # EAP fast re-authentication # By default, fast re-authentication is enabled for all EAP methods that # support it. This variable can be used to disable fast re-authentication. @@ -132,6 +145,16 @@ fast_reauth=1 # configure the path to the pkcs11 module required by the pkcs11 engine #pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so +# OpenSSL cipher string +# +# This is an OpenSSL specific configuration option for configuring the default +# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default. +# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation +# on cipher suite configuration. This is applicable only if wpa_supplicant is +# built to use OpenSSL. +#openssl_ciphers=DEFAULT:!EXP:!LOW + + # Dynamic EAP methods # If EAP methods were built dynamically as shared object files, they need to be # loaded here before being used in the network blocks. By default, EAP methods @@ -932,6 +955,12 @@ fast_reauth=1 # 1 = try to use OCSP stapling, but not require response # 2 = require valid OCSP stapling response # +# openssl_ciphers: OpenSSL specific cipher configuration +# This can be used to override the global openssl_ciphers configuration +# parameter (see above). +# +# erp: Whether EAP Re-authentication Protocol (ERP) is enabled +# # EAP-FAST variables: # pac_file: File path for the PAC entries. wpa_supplicant will need to be able # to create this file and write updates to it when PAC is being @@ -1310,6 +1339,23 @@ network={ psk="secret passphrase" } +# open mesh network +network={ + ssid="test mesh" + mode=5 + frequency=2437 + key_mgmt=NONE +} + +# secure (SAE + AMPE) network +network={ + ssid="secure mesh" + mode=5 + frequency=2437 + key_mgmt=SAE + psk="very secret passphrase" +} + # Catch all example that allows more or less all configuration modes network={ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ae9dddde..c541ccb4 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -15,6 +15,7 @@ #include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "config_ssid.h" +#include "wmm_ac.h" extern const char *wpa_supplicant_version; extern const char *wpa_supplicant_license; @@ -273,6 +274,7 @@ struct wpa_global { } conc_pref; unsigned int p2p_per_sta_psk:1; unsigned int p2p_fail_on_wps_complete:1; + unsigned int p2p_24ghz_social_channels:1; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; @@ -376,6 +378,31 @@ struct wpa_used_freq_data { unsigned int flags; }; +#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */ + +/* + * struct rrm_data - Data used for managing RRM features + */ +struct rrm_data { + /* rrm_used - indication regarding the current connection */ + unsigned int rrm_used:1; + + /* + * notify_neighbor_rep - Callback for notifying report requester + */ + void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep); + + /* + * neighbor_rep_cb_ctx - Callback context + * Received in the callback registration, and sent to the callback + * function as a parameter. + */ + void *neighbor_rep_cb_ctx; + + /* next_neighbor_rep_token - Next request's dialog token */ + u8 next_neighbor_rep_token; +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -417,6 +444,7 @@ struct wpa_supplicant { u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ + int reassoc_same_bss; /* reassociating to the same bss */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; @@ -573,8 +601,10 @@ struct wpa_supplicant { int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; - unsigned int drv_flags; + u64 drv_flags; unsigned int drv_enc; + unsigned int drv_smps_modes; + unsigned int drv_rrm_flags; /* * A bitmap of supported protocols for probe response offload. See @@ -646,6 +676,9 @@ struct wpa_supplicant { * SA Query transaction identifiers */ struct os_reltime sa_query_start; struct os_reltime last_unprot_disconnect; + enum { HT_SEC_CHAN_UNKNOWN, + HT_SEC_CHAN_ABOVE, + HT_SEC_CHAN_BELOW } ht_sec_chan; u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; @@ -653,6 +686,7 @@ struct wpa_supplicant { struct sae_data sae; struct wpabuf *sae_token; int sae_group_index; + unsigned int sae_pmksa_caching:1; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ @@ -664,6 +698,13 @@ struct wpa_supplicant { void *ap_configured_cb_data; #endif /* CONFIG_AP */ + struct hostapd_iface *ifmsh; +#ifdef CONFIG_MESH + struct mesh_rsn *mesh_rsn; + int mesh_if_idx; + unsigned int mesh_if_created:1; +#endif /* CONFIG_MESH */ + unsigned int off_channel_freq; struct wpabuf *pending_action_tx; u8 pending_action_src[ETH_ALEN]; @@ -685,6 +726,7 @@ struct wpa_supplicant { int p2p_mgmt; #ifdef CONFIG_P2P + struct wpa_supplicant *p2p_dev; struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; @@ -755,6 +797,7 @@ struct wpa_supplicant { unsigned int p2p_go_vht:1; unsigned int user_initiated_pd:1; unsigned int p2p_go_group_formation_completed:1; + unsigned int group_formation_reported:1; unsigned int waiting_presence_resp; int p2p_first_connection_timeout; unsigned int p2p_nfc_tag_enabled:1; @@ -775,6 +818,10 @@ struct wpa_supplicant { * formation */ u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; u8 p2p_ip_addr_info[3 * 4]; + + /* group common frequencies */ + int *p2p_group_common_freqs; + unsigned int p2p_group_common_freqs_num; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; @@ -811,6 +858,7 @@ struct wpa_supplicant { unsigned int auto_network_select:1; unsigned int fetch_all_anqp:1; unsigned int fetch_osu_info:1; + unsigned int fetch_osu_waiting_scan:1; unsigned int fetch_osu_icon_in_progress:1; struct wpa_bss *interworking_gas_bss; unsigned int osu_icon_id; @@ -845,6 +893,23 @@ struct wpa_supplicant { unsigned int no_keep_alive:1; unsigned int ext_mgmt_frame_handling:1; + unsigned int ext_eapol_frame_io:1; + unsigned int wmm_ac_supported:1; + unsigned int ext_work_in_progress:1; + +#define MAC_ADDR_RAND_SCAN BIT(0) +#define MAC_ADDR_RAND_SCHED_SCAN BIT(1) +#define MAC_ADDR_RAND_PNO BIT(2) +#define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \ + MAC_ADDR_RAND_SCHED_SCAN | \ + MAC_ADDR_RAND_PNO) + unsigned int mac_addr_rand_supported; + unsigned int mac_addr_rand_enable; + + /* MAC Address followed by mask (2 * ETH_ALEN) */ + u8 *mac_addr_scan; + u8 *mac_addr_sched_scan; + u8 *mac_addr_pno; #ifdef CONFIG_WNM u8 wnm_dialog_token; @@ -852,9 +917,10 @@ struct wpa_supplicant { u8 wnm_num_neighbor_report; u8 wnm_mode; u16 wnm_dissoc_timer; - u8 wnm_validity_interval; u8 wnm_bss_termination_duration[12]; struct neighbor_report *wnm_neighbor_report_elements; + struct os_reltime wnm_cand_valid_until; + u8 wnm_cand_from_bss[ETH_ALEN]; #endif /* CONFIG_WNM */ #ifdef CONFIG_TESTING_GET_GTK @@ -868,6 +934,20 @@ struct wpa_supplicant { unsigned int ext_work_id; struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES]; + +#ifdef CONFIG_TESTING_OPTIONS + struct l2_packet_data *l2_test; + unsigned int extra_roc_dur; +#endif /* CONFIG_TESTING_OPTIONS */ + + struct wmm_ac_assoc_data *wmm_ac_assoc_info; + struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT]; + struct wmm_ac_addts_request *addts_request; + u8 wmm_ac_last_dialog_token; + struct wmm_tspec_element *last_tspecs; + u8 last_tspecs_count; + + struct rrm_data rrm; }; @@ -964,6 +1044,20 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s); int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); +void add_freq(int *freqs, int *num_freqs, int freq); + +void wpas_rrm_reset(struct wpa_supplicant *wpa_s); +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len); +int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + void (*cb)(void *ctx, + struct wpabuf *neighbor_rep), + void *cb_ctx); +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response @@ -991,8 +1085,6 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, struct wpa_ssid **selected_ssid); -int ht_supported(const struct hostapd_hw_modes *mode); -int vht_supported(const struct hostapd_hw_modes *mode); /* eap_register.c */ int eap_register_methods(void); diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 38279b1f..3098058b 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -96,11 +96,26 @@ static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest, u16 proto, const u8 *buf, size_t len) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(dest), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (wpa_s->l2) { return l2_packet_send(wpa_s->l2, dest, proto, buf, len); } - return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); + return -1; } #endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */ @@ -528,7 +543,44 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action, const u8 *ies, size_t ies_len) { struct wpa_supplicant *wpa_s = ctx; - return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len); + int ret; + u8 *data, *pos; + size_t data_len; + + if (action != 1) { + wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d", + action); + return -1; + } + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + data_len = 2 + 2 * ETH_ALEN + ies_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + *pos++ = 0x06; /* FT Action category */ + *pos++ = action; + os_memcpy(pos, wpa_s->own_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ies, ies_len); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, + wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, + data, data_len, 0); + os_free(data); + + return ret; } @@ -557,12 +609,14 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) #ifdef CONFIG_TDLS static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, - int *tdls_ext_setup) + int *tdls_ext_setup, + int *tdls_chan_switch) { struct wpa_supplicant *wpa_s = ctx; *tdls_supported = 0; *tdls_ext_setup = 0; + *tdls_chan_switch = 0; if (!wpa_s->drv_capa_known) return -1; @@ -573,6 +627,9 @@ static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP) *tdls_ext_setup = 1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH) + *tdls_chan_switch = 1; + return 0; } @@ -640,6 +697,25 @@ static int wpa_supplicant_tdls_peer_addset( return wpa_drv_sta_add(wpa_s, ¶ms); } + +static int wpa_supplicant_tdls_enable_channel_switch( + void *ctx, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class, + params); +} + + +static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_tdls_disable_channel_switch(wpa_s, addr); +} + #endif /* CONFIG_TDLS */ #endif /* CONFIG_NO_WPA */ @@ -748,7 +824,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx, len = os_snprintf(buf, buflen, WPA_CTRL_REQ "%s-%d:%s needed for SSID ", field_name, ssid->id, txt); - if (len < 0 || (size_t) len >= buflen) { + if (os_snprintf_error(buflen, len)) { os_free(buf); return; } @@ -866,6 +942,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path; ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers; ctx->wps = wpa_s->wps; ctx->eap_param_needed = wpa_supplicant_eap_param_needed; ctx->port_cb = wpa_supplicant_port_cb; @@ -899,6 +976,19 @@ static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek, #endif /* CONFIG_NO_WPA */ +static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, + size_t pmk_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->conf->key_mgmt_offload) + return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, + NULL, 0, pmk, pmk_len); + else + return 0; +} + + int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_WPA @@ -938,8 +1028,13 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt; ctx->tdls_oper = wpa_supplicant_tdls_oper; ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset; + ctx->tdls_enable_channel_switch = + wpa_supplicant_tdls_enable_channel_switch; + ctx->tdls_disable_channel_switch = + wpa_supplicant_tdls_disable_channel_switch; #endif /* CONFIG_TDLS */ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; + ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 40a5c696..b1266c62 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -286,7 +286,9 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, /* compare security parameters */ if (ssid->auth_alg != new_ssid->auth_alg || ssid->key_mgmt != new_ssid->key_mgmt || - ssid->group_cipher != new_ssid->group_cipher) + (ssid->group_cipher != new_ssid->group_cipher && + !(ssid->group_cipher & new_ssid->group_cipher & + WPA_CIPHER_CCMP))) continue; /* @@ -337,6 +339,8 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, /* Remove the duplicated older network entry. */ wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); wpas_notify_network_removed(wpa_s, ssid); + if (wpa_s->current_ssid == ssid) + wpa_s->current_ssid = NULL; wpa_config_remove_network(wpa_s->conf, ssid->id); } } @@ -471,6 +475,11 @@ static int wpa_supplicant_wps_cred(void *ctx, break; case WPS_ENCR_AES: ssid->pairwise_cipher = WPA_CIPHER_CCMP; + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP; + ssid->group_cipher |= WPA_CIPHER_GCMP; + } break; } @@ -1082,6 +1091,14 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group) { struct wpa_ssid *ssid; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid); if (ssid == NULL) @@ -1122,6 +1139,13 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, unsigned int rpin = 0; char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10]; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); if (bssid && is_zero_ether_addr(bssid)) bssid = NULL; @@ -1235,6 +1259,13 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, char *pos, *end; int res; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ if (!pin) return -1; wpas_clear_wps(wpa_s); @@ -1245,7 +1276,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, pos = val; end = pos + sizeof(val); res = os_snprintf(pos, end - pos, "\"pin=%s", pin); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; if (settings) { @@ -1253,12 +1284,12 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, "new_encr=%s new_key=%s", settings->ssid_hex, settings->auth, settings->encr, settings->key_hex); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; } res = os_snprintf(pos, end - pos, "\""); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; if (wpa_config_set(ssid, "phase1", val, 0) < 0) return -1; @@ -1309,7 +1340,7 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); - if (len > 0 && len < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), len)) wpa_printf(MSG_INFO, "%s", txt); } @@ -1697,6 +1728,10 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, uuid = wps_get_uuid_e(ie); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", uuid, UUID_LEN); + if (os_memcmp(selected->bssid, bss->bssid, ETH_ALEN) == 0) { + wpabuf_free(ie); + continue; + } if (sel_uuid == NULL || uuid == NULL || os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ @@ -1800,13 +1835,12 @@ int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) } -int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) +void wpas_wps_er_stop(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; #endif /* CONFIG_WPS_ER */ - return 0; } @@ -1907,6 +1941,7 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, u8 addr[ETH_ALEN], *use_addr = NULL; struct wpa_ssid *ssid; struct wps_credential cred; + int ret; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; @@ -1920,7 +1955,9 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, if (wpas_wps_network_to_cred(ssid, &cred) < 0) return -1; - return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + os_memset(&cred, 0, sizeof(cred)); + return ret; } diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 2263512c..683bd50e 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -47,7 +47,7 @@ int wpas_wps_searching(struct wpa_supplicant *wpa_s); int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, char *end); int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter); -int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); +void wpas_wps_er_stop(struct wpa_supplicant *wpa_s); int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, const char *uuid, const char *pin); int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid); |
