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) - 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 +#include +#endif /* CONFIG_TESTING_OPTIONS */ + #include #include #include @@ -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=,,, + * ,[,] + */ + 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: */ + + 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= + # 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 +#include + +#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 + * Copyright (c) 2002-2014, Jouni Malinen * * 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 + * Copyright (c) 2002-2014, Jouni Malinen * * 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 +#include + +#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; - - /* - * 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); + /* send disassociation frame after time-out */ + set_disassoc_timer(hapd, sta, disassoc_timer); + } + + 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 + * + * 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 + * Copyright (c) 2003-2014, Jouni Malinen * * 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; + + /** + * 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; - int freq; - int channel; - /* for HT */ - int ht_enabled; - int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, - * secondary channel below primary, 1 = HT40 - * enabled, secondary channel above primary */ + /** + * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..) + */ + enum hostapd_hw_mode mode; - /* for VHT */ + /** + * freq - Primary channel center frequency in MHz + */ + int freq; + + /** + * channel - Channel number + */ + int channel; + + /** + * ht_enabled - Whether HT is enabled + */ + int ht_enabled; + + /** + * 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 { @@ -894,6 +1010,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 */ @@ -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 @@ -1604,27 +1846,6 @@ struct wpa_driver_ops { * a MAC address. */ 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 @@ -1699,22 +1920,6 @@ struct wpa_driver_ops { int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies, 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 @@ -2546,6 +2751,45 @@ struct wpa_driver_ops { int (*set_qos_map)(void *priv, const u8 *qos_map_set, 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 @@ -2750,6 +2994,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 @@ -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); }; @@ -3210,11 +3537,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 */ @@ -3254,13 +3576,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 * @@ -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, }; @@ -3617,10 +3943,63 @@ 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; /** @@ -3829,15 +4208,6 @@ union wpa_event_data { u8 addr[ETH_ALEN]; } 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 */ @@ -3921,17 +4291,6 @@ union wpa_event_data { size_t num_ssids; } 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 */ @@ -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<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<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 - * Copyright (c) 2004, Video54 Technologies - * Copyright (c) 2004-2007, Jouni Malinen - * - * 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 - -#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 -#ifdef WME_NUM_AC -/* Assume this is built against BSD branch of madwifi driver. */ -#define MADWIFI_BSD -#include -#endif /* WME_NUM_AC */ -#include -#include - -#ifdef CONFIG_WPS -#ifdef IEEE80211_IOCTL_FILTERFRAME -#include - -#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<wpa_pairwise & WPA_CIPHER_TKIP) - v |= 1<wpa_pairwise & WPA_CIPHER_NONE) - v |= 1<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 #include -#include #include #include #include -#include #include #ifdef CONFIG_LIBNL3_ROUTE #include #endif /* CONFIG_LIBNL3_ROUTE */ #include #include -#include #include -#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" +#include "driver_nl80211.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 - -#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); + + 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); } } -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 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,6 +1834,57 @@ 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_mgmt_subscribe_mesh(struct i802_bss *bss) +{ + int ret = 0; + + if (nl80211_alloc_mgmt_handle(bss)) + return -1; + + wpa_printf(MSG_DEBUG, + "nl80211: Subscribe to mgmt frames with mesh handle %p", + bss->nl_mgmt); + + /* 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; + + /* 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; @@ -4675,29 +1893,16 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) static int nl80211_register_spurious_class3(struct i802_bss *bss) { - struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; - int ret = -1; + int ret; - msg = nlmsg_alloc(); - if (!msg) - return -1; - - nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME); - - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex); - - ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL); - msg = NULL; + 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; + int ret; - 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); - - 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); + 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_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); - - 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); + 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; + } - 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; - - 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) + + 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; - - 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; + } 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); + 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; - 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); + 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,426 +3909,17 @@ 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; -} - - static int nl80211_setup_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -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; + int ret; - msg = nlmsg_alloc(); - if (!msg) - return -1; - - 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); + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + return -1; - if (params->disable_ht) - NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT); + 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)) + 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 (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); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT); if (!msg) return -1; - wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT); - 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(); + wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", + drv->ifindex); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE); if (!msg) return -1; - wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)", - drv->ifindex); - nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE); - 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); + 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; + } - NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname)); - - return send_and_recv_msgs(drv, msg, get_sta_handler, data); - nla_put_failure: - nlmsg_free(msg); - return -ENOBUFS; + 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; + } 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; } - 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; - -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; - } + else + wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data); - 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,158 +6966,75 @@ 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; + +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 nl_msg *msg; + int ret = -ENOBUFS; + + 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: 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; + } + + return send_and_recv_msgs(drv, msg, NULL, NULL); +} + + +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 nl_msg *msg; + + 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; + } + + return send_and_recv_msgs(drv, msg, NULL, NULL); } #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) -{ - 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; -} - - -static 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"); -} - - -static int android_pno_stop(struct i802_bss *bss) -{ - return android_priv_cmd(bss, "PNOFORCE 0"); -} - -#endif /* ANDROID */ - - static int driver_nl80211_set_key(const char *ifname, void *priv, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, @@ -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 + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg + * 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 + * Copyright (c) 2007, Johannes Berg + * 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 +#include +#include +#include +#include +#include + +#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 + * Copyright (c) 2007, Johannes Berg + * 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 + +#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 + * Copyright (c) 2007, Johannes Berg + * 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 + +#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 + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg + * 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 +#include + +#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 + * Copyright (c) 2007, Johannes Berg + * 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 + +#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 - * - * 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 -#endif /* CONFIG_NATIVE_WINDOWS */ - -#include "utils/includes.h" - -#ifndef CONFIG_NATIVE_WINDOWS -#include -#include -#include -#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 */ - 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 + * + * 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 +#include #include -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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 }; /** @@ -267,6 +274,14 @@ struct eap_config { * Usually, path to opensc-pkcs11.so. */ 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 * @@ -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); - pos += 2; - if (pos + pac->pac_info_len > end) + if (2 > end - pos) goto parse_fail; + val = WPA_GET_BE16(pos); + pos += 2; + 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 + * Copyright (c) 2004-2014, Jouni Malinen * * 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 + * Copyright (c) 2002-2014, Jouni Malinen * * 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 */ @@ -209,6 +211,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 * @@ -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 #include #include +#include #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; - p2p_set_state(p2p, P2P_SEARCH); - 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; - } + int found; - if (p2p_start_sd(p2p, dev) == 0) - 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; + 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 == 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; + } + } + + /* + * 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 == 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 @@ -948,6 +948,13 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, unsigned int num_req_dev_types, const u8 *req_dev_types, 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 @@ -251,6 +251,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 * @@ -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 = @@ -1801,6 +1819,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 @@ -158,6 +158,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 * @@ -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; + /* bit 38 - TDLS Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x40); +} - if (ies == NULL) - return 0; - if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) - return 0; - - if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) - return 0; - - /* bit 38 - TDLS Prohibited */ - return !!(elems.ext_capab[2 + 4] & 0x40); +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 +#include #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,11 +198,24 @@ endif ifdef CONFIG_IEEE80211R L_CFLAGS += -DCONFIG_IEEE80211R OBJS += src/rsn_supp/wpa_ft.c -NEED_80211_COMMON=y NEED_SHA256=y NEED_AES_OMAC1=y endif +ifdef CONFIG_MESH +NEED_80211_COMMON=y +NEED_SHA256=y +NEED_AES_SIV=y +NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +L_CFLAGS += -DCONFIG_MESH +OBJS += mesh.c +OBJS += mesh_mpm.c +OBJS += mesh_rsn.c +endif + ifdef CONFIG_SAE L_CFLAGS += -DCONFIG_SAE OBJS += src/common/sae.c @@ -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,11 +201,24 @@ endif ifdef CONFIG_IEEE80211R CFLAGS += -DCONFIG_IEEE80211R OBJS += ../src/rsn_supp/wpa_ft.o -NEED_80211_COMMON=y 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 CFLAGS += -DCONFIG_SAE OBJS += ../src/common/sae.o @@ -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" @@ -516,6 +519,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 * @@ -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 +#include +#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 []" */ 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: */ + + 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)) + if (os_snprintf_error(sizeof(array_type), err)) 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)) - 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,21 +1603,25 @@ 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; - msg = dbus_message_new_signal(wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "ServiceDiscoveryResponse"); - if (msg == 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)) - goto error; + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_P2PDEVICE, + "ServiceDiscoveryResponse"); + if (msg == NULL) + return; os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" @@ -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; + &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); - if (properties) { - if (!wpa_dbus_get_object_properties( - iface, pgrp_obj_path, - WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)) - goto err; - } - - dbus_connection_send(iface->con, msg, NULL); - - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -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; + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); - dbus_connection_send(iface->con, msg, NULL); - - dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -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; - - reply = dbus_message_new_error(message, - WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, - error); - - return reply; + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, + error); } -static const char *dont_quote[] = { +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 * 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)) { + 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 3", __func__); - return FALSE; - } - - if (!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)) { + 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 2", __func__); - return FALSE; - } - - if (!dbus_message_iter_close_container(iter, &variant_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to close message 1", __func__); + "%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)) { + &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; + reply = wpas_dbus_error_no_memory(message); } - if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, - &(blob->data), blob->len)) { - 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; - } - -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 (wpas_p2p_listen(wpa_s, (unsigned int)timeout)) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + 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, + 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)) { + 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 6", __func__); - return FALSE; - } - - if (!dbus_message_iter_close_container(iter, &variant_iter)) { - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 7", __func__); + "%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; + &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); + } - /* Changed properties dict */ - if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, - "{sv}", &dict_iter)) - goto err; - - if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - /* Invalidated properties array (empty) */ - if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, - "s", &dict_iter)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - dbus_connection_send(con, msg, NULL); - -out: dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_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; + "{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); + } - 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); - -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, network, - * and BSSID parts, if those parts exist. + * 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. + * + * 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 "/"); + 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 */ - 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; - } + /* Separate network interface prefix from the path */ + *pos++ = '\0'; - /* Cut off interface object path before "/" */ - *next_sep = '\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 -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, ""); } 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); + 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); + dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto error; - - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", + 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; + 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."); } - 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; - } - - 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 (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."); - goto out; - } - 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."); + goto out; + } + 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 @@ Enable DBus control interface. If enabled, interface definitions may be omitted. (This is only available if wpa_supplicant was built with - the CONFIG_DBUS option.)0 + the CONFIG_DBUS option.) 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); + MAC2STR(wpa_s->bssid), 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); + 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 = 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; + } - 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; - } - - /* 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); + if (bss == wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Already associated with the preferred candidate"); + return 1; + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); + 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, + 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); + } +} + + +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; } - /* Send reject response for all the failures */ -send_bss_resp_fail: - wnm_deallocate_memory(wpa_s); - if (wpa_s->wnm_reply) { - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - WNM_BSS_TM_REJECT_UNSPECIFIED, - 0, NULL); - } - 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); - wpa_s->scan_res_handler = wnm_scan_response; + 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"); + } + } + + 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, " = 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, + " = join a mesh network (disable others)" }, + { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL, + cli_cmd_flag_none, + " = 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, " = tear down TDLS with " }, + { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, + cli_cmd_flag_none, + " [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, + " = 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, + " [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, + " = disable channel switching with TDLS peer " }, { "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, " [] = Send vendor command" }, + { "neighbor_rep_request", + wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, + "[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, + " 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);