DO NOT MERGE Merge remote-tracking branch 'goog/master' into mm-wireless-internal-dev
Pull in WiFi changes from goog/master to partner branch.
Merge from d7ff03d48f825360eec2a371e3361306f2fd721b
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index ca09bae..caef362 100644
--- a/CONTRIBUTIONS
+++ b/CONTRIBUTIONS
@@ -29,6 +29,34 @@
unfortunately be accepted.
+The preferred method of submitting the contribution to the project is by
+email to the hostap mailing list:
+hostap@lists.infradead.org
+Note that the list may require subscription before accepting message
+without moderation. You can subscribe to the list at this address:
+http://lists.infradead.org/mailman/listinfo/hostap
+
+The message should contain an inlined patch against the current
+development branch (i.e., the master branch of
+git://w1.fi/hostap.git). Please make sure the software you use for
+sending the patch does not corrupt whitespace. If that cannot be fixed
+for some reason, it is better to include an attached version of the
+patch file than just send a whitespace damaged version in the message
+body.
+
+The patches should be separate logical changes rather than doing
+everything in a single patch. In other words, please keep cleanup, new
+features, and bug fixes all in their own patches. Each patch needs a
+commit log that describes the changes (what the changes fix, what
+functionality is added, why the changes are useful, etc.).
+
+Please try to follow the coding style used in the project.
+
+In general, the best way of generating a suitable formatted patch file
+is by committing the changes to a cloned git repository and using git
+format-patch. The patch can then be sent, e.g., with git send-email.
+
+
History of license and contributions terms
------------------------------------------
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 52d4cfe..a550866 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -126,6 +126,15 @@
endif
OBJS += src/utils/eloop.c
+
+ifdef CONFIG_ELOOP_POLL
+L_CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
OBJS += src/utils/common.c
OBJS += src/utils/wpa_debug.c
OBJS += src/utils/wpabuf.c
@@ -252,6 +261,22 @@
L_CFLAGS += -DCONFIG_IEEE80211AC
endif
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
include $(LOCAL_PATH)/src/drivers/drivers.mk
OBJS += $(DRV_AP_OBJS)
@@ -529,6 +554,7 @@
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
LIBS += -lssl
endif
OBJS += src/crypto/crypto_openssl.c
@@ -536,6 +562,8 @@
ifdef NEED_FIPS186_2_PRF
OBJS += src/crypto/fips_prf_openssl.c
endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_h += -lcrypto
endif
@@ -623,6 +651,8 @@
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
@@ -751,11 +781,17 @@
endif
endif
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
OBJS += src/crypto/rc4.c
endif
endif
+endif
ifdef NEED_SHA256
L_CFLAGS += -DCONFIG_SHA256
@@ -772,6 +808,17 @@
endif
ifdef NEED_SHA384
L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += src/crypto/sha384-internal.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += src/crypto/sha512-internal.c
endif
ifdef NEED_DH_GROUPS
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index e6f8c6a..af54e1e 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,41 @@
ChangeLog for hostapd
+2015-09-27 - v2.5
+ * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+ [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+ * fixed WMM Action frame parser
+ [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+ * fixed EAP-pwd server missing payload length validation
+ [http://w1.fi/security/2015-4/]
+ (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145)
+ * fixed validation of WPS and P2P NFC NDEF record payload length
+ [http://w1.fi/security/2015-5/]
+ * nl80211:
+ - fixed vendor command handling to check OUI properly
+ * fixed hlr_auc_gw build with OpenSSL
+ * hlr_auc_gw: allow Milenage RES length to be reduced
+ * disable HT for a station that does not support WMM/QoS
+ * added support for hashed password (NtHash) in EAP-pwd server
+ * fixed and extended dynamic VLAN cases
+ * added EAP-EKE server support for deriving Session-Id
+ * set Acct-Session-Id to a random value to make it more likely to be
+ unique even if the device does not have a proper clock
+ * added more 2.4 GHz channels for 20/40 MHz HT co-ex scan
+ * modified SAE routines to be more robust and PWE generation to be
+ stronger against timing attacks
+ * added support for Brainpool Elliptic Curves with SAE
+ * increases maximum value accepted for cwmin/cwmax
+ * added support for CCMP-256 and GCMP-256 as group ciphers with FT
+ * added Fast Session Transfer (FST) module
+ * removed optional fields from RSNE when using FT with PMF
+ (workaround for interoperability issues with iOS 8.4)
+ * added EAP server support for TLS session resumption
+ * fixed key derivation for Suite B 192-bit AKM (this breaks
+ compatibility with the earlier version)
+ * added mechanism to track unconnected stations and do minimal band
+ steering
+ * number of small fixes
+
2015-03-15 - v2.4
* allow OpenSSL cipher configuration to be set for internal EAP server
(openssl_ciphers parameter)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index d4fd36e..fa5435d 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -18,6 +18,16 @@
-include .config
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
ifdef CONFIG_TESTING_OPTIONS
CFLAGS += -DCONFIG_TESTING_OPTIONS
CONFIG_WPS_TESTING=y
@@ -107,6 +117,14 @@
LIBS_n += -lrt
endif
+ifdef CONFIG_ELOOP_POLL
+CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
OBJS += ../src/utils/common.o
OBJS_c += ../src/utils/common.o
OBJS += ../src/utils/wpa_debug.o
@@ -526,6 +544,7 @@
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl
endif
OBJS += ../src/crypto/crypto_openssl.o
@@ -533,8 +552,14 @@
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_openssl.o
endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_h += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_h += -ldl
+endif
endif
ifeq ($(CONFIG_TLS), gnutls)
@@ -620,6 +645,8 @@
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
@@ -747,11 +774,17 @@
endif
endif
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
OBJS += ../src/crypto/rc4.o
endif
endif
+endif
ifdef NEED_SHA256
CFLAGS += -DCONFIG_SHA256
@@ -771,6 +804,17 @@
endif
ifdef NEED_SHA384
CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += ../src/crypto/sha384-internal.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += ../src/crypto/sha512-internal.o
endif
ifdef NEED_DH_GROUPS
@@ -898,6 +942,21 @@
LIBS_h += -lsqlite3
endif
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+OBJS += ../src/fst/fst.o
+OBJS += ../src/fst/fst_group.o
+OBJS += ../src/fst/fst_iface.o
+OBJS += ../src/fst/fst_session.o
+OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+endif
+
ALL=hostapd hostapd_cli
all: verify_config $(ALL)
@@ -960,9 +1019,11 @@
NOBJS += ../src/utils/common.o
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
NOBJS += ../src/crypto/rc4.o
endif
endif
+endif
ifdef CONFIG_INTERNAL_MD5
NOBJS += ../src/crypto/md5-internal.o
endif
diff --git a/hostapd/android.config b/hostapd/android.config
index 938aa54..c2d8b22 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -25,6 +25,9 @@
#LIBS += -L$(LIBNL)/lib
CONFIG_LIBNL20=y
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
#CFLAGS += -I/usr/local/include
@@ -180,5 +183,14 @@
#LIBS_p += -lbfd -liberty -lz
#LIBS_c += -lbfd -liberty -lz
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
# Enable AP
CONFIG_AP=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 49f8320..bf42466 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -912,11 +912,11 @@
IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
};
-static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
- char *val)
+static int hostapd_config_tx_queue(struct hostapd_config *conf,
+ const char *name, const char *val)
{
int num;
- char *pos;
+ const char *pos;
struct hostapd_tx_queue_params *queue;
/* skip 'tx_queue_' prefix */
@@ -1160,9 +1160,21 @@
if (os_strstr(capab, "[BF-ANTENNA-2]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ if (os_strstr(capab, "[BF-ANTENNA-3]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+ if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+ if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+ if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") &&
+ (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
if (os_strstr(capab, "[MU-BEAMFORMER]"))
conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
if (os_strstr(capab, "[VHT-TXOP-PS]"))
@@ -1507,6 +1519,54 @@
}
+static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
+{
+ char *delim;
+ u16 infoid;
+ size_t len;
+ struct wpabuf *payload;
+ struct anqp_element *elem;
+
+ delim = os_strchr(buf, ':');
+ if (!delim)
+ return -1;
+ delim++;
+ infoid = atoi(buf);
+ len = os_strlen(delim);
+ if (len & 1)
+ return -1;
+ len /= 2;
+ payload = wpabuf_alloc(len);
+ if (!payload)
+ return -1;
+ if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) {
+ wpabuf_free(payload);
+ return -1;
+ }
+
+ dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) {
+ if (elem->infoid == infoid) {
+ /* Update existing entry */
+ wpabuf_free(elem->payload);
+ elem->payload = payload;
+ return 0;
+ }
+ }
+
+ /* Add a new entry */
+ elem = os_zalloc(sizeof(*elem));
+ if (!elem) {
+ wpabuf_free(payload);
+ return -1;
+ }
+ elem->infoid = infoid;
+ elem->payload = payload;
+ dl_list_add(&bss->anqp_elem, &elem->list);
+
+ return 0;
+}
+
+
static int parse_qos_map_set(struct hostapd_bss_config *bss,
char *buf, int line)
{
@@ -1924,7 +1984,7 @@
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
- char *buf, char *pos, int line)
+ const char *buf, char *pos, int line)
{
if (os_strcmp(buf, "interface") == 0) {
os_strlcpy(conf->bss[0]->iface, pos,
@@ -2067,6 +2127,8 @@
bss->private_key_passwd = os_strdup(pos);
} else if (os_strcmp(buf, "check_crl") == 0) {
bss->check_crl = atoi(pos);
+ } else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
+ bss->tls_session_lifetime = atoi(pos);
} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
os_free(bss->ocsp_stapling_response);
bss->ocsp_stapling_response = os_strdup(pos);
@@ -2125,6 +2187,8 @@
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
os_free(bss->eap_sim_db);
bss->eap_sim_db = os_strdup(pos);
+ } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) {
+ bss->eap_sim_db_timeout = atoi(pos);
} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
bss->eap_sim_aka_result_ind = atoi(pos);
#endif /* EAP_SERVER_SIM */
@@ -2630,7 +2694,7 @@
}
} else if (os_strcmp(buf, "rts_threshold") == 0) {
conf->rts_threshold = atoi(pos);
- if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+ if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
wpa_printf(MSG_ERROR,
"Line %d: invalid rts_threshold %d",
line, conf->rts_threshold);
@@ -2638,8 +2702,10 @@
}
} else if (os_strcmp(buf, "fragm_threshold") == 0) {
conf->fragm_threshold = atoi(pos);
- if (conf->fragm_threshold < 256 ||
- conf->fragm_threshold > 2346) {
+ if (conf->fragm_threshold == -1) {
+ /* allow a value of -1 */
+ } else if (conf->fragm_threshold < 256 ||
+ conf->fragm_threshold > 2346) {
wpa_printf(MSG_ERROR,
"Line %d: invalid fragm_threshold %d",
line, conf->fragm_threshold);
@@ -2672,6 +2738,8 @@
conf->preamble = LONG_PREAMBLE;
} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
bss->ignore_broadcast_ssid = atoi(pos);
+ } else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
+ bss->no_probe_resp_if_max_sta = atoi(pos);
} else if (os_strcmp(buf, "wep_default_key") == 0) {
bss->ssid.wep.idx = atoi(pos);
if (bss->ssid.wep.idx > 3) {
@@ -3122,6 +3190,9 @@
} else if (os_strcmp(buf, "nai_realm") == 0) {
if (parse_nai_realm(bss, pos, line) < 0)
return 1;
+ } else if (os_strcmp(buf, "anqp_elem") == 0) {
+ if (parse_anqp_elem(bss, pos, line) < 0)
+ return 1;
} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
bss->gas_frag_limit = atoi(pos);
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
@@ -3235,6 +3306,8 @@
PARSE_TEST_PROBABILITY(ignore_assoc_probability)
PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+ } else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
+ conf->ecsa_ie_only = atoi(pos);
} else if (os_strcmp(buf, "bss_load_test") == 0) {
WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
pos = os_strchr(pos, ':');
@@ -3256,6 +3329,24 @@
bss->bss_load_test_set = 1;
} else if (os_strcmp(buf, "radio_measurements") == 0) {
bss->radio_measurements = atoi(pos);
+ } else if (os_strcmp(buf, "own_ie_override") == 0) {
+ struct wpabuf *tmp;
+ size_t len = os_strlen(pos) / 2;
+
+ tmp = wpabuf_alloc(len);
+ if (!tmp)
+ return 1;
+
+ if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
+ wpabuf_free(tmp);
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid own_ie_override '%s'",
+ line, pos);
+ return 1;
+ }
+
+ wpabuf_free(bss->own_ie_override);
+ bss->own_ie_override = tmp;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strcmp(buf, "vendor_elements") == 0) {
struct wpabuf *elems;
@@ -3309,6 +3400,74 @@
} else if (os_strcmp(buf, "wowlan_triggers") == 0) {
os_free(bss->wowlan_triggers);
bss->wowlan_triggers = os_strdup(pos);
+#ifdef CONFIG_FST
+ } else if (os_strcmp(buf, "fst_group_id") == 0) {
+ size_t len = os_strlen(pos);
+
+ if (!len || len >= sizeof(conf->fst_cfg.group_id)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid fst_group_id value '%s'",
+ line, pos);
+ return 1;
+ }
+
+ if (conf->fst_cfg.group_id[0]) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Duplicate fst_group value '%s'",
+ line, pos);
+ return 1;
+ }
+
+ os_strlcpy(conf->fst_cfg.group_id, pos,
+ sizeof(conf->fst_cfg.group_id));
+ } else if (os_strcmp(buf, "fst_priority") == 0) {
+ char *endp;
+ long int val;
+
+ if (!*pos) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: fst_priority value not supplied (expected 1..%u)",
+ line, FST_MAX_PRIO_VALUE);
+ return -1;
+ }
+
+ val = strtol(pos, &endp, 0);
+ if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)",
+ line, val, pos, FST_MAX_PRIO_VALUE);
+ return 1;
+ }
+ conf->fst_cfg.priority = (u8) val;
+ } else if (os_strcmp(buf, "fst_llt") == 0) {
+ char *endp;
+ long int val;
+
+ if (!*pos) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: fst_llt value not supplied (expected 1..%u)",
+ line, FST_MAX_LLT_MS);
+ return -1;
+ }
+ val = strtol(pos, &endp, 0);
+ if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
+ line, val, pos, FST_MAX_LLT_MS);
+ return 1;
+ }
+ conf->fst_cfg.llt = (u32) val;
+#endif /* CONFIG_FST */
+ } else if (os_strcmp(buf, "track_sta_max_num") == 0) {
+ conf->track_sta_max_num = atoi(pos);
+ } else if (os_strcmp(buf, "track_sta_max_age") == 0) {
+ conf->track_sta_max_age = atoi(pos);
+ } else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) {
+ os_free(bss->no_probe_resp_if_seen_on);
+ bss->no_probe_resp_if_seen_on = os_strdup(pos);
+ } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
+ os_free(bss->no_auth_if_seen_on);
+ bss->no_auth_if_seen_on = os_strdup(pos);
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
@@ -3329,7 +3488,7 @@
{
struct hostapd_config *conf;
FILE *f;
- char buf[512], *pos;
+ char buf[4096], *pos;
int line = 0;
int errors = 0;
size_t i;
@@ -3411,7 +3570,8 @@
int hostapd_set_iface(struct hostapd_config *conf,
- struct hostapd_bss_config *bss, char *field, char *value)
+ struct hostapd_bss_config *bss, const char *field,
+ char *value)
{
int errors;
size_t i;
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index fba57b8..c98bdb6 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -11,7 +11,7 @@
struct hostapd_config * hostapd_config_read(const char *fname);
int hostapd_set_iface(struct hostapd_config *conf,
- struct hostapd_bss_config *bss, char *field,
+ struct hostapd_bss_config *bss, const char *field,
char *value);
#endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index c606f2c..cb6fb17 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -25,6 +25,7 @@
#include "common/ieee802_11_defs.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
+#include "eapol_auth/eapol_auth_sm.h"
#include "radius/radius_client.h"
#include "radius/radius_server.h"
#include "l2_packet/l2_packet.h"
@@ -43,10 +44,13 @@
#include "ap/beacon.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
+#include "fst/fst_ctrl_iface.h"
#include "config_file.h"
#include "ctrl_iface.h"
+#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
struct wpa_ctrl_dst {
struct wpa_ctrl_dst *next;
struct sockaddr_un addr;
@@ -1056,6 +1060,97 @@
#endif /* CONFIG_WNM */
+static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ int ret = 0;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ WPA_ASSERT(hapd->conf->wpa_key_mgmt);
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+ ret = os_snprintf(pos, end - pos, "WPA-PSK ");
+ 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 (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 (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 (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 (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+ ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+ 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 (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+ ret = os_snprintf(pos, end - pos, "SAE ");
+ 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;
+ }
+ if (hapd->conf->wpa_key_mgmt &
+ WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ ret = os_snprintf(pos, end - pos,
+ "WPA-EAP-SUITE-B-192 ");
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (pos > buf && *(pos - 1) == ' ') {
+ *(pos - 1) = '\0';
+ pos--;
+ }
+
+ return pos - buf;
+}
+
+
static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
@@ -1105,82 +1200,20 @@
}
#endif /* CONFIG_WPS */
+ if (hapd->conf->wpa) {
+ ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
ret = os_snprintf(pos, end - pos, "key_mgmt=");
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 (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 (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 (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 (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 (os_snprintf_error(end - pos, ret))
- return pos - buf;
- pos += ret;
- }
-#endif /* CONFIG_SAE */
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
- if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
- ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
- 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 (os_snprintf_error(end - pos, ret))
- return pos - buf;
- pos += ret;
- }
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_SAE
- if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
- ret = os_snprintf(pos, end - pos, "SAE ");
- 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;
- }
- if (hapd->conf->wpa_key_mgmt &
- WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
- ret = os_snprintf(pos, end - pos,
- "WPA-EAP-SUITE-B-192 ");
- if (os_snprintf_error(end - pos, ret))
- return pos - buf;
- pos += ret;
- }
+ pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
@@ -1529,7 +1562,7 @@
{
struct hostapd_data *hapd = ctx;
const struct ether_header *eth;
- const struct iphdr *ip;
+ struct iphdr ip;
const u8 *pos;
unsigned int i;
@@ -1537,14 +1570,14 @@
return;
eth = (const struct ether_header *) buf;
- ip = (const struct iphdr *) (eth + 1);
- pos = (const u8 *) (ip + 1);
+ os_memcpy(&ip, eth + 1, sizeof(ip));
+ pos = &buf[sizeof(*eth) + sizeof(ip)];
- if (ip->ihl != 5 || ip->version != 4 ||
- ntohs(ip->tot_len) != HWSIM_IP_LEN)
+ 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++) {
+ for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
if (*pos != (u8) i)
return;
pos++;
@@ -1600,7 +1633,7 @@
int used;
long int val;
u8 tos;
- u8 buf[HWSIM_PACKETLEN];
+ u8 buf[2 + HWSIM_PACKETLEN];
struct ether_header *eth;
struct iphdr *ip;
u8 *dpos;
@@ -1628,7 +1661,7 @@
return -1;
tos = val;
- eth = (struct ether_header *) buf;
+ eth = (struct ether_header *) &buf[2];
os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
os_memcpy(eth->ether_shost, src, ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_IP);
@@ -1640,14 +1673,14 @@
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->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+ ip->daddr = htonl(192U << 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,
+ if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
HWSIM_PACKETLEN) < 0)
return -1;
@@ -1747,6 +1780,45 @@
#endif /* WPA_TRACE_BFD */
}
+
+static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+ char *pos;
+
+ wpa_trace_test_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_test_fail_func, pos,
+ sizeof(wpa_trace_test_fail_func));
+ } else {
+ wpa_trace_test_fail_after = 0;
+ }
+
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+ wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
@@ -1848,41 +1920,134 @@
}
-static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
- void *sock_ctx)
+static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
+ const char *cmd)
{
- struct hostapd_data *hapd = eloop_ctx;
- char buf[4096];
- int res;
- struct sockaddr_un from;
- socklen_t fromlen = sizeof(from);
- char *reply;
- const int reply_size = 4096;
- int reply_len;
- int level = MSG_DEBUG;
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
- res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
- (struct sockaddr *) &from, &fromlen);
- if (res < 0) {
- wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
- strerror(errno));
- return;
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->eapol_sm)
+ return -1;
+
+ eapol_auth_reauthenticate(sta->eapol_sm);
+ return 0;
+}
+
+
+static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
+{
+ u8 addr[ETH_ALEN];
+ struct sta_info *sta;
+ char *pos = cmd, *param;
+
+ if (hwaddr_aton(pos, addr) || pos[17] != ' ')
+ return -1;
+ pos += 18;
+ param = pos;
+ pos = os_strchr(pos, ' ');
+ if (!pos)
+ return -1;
+ *pos++ = '\0';
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->eapol_sm)
+ return -1;
+
+ return eapol_auth_set_conf(sta->eapol_sm, param, pos);
+}
+
+
+static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ char *pos, *end, *stamp;
+ int ret;
+
+ /* cmd: "LOG_LEVEL [<level>]" */
+ if (*cmd == '\0') {
+ pos = buf;
+ end = buf + buflen;
+ ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+ "Timestamp: %d\n",
+ debug_level_str(wpa_debug_level),
+ wpa_debug_timestamp);
+ if (os_snprintf_error(end - pos, ret))
+ ret = 0;
+
+ return ret;
}
- buf[res] = '\0';
- if (os_strcmp(buf, "PING") == 0)
- level = MSG_EXCESSIVE;
- wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
- reply = os_malloc(reply_size);
- if (reply == NULL) {
- if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
- fromlen) < 0) {
- wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
- strerror(errno));
+ while (*cmd == ' ')
+ cmd++;
+
+ stamp = os_strchr(cmd, ' ');
+ if (stamp) {
+ *stamp++ = '\0';
+ while (*stamp == ' ') {
+ stamp++;
}
- return;
}
+ if (os_strlen(cmd)) {
+ int level = str_to_debug_level(cmd);
+ if (level < 0)
+ return -1;
+ wpa_debug_level = level;
+ }
+
+ if (stamp && os_strlen(stamp))
+ wpa_debug_timestamp = atoi(stamp);
+
+ os_memcpy(buf, "OK\n", 3);
+ return 3;
+}
+
+
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ char *pos, *end;
+ struct hostapd_sta_info *info;
+ struct os_reltime now;
+
+ sta_track_expire(iface, 0);
+
+ pos = buf;
+ end = buf + buflen;
+
+ os_get_reltime(&now);
+ dl_list_for_each_reverse(info, &iface->sta_seen,
+ struct hostapd_sta_info, list) {
+ struct os_reltime age;
+ int ret;
+
+ os_reltime_sub(&now, &info->last_seen, &age);
+ ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+ MAC2STR(info->addr), (unsigned int) age.sec);
+ if (os_snprintf_error(end - pos, ret))
+ break;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+ char *buf, char *reply,
+ int reply_size,
+ struct sockaddr_un *from,
+ socklen_t fromlen)
+{
+ int reply_len, res;
+
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
@@ -1939,13 +2104,13 @@
reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
reply_size);
} else if (os_strcmp(buf, "ATTACH") == 0) {
- if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
+ if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
reply_len = -1;
} else if (os_strcmp(buf, "DETACH") == 0) {
- if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
+ if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
reply_len = -1;
} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
- if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
+ if (hostapd_ctrl_iface_level(hapd, from, fromlen,
buf + 6))
reply_len = -1;
} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
@@ -2080,6 +2245,11 @@
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
reply_size);
+ } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+ if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_FAIL") == 0) {
+ reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -2092,6 +2262,20 @@
#ifdef RADIUS_SERVER
radius_server_erp_flush(hapd->radius_srv);
#endif /* RADIUS_SERVER */
+ } else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
+ if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
+ if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
+ reply_len = -1;
+ } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+ reply_len = hostapd_ctrl_iface_log_level(
+ hapd, buf + 9, reply, reply_size);
+#ifdef NEED_AP_MLME
+ } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
+ reply_len = hostapd_ctrl_iface_track_sta_list(
+ hapd, reply, reply_size);
+#endif /* NEED_AP_MLME */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -2101,6 +2285,50 @@
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
+
+ return reply_len;
+}
+
+
+static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ char buf[4096];
+ int res;
+ struct sockaddr_un from;
+ socklen_t fromlen = sizeof(from);
+ char *reply;
+ const int reply_size = 4096;
+ int reply_len;
+ int level = MSG_DEBUG;
+
+ res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+ (struct sockaddr *) &from, &fromlen);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+ strerror(errno));
+ return;
+ }
+ buf[res] = '\0';
+ if (os_strcmp(buf, "PING") == 0)
+ level = MSG_EXCESSIVE;
+ wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
+
+ reply = os_malloc(reply_size);
+ if (reply == NULL) {
+ if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+ fromlen) < 0) {
+ wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+ strerror(errno));
+ }
+ return;
+ }
+
+ reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
+ reply, reply_size,
+ &from, fromlen);
+
if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
@@ -2423,6 +2651,214 @@
}
+#ifdef CONFIG_FST
+
+static int
+hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
+ const char *cmd)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct fst_iface_cfg cfg;
+ struct hostapd_data *hapd;
+ struct fst_wpa_obj iface_obj;
+
+ if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+ hapd = hostapd_get_iface(interfaces, ifname);
+ if (hapd) {
+ if (hapd->iface->fst) {
+ wpa_printf(MSG_INFO, "FST: Already attached");
+ return -1;
+ }
+ fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+ hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
+ &iface_obj, &cfg);
+ if (hapd->iface->fst)
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+static int
+hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
+ const char *cmd)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct hostapd_data * hapd;
+
+ if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+ hapd = hostapd_get_iface(interfaces, ifname);
+ if (hapd) {
+ if (!fst_iface_detach(ifname)) {
+ hapd->iface->fst = NULL;
+ hapd->iface->fst_ies = NULL;
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+
+#endif /* CONFIG_FST */
+
+
+static struct hostapd_data *
+hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
+ const char *ifname)
+{
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd;
+
+ hapd = iface->bss[j];
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ return hapd;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
+ struct hostapd_data *dst_hapd,
+ const char *param)
+{
+ int res;
+ char *value;
+
+ value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+ if (!value) {
+ wpa_printf(MSG_ERROR,
+ "DUP: cannot allocate buffer to stringify %s",
+ param);
+ goto error_return;
+ }
+
+ if (os_strcmp(param, "wpa") == 0) {
+ os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
+ src_hapd->conf->wpa);
+ } else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
+ src_hapd->conf->wpa_key_mgmt) {
+ res = hostapd_ctrl_iface_get_key_mgmt(
+ src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+ if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
+ goto error_stringify;
+ } else if (os_strcmp(param, "wpa_pairwise") == 0 &&
+ src_hapd->conf->wpa_pairwise) {
+ res = wpa_write_ciphers(value,
+ value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+ src_hapd->conf->wpa_pairwise, " ");
+ if (res < 0)
+ goto error_stringify;
+ } else if (os_strcmp(param, "rsn_pairwise") == 0 &&
+ src_hapd->conf->rsn_pairwise) {
+ res = wpa_write_ciphers(value,
+ value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+ src_hapd->conf->rsn_pairwise, " ");
+ if (res < 0)
+ goto error_stringify;
+ } else if (os_strcmp(param, "wpa_passphrase") == 0 &&
+ src_hapd->conf->ssid.wpa_passphrase) {
+ os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
+ src_hapd->conf->ssid.wpa_passphrase);
+ } else if (os_strcmp(param, "wpa_psk") == 0 &&
+ src_hapd->conf->ssid.wpa_psk_set) {
+ wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+ src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+ } else {
+ wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
+ goto error_return;
+ }
+
+ res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
+ os_free(value);
+ return res;
+
+error_stringify:
+ wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
+error_return:
+ os_free(value);
+ return -1;
+}
+
+
+static int
+hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
+ char *cmd)
+{
+ char *p_start = cmd, *p_end;
+ struct hostapd_data *src_hapd, *dst_hapd;
+
+ /* cmd: "<src ifname> <dst ifname> <variable name> */
+
+ p_end = os_strchr(p_start, ' ');
+ if (!p_end) {
+ wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
+ cmd);
+ return -1;
+ }
+
+ *p_end = '\0';
+ src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+ if (!src_hapd) {
+ wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
+ p_start);
+ return -1;
+ }
+
+ p_start = p_end + 1;
+ p_end = os_strchr(p_start, ' ');
+ if (!p_end) {
+ wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
+ cmd);
+ return -1;
+ }
+
+ *p_end = '\0';
+ dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+ if (!dst_hapd) {
+ wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
+ p_start);
+ return -1;
+ }
+
+ p_start = p_end + 1;
+ return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
+}
+
+
+static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
+ const char *ifname,
+ char *buf, char *reply,
+ int reply_size,
+ struct sockaddr_un *from,
+ socklen_t fromlen)
+{
+ struct hostapd_data *hapd;
+
+ hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
+ if (hapd == NULL) {
+ int res;
+
+ res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
+ if (os_snprintf_error(reply_size, res))
+ return -1;
+ return res;
+ }
+
+ return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
+ from, fromlen);
+}
+
+
static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
@@ -2458,6 +2894,18 @@
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
+ if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+ char *pos = os_strchr(buf + 7, ' ');
+
+ if (pos) {
+ *pos++ = '\0';
+ reply_len = hostapd_global_ctrl_iface_ifname(
+ interfaces, buf + 7, pos, reply, reply_size,
+ &from, fromlen);
+ goto send_reply;
+ }
+ }
+
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
@@ -2486,12 +2934,33 @@
if (hapd_module_tests() < 0)
reply_len = -1;
#endif /* CONFIG_MODULE_TESTS */
+#ifdef CONFIG_FST
+ } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+ if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
+ reply_len = os_snprintf(reply, reply_size, "OK\n");
+ else
+ reply_len = -1;
+ } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+ if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
+ reply_len = os_snprintf(reply, reply_size, "OK\n");
+ else
+ reply_len = -1;
+ } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+ reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
+ } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+ if (!hostapd_global_ctrl_iface_dup_network(interfaces,
+ buf + 12))
+ reply_len = os_snprintf(reply, reply_size, "OK\n");
+ else
+ reply_len = -1;
} else {
wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
"ignored");
reply_len = -1;
}
+send_reply:
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4cde2b5..2a749dd 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -18,6 +18,9 @@
# Driver interface for drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
# driver_nl80211.c requires libnl. If you are compiling it yourself
# you may need to point hostapd to your version of libnl.
#
@@ -240,6 +243,12 @@
# requirements described above.
#CONFIG_NO_RANDOM_POOL=y
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
# Select TLS implementation
# openssl = OpenSSL (default)
# gnutls = GnuTLS
@@ -283,6 +292,12 @@
# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
#CONFIG_SQLITE=y
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
# Testing options
# This can be used to enable some testing options (see also the example
# configuration file) that are really useful only for testing clients that
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 8afe457..84d0308 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -550,7 +550,7 @@
static void update_milenage_file(const char *fname)
{
FILE *f, *f2;
- char buf[500], *pos;
+ char name[500], buf[500], *pos;
char *end = buf + sizeof(buf);
struct milenage_parameters *m;
size_t imsi_len;
@@ -561,10 +561,10 @@
return;
}
- snprintf(buf, sizeof(buf), "%s.new", fname);
- f2 = fopen(buf, "w");
+ snprintf(name, sizeof(name), "%s.new", fname);
+ f2 = fopen(name, "w");
if (f2 == NULL) {
- printf("Could not write Milenage data file '%s'\n", buf);
+ printf("Could not write Milenage data file '%s'\n", name);
fclose(f);
return;
}
@@ -606,14 +606,14 @@
fclose(f2);
fclose(f);
- snprintf(buf, sizeof(buf), "%s.bak", fname);
- if (rename(fname, buf) < 0) {
+ snprintf(name, sizeof(name), "%s.bak", fname);
+ if (rename(fname, name) < 0) {
perror("rename");
return;
}
- snprintf(buf, sizeof(buf), "%s.new", fname);
- if (rename(buf, fname) < 0) {
+ snprintf(name, sizeof(name), "%s.new", fname);
+ if (rename(name, fname) < 0) {
perror("rename");
return;
}
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 00fc142..4f51140 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -173,7 +173,7 @@
# Channel list restriction. This option allows hostapd to select one of the
# provided channels when a channel should be automatically selected.
# Channel list can be provided as range using hyphen ('-') or individual
-# channels can be specified by space (' ') seperated values
+# channels can be specified by space (' ') separated values
# Default: all channels allowed in selected hw_mode
#chanlist=100 104 108 112 116
#chanlist=1 6 11-13
@@ -192,16 +192,16 @@
# (default: 2007)
max_num_sta=255
-# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
+# RTS/CTS threshold; -1 = disabled (default); range -1..65535
# If this field is not included in hostapd.conf, hostapd will not control
# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it.
-rts_threshold=2347
+rts_threshold=-1
-# Fragmentation threshold; 2346 = disabled (default); range 256..2346
+# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346
# If this field is not included in hostapd.conf, hostapd will not control
# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set
# it.
-fragm_threshold=2346
+fragm_threshold=-1
# Rate configuration
# Default is to enable all rates supported by the hardware. This configuration
@@ -267,7 +267,14 @@
# requests for broadcast SSID
ignore_broadcast_ssid=0
-# Additional vendor specfic elements for Beacon and Probe Response frames
+# Do not reply to broadcast Probe Request frames from unassociated STA if there
+# is no room for additional stations (max_num_sta). This can be used to
+# discourage a STA from trying to associate with this AP if the association
+# would be rejected due to maximum STA limit.
+# Default: 0 (disabled)
+#no_probe_resp_if_max_sta=0
+
+# Additional vendor specific elements for Beacon and Probe Response frames
# This parameter can be used to add additional vendor specific element(s) into
# the end of the Beacon and Probe Response frames. The format for these
# element(s) is a hexdump of the raw information elements (id+len+payload for
@@ -582,14 +589,16 @@
# 0 = Not supported (default)
# 1 = Supported
#
-# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
# Beamformee's capability indicating the maximum number of beamformer
# antennas the beamformee can support when sending compressed beamforming
# feedback
# If SU beamformer capable, set to maximum value minus 1
# else reserved (default)
#
-# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2]
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
# Beamformer's capability indicating the maximum value of the NUM_STS parameter
# in the TXVECTOR of a VHT NDP
# If SU beamformer capable, set to maximum value minus 1
@@ -603,9 +612,9 @@
# VHT TXOP PS: [VHT-TXOP-PS]
# Indicates whether or not the AP supports VHT TXOP Power Save Mode
# or whether or not the STA is in VHT TXOP Power Save mode
-# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
+# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
# mode
-# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
# mode
#
# +HTC-VHT Capable: [HTC-VHT]
@@ -766,6 +775,12 @@
# 2 = check all CRLs in the certificate path
#check_crl=1
+# TLS Session Lifetime in seconds
+# This can be used to allow TLS sessions to be cached and resumed with an
+# abbreviated handshake when using EAP-TLS/TTLS/PEAP.
+# (default: 0 = session caching and resumption disabled)
+#tls_session_lifetime=3600
+
# Cached OCSP stapling response (DER encoded)
# If set, this file is sent as a certificate status response by the EAP server
# if the EAP peer requests certificate status in the ClientHello message.
@@ -817,6 +832,11 @@
#eap_sim_db=unix:/tmp/hlr_auc_gw.sock
#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
+# EAP-SIM DB request timeout
+# This parameter sets the maximum time to wait for a database request response.
+# The parameter value is in seconds.
+#eap_sim_db_timeout=1
+
# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
# random value. It is configured as a 16-octet value in hex format. It can be
# generated, e.g., with the following command:
@@ -1249,6 +1269,11 @@
# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
#pmk_r1_push=1
+# Whether to enable FT-over-DS
+# 0 = FT-over-DS disabled
+# 1 = FT-over-DS enabled (default)
+#ft_over_ds=1
+
##### Neighbor table ##########################################################
# Maximum number of entries kept in AP table (either for neigbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -1266,6 +1291,43 @@
# default: 60
#ap_table_expiration_time=3600
+# Maximum number of stations to track on the operating channel
+# This can be used to detect dualband capable stations before they have
+# associated, e.g., to provide guidance on which colocated BSS to use.
+# Default: 0 (disabled)
+#track_sta_max_num=100
+
+# Maximum age of a station tracking entry in seconds
+# Default: 180
+#track_sta_max_age=180
+
+# Do not reply to group-addressed Probe Request from a station that was seen on
+# another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to restrict Probe Request
+# frame handling from replying to group-addressed Probe Request frames from a
+# station that has been detected to be capable of operating on another band,
+# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when
+# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# discovering the AP.
+#no_probe_resp_if_seen_on=wlan1
+
+# Reject authentication from a station that was seen on another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to reject authentication
+# attempts from a station that has been detected to be capable of operating on
+# another band, e.g., to try to reduce likelihood of the station selecting a
+# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# connecting with the AP.
+#no_auth_if_seen_on=wlan1
##### Wi-Fi Protected Setup (WPS) #############################################
@@ -1634,6 +1696,17 @@
# username/password
#nai_realm=0,example.org,13[5:6],21[2:4][5:7]
+# Arbitrary ANQP-element configuration
+# Additional ANQP-elements with arbitrary values can be defined by specifying
+# their contents in raw format as a hexdump of the payload. Note that these
+# values will override ANQP-element contents that may have been specified in the
+# more higher layer configuration parameters listed above.
+# format: anqp_elem=<InfoID>:<hexdump of payload>
+# For example, AP Geospatial Location ANQP-element with unknown location:
+#anqp_elem=265:0000
+# For example, AP Civic Location ANQP-element with unknown location:
+#anqp_elem=266:000000
+
# QoS Map Set configuration
#
# Comma delimited QoS Map Set in decimal values
@@ -1747,6 +1820,32 @@
#
#osu_server_uri=...
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 ms.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration
@@ -1768,6 +1867,10 @@
#
# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
#corrupt_gtk_rekey_mic_probability=0.0
+#
+# Include only ECSA IE without CSA IE where possible
+# (channel switch operating class is needed)
+#ecsa_ie_only=0
##### Multiple BSSID support ##################################################
#
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index e299183..46c2f37 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -97,6 +97,7 @@
#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
#endif /* CONFIG_CTRL_IFACE_DIR */
static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
@@ -112,13 +113,15 @@
"\n"
"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
"[-a<path>] \\\n"
- " [-G<ping interval>] [command..]\n"
+ " [-P<pid file>] [-G<ping interval>] [command..]\n"
"\n"
"Options:\n"
" -h help (show this usage text)\n"
" -v shown version information\n"
" -p<path> path to find control sockets (default: "
"/var/run/hostapd)\n"
+ " -s<dir_path> dir path to open client sockets (default: "
+ CONFIG_CTRL_IFACE_DIR ")\n"
" -a<file> run in daemon mode executing the action file "
"based on events\n"
" from hostapd\n"
@@ -145,7 +148,14 @@
return NULL;
snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
- ctrl_conn = wpa_ctrl_open(cfile);
+ if (client_socket_dir && client_socket_dir[0] &&
+ access(client_socket_dir, F_OK) < 0) {
+ perror(client_socket_dir);
+ free(cfile);
+ return NULL;
+ }
+
+ ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
free(cfile);
return ctrl_conn;
}
@@ -922,6 +932,35 @@
}
+#ifdef CONFIG_FST
+static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ char cmd[256];
+ int res;
+ int i;
+ int total;
+
+ if (argc <= 0) {
+ printf("FST command: parameters are required.\n");
+ return -1;
+ }
+
+ total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
+
+ for (i = 0; i < argc; i++) {
+ res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
+ argv[i]);
+ if (os_snprintf_error(sizeof(cmd) - total, res)) {
+ printf("Too long fst command.\n");
+ return -1;
+ }
+ total += res;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_FST */
+
+
static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
@@ -1010,6 +1049,25 @@
}
+static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ char cmd[256];
+ int res;
+
+ res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
+ argc >= 1 ? " " : "",
+ argc >= 1 ? argv[0] : "",
+ argc == 2 ? " " : "",
+ argc == 2 ? argv[1] : "");
+ if (os_snprintf_error(sizeof(cmd), res)) {
+ printf("Too long option\n");
+ return -1;
+ }
+ return wpa_ctrl_command(ctrl, cmd);
+}
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1049,6 +1107,9 @@
{ "get_config", hostapd_cli_cmd_get_config },
{ "help", hostapd_cli_cmd_help },
{ "interface", hostapd_cli_cmd_interface },
+#ifdef CONFIG_FST
+ { "fst", hostapd_cli_cmd_fst },
+#endif /* CONFIG_FST */
{ "level", hostapd_cli_cmd_level },
{ "license", hostapd_cli_cmd_license },
{ "quit", hostapd_cli_cmd_quit },
@@ -1064,6 +1125,7 @@
{ "reload", hostapd_cli_cmd_reload },
{ "disable", hostapd_cli_cmd_disable },
{ "erp_flush", hostapd_cli_cmd_erp_flush },
+ { "log_level", hostapd_cli_cmd_log_level },
{ NULL, NULL }
};
@@ -1285,7 +1347,7 @@
return -1;
for (;;) {
- c = getopt(argc, argv, "a:BhG:i:p:v");
+ c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
if (c < 0)
break;
switch (c) {
@@ -1311,6 +1373,12 @@
case 'p':
ctrl_iface_dir = optarg;
break;
+ case 'P':
+ pid_file = optarg;
+ break;
+ case 's':
+ client_socket_dir = optarg;
+ break;
default:
usage();
return -1;
diff --git a/hostapd/main.c b/hostapd/main.c
index 62d0775..72b1d91 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -24,6 +24,7 @@
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
+#include "fst/fst.h"
#include "config_file.h"
#include "eap_register.h"
#include "ctrl_iface.h"
@@ -455,6 +456,7 @@
" -T = record to Linux tracing in addition to logging\n"
" (records all messages regardless of debug verbosity)\n"
#endif /* CONFIG_DEBUG_LINUX_TRACING */
+ " -S start all the interfaces synchronously\n"
" -t include timestamps in some debug messages\n"
" -v show hostapd version\n");
@@ -533,6 +535,28 @@
#endif /* CONFIG_WPS */
+#ifndef HOSTAPD_CLEANUP_INTERVAL
+#define HOSTAPD_CLEANUP_INTERVAL 10
+#endif /* HOSTAPD_CLEANUP_INTERVAL */
+
+static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
+{
+ hostapd_periodic_iface(iface);
+ return 0;
+}
+
+
+/* Periodic cleanup tasks */
+static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hapd_interfaces *interfaces = eloop_ctx;
+
+ eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+ hostapd_periodic, interfaces, NULL);
+ hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
+}
+
+
int main(int argc, char *argv[])
{
struct hapd_interfaces interfaces;
@@ -547,6 +571,7 @@
#ifdef CONFIG_DEBUG_LINUX_TRACING
int enable_trace_dbg = 0;
#endif /* CONFIG_DEBUG_LINUX_TRACING */
+ int start_ifaces_in_sync = 0;
if (os_program_init())
return -1;
@@ -564,7 +589,7 @@
interfaces.global_ctrl_dst = NULL;
for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
+ c = getopt(argc, argv, "b:Bde:f:hKP:STtu:vg:G:");
if (c < 0)
break;
switch (c) {
@@ -621,6 +646,9 @@
bss_config = tmp_bss;
bss_config[num_bss_configs++] = optarg;
break;
+ case 'S':
+ start_ifaces_in_sync = 1;
+ break;
#ifdef CONFIG_WPS
case 'u':
return gen_uuid(optarg);
@@ -666,6 +694,20 @@
return -1;
}
+ eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+ hostapd_periodic, &interfaces, NULL);
+
+ if (fst_global_init()) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize global FST context");
+ goto out;
+ }
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+ if (!fst_global_add_ctrl(fst_ctrl_cli))
+ wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
+
/* Allocate and parse configuration for full interface files */
for (i = 0; i < interfaces.count; i++) {
interfaces.iface[i] = hostapd_interface_init(&interfaces,
@@ -675,6 +717,8 @@
wpa_printf(MSG_ERROR, "Failed to initialize interface");
goto out;
}
+ if (start_ifaces_in_sync)
+ interfaces.iface[i]->need_to_start_in_sync = 1;
}
/* Allocate and parse configuration for per-BSS files */
@@ -750,6 +794,7 @@
}
os_free(interfaces.iface);
+ eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
hostapd_global_deinit(pid_file);
os_free(pid_file);
@@ -759,6 +804,8 @@
os_free(bss_config);
+ fst_global_deinit();
+
os_program_deinit();
return ret;
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index a71e86d..e4db322 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -54,6 +54,7 @@
OBJS += ../../src/crypto/md5-internal.c
OBJS += ../../src/crypto/sha1-internal.c
OBJS += ../../src/crypto/sha256-internal.c
+OBJS += ../../src/crypto/tls_openssl_ocsp.c
L_CFLAGS += -DEAP_TLS_OPENSSL
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index 94cd5f1..fc9b619 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -76,6 +76,7 @@
endif
CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += ../../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl -lcrypto
hs20-osu-client: $(OBJS)
diff --git a/hs20/client/est.c b/hs20/client/est.c
index ec05bc4..9f1519b 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -16,6 +16,9 @@
#include <openssl/asn1t.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/buf.h>
+#endif /* OPENSSL_IS_BORINGSSL */
#include "common.h"
#include "utils/base64.h"
@@ -27,12 +30,28 @@
static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
size_t len, char *pem_file, char *der_file)
{
+#ifdef OPENSSL_IS_BORINGSSL
+ CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
PKCS7 *p7 = NULL;
const unsigned char *p = pkcs7;
+#endif /* OPENSSL_IS_BORINGSSL */
STACK_OF(X509) *certs;
int i, num, ret = -1;
BIO *out = NULL;
+#ifdef OPENSSL_IS_BORINGSSL
+ certs = sk_X509_new_null();
+ if (!certs)
+ goto fail;
+ CBS_init(&pkcs7_cbs, pkcs7, len);
+ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+ wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ write_result(ctx, "Could not parse PKCS#7 object from EST");
+ goto fail;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
p7 = d2i_PKCS7(NULL, &p, len);
if (p7 == NULL) {
wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
@@ -52,6 +71,7 @@
certs = NULL;
break;
}
+#endif /* OPENSSL_IS_BORINGSSL */
if (!certs || ((num = sk_X509_num(certs)) == 0)) {
wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
@@ -84,7 +104,12 @@
ret = 0;
fail:
+#ifdef OPENSSL_IS_BORINGSSL
+ if (certs)
+ sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
if (out)
BIO_free_all(out);
@@ -310,6 +335,23 @@
if (!csrattrs || ! csrattrs->attrs)
return;
+#ifdef OPENSSL_IS_BORINGSSL
+ num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
+ csrattrs->attrs));
+ for (i = 0; i < num; i++) {
+ AttrOrOID *ao = sk_value(
+ CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *,
+ csrattrs->attrs), i);
+ switch (ao->type) {
+ case 0:
+ add_csrattrs_oid(ctx, ao->d.oid, exts);
+ break;
+ case 1:
+ add_csrattrs_attr(ctx, ao->d.attribute, exts);
+ break;
+ }
+ }
+#else /* OPENSSL_IS_BORINGSSL */
num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
for (i = 0; i < num; i++) {
AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
@@ -322,6 +364,7 @@
break;
}
}
+#endif /* OPENSSL_IS_BORINGSSL */
}
@@ -340,6 +383,7 @@
STACK_OF(X509_EXTENSION) *exts = NULL;
X509_EXTENSION *ex;
BIO *out;
+ CONF *ctmp = NULL;
wpa_printf(MSG_INFO, "Generate RSA private key");
write_summary(ctx, "Generate RSA private key");
@@ -421,20 +465,20 @@
if (!exts)
goto fail;
- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
- "CA:FALSE");
+ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
+ "CA:FALSE");
if (ex == NULL ||
!sk_X509_EXTENSION_push(exts, ex))
goto fail;
- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
- "nonRepudiation,digitalSignature,keyEncipherment");
+ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
+ "nonRepudiation,digitalSignature,keyEncipherment");
if (ex == NULL ||
!sk_X509_EXTENSION_push(exts, ex))
goto fail;
- ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
- "1.3.6.1.4.1.40808.1.1.2");
+ ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
+ "1.3.6.1.4.1.40808.1.1.2");
if (ex == NULL ||
!sk_X509_EXTENSION_push(exts, ex))
goto fail;
@@ -454,7 +498,9 @@
char *txt;
size_t rlen;
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
X509_REQ_print(out, req);
+#endif
rlen = BIO_ctrl_pending(out);
txt = os_malloc(rlen + 1);
if (txt) {
@@ -473,7 +519,9 @@
FILE *f = fopen(csr_pem, "w");
if (f == NULL)
goto fail;
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
X509_REQ_print_fp(f, req);
+#endif
if (!PEM_write_X509_REQ(f, req)) {
fclose(f);
goto fail;
diff --git a/src/Makefile b/src/Makefile
index 10e0171..c9e84c1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,5 @@
SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS += fst
all:
for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index a096de4..c60b3a6 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -1,6 +1,6 @@
/*
* hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -147,6 +147,15 @@
wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
goto fail;
}
+
+ if (sta->ipaddr &&
+ !radius_msg_add_attr_int32(msg,
+ RADIUS_ATTR_FRAMED_IP_ADDRESS,
+ be_to_host32(sta->ipaddr))) {
+ wpa_printf(MSG_ERROR,
+ "Could not add Framed-IP-Address");
+ goto fail;
+ }
}
return msg;
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 455013a..cf9b2ce 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -38,6 +38,8 @@
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
{
+ dl_list_init(&bss->anqp_elem);
+
bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
bss->logger_syslog = (unsigned int) -1;
@@ -63,6 +65,7 @@
bss->dtim_period = 2;
bss->radius_server_auth_port = 1812;
+ bss->eap_sim_db_timeout = 1;
bss->ap_max_inactivity = AP_MAX_INACTIVITY;
bss->eapol_version = EAPOL_VERSION;
@@ -172,6 +175,7 @@
conf->ap_table_max_size = 255;
conf->ap_table_expiration_time = 60;
+ conf->track_sta_max_age = 180;
#ifdef CONFIG_TESTING_OPTIONS
conf->ignore_probe_probability = 0.0;
@@ -179,6 +183,7 @@
conf->ignore_assoc_probability = 0.0;
conf->ignore_reassoc_probability = 0.0;
conf->corrupt_gtk_rekey_mic_probability = 0.0;
+ conf->ecsa_ie_only = 0;
#endif /* CONFIG_TESTING_OPTIONS */
conf->acs = 0;
@@ -409,6 +414,19 @@
}
+static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
+{
+ struct anqp_element *elem;
+
+ while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
+ list))) {
+ dl_list_del(&elem->list);
+ wpabuf_free(elem->payload);
+ os_free(elem);
+ }
+}
+
+
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
struct hostapd_eap_user *user, *prev_user;
@@ -522,6 +540,7 @@
os_free(conf->network_auth_type);
os_free(conf->anqp_3gpp_cell_net);
os_free(conf->domain_name);
+ hostapd_config_free_anqp_elem(conf);
#ifdef CONFIG_RADIUS_TEST
os_free(conf->dump_msk_file);
@@ -561,6 +580,13 @@
os_free(conf->server_id);
+#ifdef CONFIG_TESTING_OPTIONS
+ wpabuf_free(conf->own_ie_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_free(conf->no_probe_resp_if_seen_on);
+ os_free(conf->no_auth_if_seen_on);
+
os_free(conf);
}
@@ -967,10 +993,11 @@
bss->rsn_pairwise = WPA_CIPHER_CCMP;
} else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
- bss->wpa_group = WPA_CIPHER_NONE;
- bss->wpa_pairwise = WPA_CIPHER_NONE;
- bss->rsn_pairwise = WPA_CIPHER_NONE;
- if (full_config)
+ if (full_config) {
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+ }
}
}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c14eeda..ff9dcb0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,11 +10,13 @@
#define HOSTAPD_CONFIG_H
#include "common/defs.h"
+#include "utils/list.h"
#include "ip_addr.h"
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "wps/wps.h"
+#include "fst/fst.h"
/**
* mesh_conf - local MBSS state and settings
@@ -32,8 +34,8 @@
u8 mesh_sp_id;
/* Authentication Protocol Identifier */
u8 mesh_auth_id;
- u8 *ies;
- int ie_len;
+ u8 *rsn_ie;
+ int rsn_ie_len;
#define MESH_CONF_SEC_NONE BIT(0)
#define MESH_CONF_SEC_AUTH BIT(1)
#define MESH_CONF_SEC_AMPE BIT(2)
@@ -204,6 +206,13 @@
} eap_method[MAX_NAI_EAP_METHODS];
};
+struct anqp_element {
+ struct dl_list list;
+ u16 infoid;
+ struct wpabuf *payload;
+};
+
+
/**
* struct hostapd_bss_config - Per-BSS configuration
*/
@@ -230,6 +239,7 @@
struct hostapd_eap_user *eap_user;
char *eap_user_sqlite;
char *eap_sim_db;
+ unsigned int eap_sim_db_timeout;
int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
@@ -329,6 +339,7 @@
char *private_key;
char *private_key_passwd;
int check_crl;
+ unsigned int tls_session_lifetime;
char *ocsp_stapling_response;
char *dh_file;
char *openssl_ciphers;
@@ -356,6 +367,7 @@
int ap_max_inactivity;
int ignore_broadcast_ssid;
+ int no_probe_resp_if_max_sta;
int wmm_enabled;
int wmm_uapsd;
@@ -479,6 +491,8 @@
unsigned int nai_realm_count;
struct hostapd_nai_realm_data *nai_realm_data;
+ struct dl_list anqp_elem; /* list of struct anqp_element */
+
u16 gas_comeback_delay;
int gas_frag_limit;
@@ -543,6 +557,7 @@
#ifdef CONFIG_TESTING_OPTIONS
u8 bss_load_test[5];
u8 bss_load_test_set;
+ struct wpabuf *own_ie_override;
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
@@ -551,6 +566,9 @@
int radio_measurements;
int vendor_vht;
+
+ char *no_probe_resp_if_seen_on;
+ char *no_auth_if_seen_on;
};
@@ -583,6 +601,9 @@
int ap_table_max_size;
int ap_table_expiration_time;
+ unsigned int track_sta_max_num;
+ unsigned int track_sta_max_age;
+
char country[3]; /* first two octets: country code as described in
* ISO/IEC 3166-1. Third octet:
* ' ' (ascii 32): all environments
@@ -619,6 +640,7 @@
u16 ht_capab;
int ieee80211n;
int secondary_channel;
+ int no_pri_sec_switch;
int require_ht;
int obss_interval;
u32 vht_capab;
@@ -628,6 +650,10 @@
u8 vht_oper_centr_freq_seg0_idx;
u8 vht_oper_centr_freq_seg1_idx;
+#ifdef CONFIG_FST
+ struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
#ifdef CONFIG_P2P
u8 p2p_go_ctwindow;
#endif /* CONFIG_P2P */
@@ -638,6 +664,7 @@
double ignore_assoc_probability;
double ignore_reassoc_probability;
double corrupt_gtk_rekey_mic_probability;
+ int ecsa_ie_only;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f3f7edd..b390450 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -81,6 +81,22 @@
wpabuf_put_data(proberesp, buf, pos - buf);
}
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ size_t add = wpabuf_len(hapd->iface->fst_ies);
+
+ if (wpabuf_resize(&beacon, add) < 0)
+ goto fail;
+ wpabuf_put_buf(beacon, hapd->iface->fst_ies);
+ if (wpabuf_resize(&proberesp, add) < 0)
+ goto fail;
+ wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
+ if (wpabuf_resize(&assocresp, add) < 0)
+ goto fail;
+ wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
if (hapd->wps_beacon_ie) {
if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
0)
@@ -452,7 +468,7 @@
return -1;
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
bss_ctx, drv_priv, force_ifname, if_addr,
- bridge, use_existing);
+ bridge, use_existing, 1);
}
@@ -633,7 +649,19 @@
{
if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
return 0;
- return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+ NULL, 0);
+}
+
+
+int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack,
+ const u16 *csa_offs, size_t csa_offs_len)
+{
+ if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+ return 0;
+ return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+ csa_offs, csa_offs_len);
}
@@ -727,6 +755,25 @@
}
+static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode,
+ int acs_ch_list_all,
+ int **freq_list)
+{
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if ((acs_ch_list_all ||
+ freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+ chan->chan)) &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ int_array_add_unique(freq_list, chan->freq);
+ }
+}
+
+
int hostapd_drv_do_acs(struct hostapd_data *hapd)
{
struct drv_acs_params params;
@@ -734,6 +781,7 @@
u8 *channels = NULL;
unsigned int num_channels = 0;
struct hostapd_hw_modes *mode;
+ int *freq_list = NULL;
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
return 0;
@@ -749,24 +797,35 @@
acs_ch_list_all = 1;
mode = hapd->iface->current_mode;
- if (mode == NULL)
- return -1;
- channels = os_malloc(mode->num_channels);
- if (channels == NULL)
- return -1;
+ if (mode) {
+ channels = os_malloc(mode->num_channels);
+ if (channels == NULL)
+ return -1;
- for (i = 0; i < mode->num_channels; i++) {
- struct hostapd_channel_data *chan = &mode->channels[i];
- if (!acs_ch_list_all &&
- !freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
- chan->chan))
- continue;
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
- channels[num_channels++] = chan->chan;
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (!acs_ch_list_all &&
+ !freq_range_list_includes(
+ &hapd->iface->conf->acs_ch_list,
+ chan->chan))
+ continue;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+ channels[num_channels++] = chan->chan;
+ int_array_add_unique(&freq_list, chan->freq);
+ }
+ }
+ } else {
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ hostapd_get_hw_mode_any_channels(hapd, mode,
+ acs_ch_list_all,
+ &freq_list);
+ }
}
params.ch_list = channels;
params.ch_list_len = num_channels;
+ params.freq_list = freq_list;
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 82eaf3f..5a1e28e 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -88,6 +88,9 @@
const u8 *key, size_t key_len);
int hostapd_drv_send_mlme(struct hostapd_data *hapd,
const void *msg, size_t len, int noack);
+int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
+ const void *msg, size_t len, int noack,
+ const u16 *csa_offs, size_t csa_offs_len);
int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
const u8 *addr, int reason);
int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 78a1f7c..8bf6dde 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -248,15 +248,12 @@
}
-static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
+void ap_list_timer(struct hostapd_iface *iface)
{
- struct hostapd_iface *iface = eloop_ctx;
struct os_reltime now;
struct ap_info *ap;
int set_beacon = 0;
- eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
-
if (!iface->ap_list)
return;
@@ -305,13 +302,11 @@
int ap_list_init(struct hostapd_iface *iface)
{
- eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
return 0;
}
void ap_list_deinit(struct hostapd_iface *iface)
{
- eloop_cancel_timeout(ap_list_timer, iface, NULL);
hostapd_free_aps(iface);
}
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index 93dc0ed..9e0353c 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -39,6 +39,7 @@
#ifdef NEED_AP_MLME
int ap_list_init(struct hostapd_iface *iface);
void ap_list_deinit(struct hostapd_iface *iface);
+void ap_list_timer(struct hostapd_iface *iface);
#else /* NEED_AP_MLME */
static inline int ap_list_init(struct hostapd_iface *iface)
{
@@ -48,6 +49,10 @@
static inline void ap_list_deinit(struct hostapd_iface *iface)
{
}
+
+static inline void ap_list_timer(struct hostapd_iface *iface)
+{
+}
#endif /* NEED_AP_MLME */
#endif /* AP_LIST_H */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index f10e1b7..c9111f6 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -132,6 +132,7 @@
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
srv.erp_domain = conf->erp_domain;
+ srv.tls_session_lifetime = conf->tls_session_lifetime;
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
@@ -151,9 +152,12 @@
if (hapd->conf->eap_server &&
(hapd->conf->ca_cert || hapd->conf->server_cert ||
hapd->conf->private_key || hapd->conf->dh_file)) {
+ struct tls_config conf;
struct tls_connection_params params;
- hapd->ssl_ctx = tls_init(NULL);
+ os_memset(&conf, 0, sizeof(conf));
+ conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ hapd->ssl_ctx = tls_init(&conf);
if (hapd->ssl_ctx == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize TLS");
authsrv_deinit(hapd);
@@ -189,6 +193,7 @@
if (hapd->conf->eap_sim_db) {
hapd->eap_sim_db_priv =
eap_sim_db_init(hapd->conf->eap_sim_db,
+ hapd->conf->eap_sim_db_timeout,
hostapd_sim_db_cb, hapd);
if (hapd->eap_sim_db_priv == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 51d0c15..9490e21 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -297,65 +297,65 @@
static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
{
- u8 chan;
-
- if (!hapd->cs_freq_params.freq)
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
- if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
- NUM_HOSTAPD_MODES)
+ if (!hapd->cs_freq_params.channel)
return eid;
*eid++ = WLAN_EID_CHANNEL_SWITCH;
*eid++ = 3;
*eid++ = hapd->cs_block_tx;
- *eid++ = chan;
+ *eid++ = hapd->cs_freq_params.channel;
*eid++ = hapd->cs_count;
return eid;
}
-static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
{
- u8 sec_ch;
-
- if (!hapd->cs_freq_params.sec_channel_offset)
+ if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
return eid;
- if (hapd->cs_freq_params.sec_channel_offset == -1)
- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
- else if (hapd->cs_freq_params.sec_channel_offset == 1)
- sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
- else
- return eid;
-
- *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
- *eid++ = 1;
- *eid++ = sec_ch;
+ *eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
+ *eid++ = 4;
+ *eid++ = hapd->cs_block_tx;
+ *eid++ = hapd->iface->cs_oper_class;
+ *eid++ = hapd->cs_freq_params.channel;
+ *eid++ = hapd->cs_count;
return eid;
}
-static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
- u8 *start, unsigned int *csa_counter_off)
+static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
{
- u8 *old_pos = pos;
+ u8 op_class, channel;
- if (!csa_counter_off)
- return pos;
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
+ !hapd->iface->freq)
+ return eid;
- *csa_counter_off = 0;
- pos = hostapd_eid_csa(hapd, pos);
+ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hapd->iconf->vht_oper_chwidth,
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
- if (pos != old_pos) {
- /* save an offset to the counter - should be last byte */
- *csa_counter_off = pos - start - 1;
- pos = hostapd_eid_secondary_channel(hapd, pos);
- }
+ *eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+ *eid++ = 2;
- return pos;
+ /* Current Operating Class */
+ *eid++ = op_class;
+
+ /* TODO: Advertise all the supported operating classes */
+ *eid++ = 0;
+
+ return eid;
}
@@ -364,7 +364,7 @@
int is_p2p, size_t *resp_len)
{
struct ieee80211_mgmt *resp;
- u8 *pos, *epos;
+ u8 *pos, *epos, *csa_pos;
size_t buflen;
#define MAX_PROBERESP_LEN 768
@@ -377,6 +377,10 @@
if (hapd->p2p_probe_resp_ie)
buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies)
+ buflen += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
if (hapd->conf->vendor_vht) {
@@ -420,6 +424,12 @@
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
+ /* CSA IE */
+ csa_pos = hostapd_eid_csa(hapd, pos);
+ if (csa_pos != pos)
+ hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
+ pos = csa_pos;
+
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -433,7 +443,19 @@
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+ /* eCSA IE */
+ csa_pos = hostapd_eid_ecsa(hapd, pos);
+ if (csa_pos != pos)
+ hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
+ pos = csa_pos;
+
+ pos = hostapd_eid_supported_op_classes(hapd, pos);
+
#ifdef CONFIG_IEEE80211N
+ /* Secondary Channel Offset element */
+ /* TODO: The standard doesn't specify a position for this element. */
+ pos = hostapd_eid_secondary_channel(hapd, pos);
+
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
#endif /* CONFIG_IEEE80211N */
@@ -447,12 +469,20 @@
pos = hostapd_eid_adv_proto(hapd, pos);
pos = hostapd_eid_roaming_consortium(hapd, pos);
- pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
- &hapd->cs_c_off_proberesp);
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ pos += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
pos = hostapd_eid_vht_capabilities(hapd, pos);
pos = hostapd_eid_vht_operation(hapd, pos);
+ pos = hostapd_eid_txpower_envelope(hapd, pos);
+ pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
}
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -524,8 +554,8 @@
pos = ssid_list;
end = ssid_list + ssid_list_len;
- while (pos + 1 <= end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos >= 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[1] == 0)
wildcard = 1;
@@ -539,6 +569,102 @@
}
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+ struct os_reltime now;
+ struct hostapd_sta_info *info;
+
+ if (!iface->num_sta_seen)
+ return;
+
+ os_get_reltime(&now);
+ while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+ list))) {
+ if (!force &&
+ !os_reltime_expired(&now, &info->last_seen,
+ iface->conf->track_sta_max_age))
+ break;
+ force = 0;
+
+ wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+ MACSTR, iface->bss[0]->conf->iface,
+ MAC2STR(info->addr));
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+ os_free(info);
+ }
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+ const u8 *addr)
+{
+ struct hostapd_sta_info *info;
+
+ dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+ if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+ return info;
+
+ return NULL;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+{
+ struct hostapd_sta_info *info;
+
+ info = sta_track_get(iface, addr);
+ if (info) {
+ /* Move the most recent entry to the end of the list */
+ dl_list_del(&info->list);
+ dl_list_add_tail(&iface->sta_seen, &info->list);
+ os_get_reltime(&info->last_seen);
+ return;
+ }
+
+ /* Add a new entry */
+ info = os_zalloc(sizeof(*info));
+ os_memcpy(info->addr, addr, ETH_ALEN);
+ os_get_reltime(&info->last_seen);
+
+ if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+ /* Expire oldest entry to make room for a new one */
+ sta_track_expire(iface, 1);
+ }
+
+ wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+ MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+ dl_list_add_tail(&iface->sta_seen, &info->list);
+ iface->num_sta_seen++;
+}
+
+
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+ const char *ifname)
+{
+ struct hapd_interfaces *interfaces = iface->interfaces;
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_data *hapd = NULL;
+
+ iface = interfaces->iface[i];
+ for (j = 0; j < iface->num_bss; j++) {
+ hapd = iface->bss[j];
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ break;
+ hapd = NULL;
+ }
+
+ if (hapd && sta_track_get(iface, addr))
+ return hapd;
+ }
+
+ return NULL;
+}
+
+
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal)
@@ -550,10 +676,15 @@
size_t i, resp_len;
int noack;
enum ssid_match_result res;
+ int ret;
+ u16 csa_offs[2];
+ size_t csa_offs_len;
ie = mgmt->u.probe_req.variable;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
return;
+ if (hapd->iconf->track_sta_max_num)
+ sta_track_add(hapd->iface, mgmt->sa);
ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
@@ -702,6 +833,29 @@
/* TODO: verify that supp_rates contains at least one matching rate
* with AP configuration */
+ if (hapd->conf->no_probe_resp_if_seen_on &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+ sta_track_seen_on(hapd->iface, mgmt->sa,
+ hapd->conf->no_probe_resp_if_seen_on)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since STA has been seen on %s",
+ hapd->conf->iface, MAC2STR(mgmt->sa),
+ hapd->conf->no_probe_resp_if_seen_on);
+ return;
+ }
+
+ if (hapd->conf->no_probe_resp_if_max_sta &&
+ is_multicast_ether_addr(mgmt->da) &&
+ is_multicast_ether_addr(mgmt->bssid) &&
+ hapd->num_sta >= hapd->conf->max_num_sta &&
+ !ap_get_sta(hapd, mgmt->sa)) {
+ wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+ " since no room for additional STA",
+ hapd->conf->iface, MAC2STR(mgmt->sa));
+ return;
+ }
+
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->iconf->ignore_probe_probability > 0.0 &&
drand48() < hapd->iconf->ignore_probe_probability) {
@@ -724,7 +878,22 @@
noack = !!(res == WILDCARD_SSID_MATCH &&
is_broadcast_ether_addr(mgmt->da));
- if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
+ csa_offs_len = 0;
+ if (hapd->csa_in_progress) {
+ if (hapd->cs_c_off_proberesp)
+ csa_offs[csa_offs_len++] =
+ hapd->cs_c_off_proberesp;
+
+ if (hapd->cs_c_off_ecsa_proberesp)
+ csa_offs[csa_offs_len++] =
+ hapd->cs_c_off_ecsa_proberesp;
+ }
+
+ ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack,
+ csa_offs_len ? csa_offs : NULL,
+ csa_offs_len);
+
+ if (ret < 0)
wpa_printf(MSG_INFO, "handle_probe_req: send failed");
os_free(resp);
@@ -783,7 +952,7 @@
size_t resp_len = 0;
#ifdef NEED_AP_MLME
u16 capab_info;
- u8 *pos, *tailpos;
+ u8 *pos, *tailpos, *csa_pos;
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
@@ -797,6 +966,10 @@
if (hapd->p2p_beacon_ie)
tail_len += wpabuf_len(hapd->p2p_beacon_ie);
#endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies)
+ tail_len += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
tail_len += wpabuf_len(hapd->conf->vendor_elements);
@@ -860,6 +1033,12 @@
/* Power Constraint element */
tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+ /* CSA IE */
+ csa_pos = hostapd_eid_csa(hapd, tailpos);
+ if (csa_pos != tailpos)
+ hapd->cs_c_off_beacon = csa_pos - tail - 1;
+ tailpos = csa_pos;
+
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);
@@ -877,7 +1056,19 @@
tailpos = hostapd_eid_bss_load(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE - tailpos);
+ /* eCSA IE */
+ csa_pos = hostapd_eid_ecsa(hapd, tailpos);
+ if (csa_pos != tailpos)
+ hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
+ tailpos = csa_pos;
+
+ tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+
#ifdef CONFIG_IEEE80211N
+ /* Secondary Channel Offset element */
+ /* TODO: The standard doesn't specify a position for this element. */
+ tailpos = hostapd_eid_secondary_channel(hapd, tailpos);
+
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
#endif /* CONFIG_IEEE80211N */
@@ -893,12 +1084,21 @@
tailpos = hostapd_eid_interworking(hapd, tailpos);
tailpos = hostapd_eid_adv_proto(hapd, tailpos);
tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
- tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
- &hapd->cs_c_off_beacon);
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ tailpos += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+ tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
}
if (hapd->conf->vendor_vht)
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 722159a..d98f42e 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,5 +21,10 @@
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params);
void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_expire(struct hostapd_iface *iface, int force);
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+ const char *ifname);
#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 60afcb0..c98978f 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -12,6 +12,7 @@
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "eapol_auth/eapol_auth_sm.h"
+#include "fst/fst_ctrl_iface.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
@@ -206,7 +207,10 @@
return -1;
}
- return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+ ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+ ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+ return ret;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 715f19b..7273caa 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -817,16 +817,6 @@
}
-static int hostapd_csa_in_progress(struct hostapd_iface *iface)
-{
- unsigned int i;
- for (i = 0; i < iface->num_bss; i++)
- if (iface->bss[i]->csa_in_progress)
- return 1;
- return 0;
-}
-
-
static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
{
struct hostapd_channel_data *channel;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 6ecd094..fd07201 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,9 +18,11 @@
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
+#include "fst/fst.h"
#include "wnm_ap.h"
#include "hostapd.h"
#include "ieee802_11.h"
+#include "ieee802_11_auth.h"
#include "sta_info.h"
#include "accounting.h"
#include "tkip_countermeasures.h"
@@ -42,10 +44,10 @@
struct ieee802_11_elems elems;
const u8 *ie;
size_t ielen;
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
u8 *p = buf;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
u16 reason = WLAN_REASON_UNSPECIFIED;
u16 status = WLAN_STATUS_SUCCESS;
const u8 *p2p_dev_addr = NULL;
@@ -58,8 +60,8 @@
* running, so better make sure we stop processing such an
* event here.
*/
- wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
- "no address");
+ wpa_printf(MSG_DEBUG,
+ "hostapd_notif_assoc: Skip event with no address");
return -1;
}
random_add_randomness(addr, ETH_ALEN);
@@ -89,8 +91,8 @@
} else {
ie = NULL;
ielen = 0;
- wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
- "(Re)AssocReq");
+ wpa_printf(MSG_DEBUG,
+ "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
}
sta = ap_get_sta(hapd, addr);
@@ -113,6 +115,14 @@
}
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
+ res = hostapd_check_acl(hapd, addr, NULL);
+ if (res != HOSTAPD_ACL_ACCEPT) {
+ wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+ MAC2STR(addr));
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+
#ifdef CONFIG_P2P
if (elems.p2p) {
wpabuf_free(sta->p2p_ie);
@@ -155,13 +165,20 @@
sta->hs20_ie = NULL;
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+ if (hapd->iface->fst)
+ sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+ else
+ sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
if (hapd->conf->wpa) {
if (ie == NULL || ielen == 0) {
#ifdef CONFIG_WPS
if (hapd->conf->wps_state) {
- wpa_printf(MSG_DEBUG, "STA did not include "
- "WPA/RSN IE in (Re)Association "
- "Request - possible WPS use");
+ wpa_printf(MSG_DEBUG,
+ "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
sta->flags |= WLAN_STA_MAYBE_WPS;
goto skip_wpa_check;
}
@@ -174,13 +191,14 @@
if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
struct wpabuf *wps;
+
sta->flags |= WLAN_STA_WPS;
wps = ieee802_11_vendor_ie_concat(ie, ielen,
WPS_IE_VENDOR_TYPE);
if (wps) {
if (wps_is_20(wps)) {
- wpa_printf(MSG_DEBUG, "WPS: STA "
- "supports WPS 2.0");
+ wpa_printf(MSG_DEBUG,
+ "WPS: STA supports WPS 2.0");
sta->flags |= WLAN_STA_WPS2;
}
wpabuf_free(wps);
@@ -194,16 +212,17 @@
sta->addr,
p2p_dev_addr);
if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
- "machine");
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize WPA state machine");
return -1;
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
ie, ielen,
elems.mdie, elems.mdie_len);
if (res != WPA_IE_OK) {
- wpa_printf(MSG_DEBUG, "WPA/RSN information element "
- "rejected? (res %u)", res);
+ wpa_printf(MSG_DEBUG,
+ "WPA/RSN information element rejected? (res %u)",
+ res);
wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
if (res == WPA_INVALID_GROUP) {
reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
@@ -246,14 +265,12 @@
if (sta->sa_query_count == 0)
ap_sta_start_sa_query(hapd, sta);
-#ifdef CONFIG_IEEE80211R
status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
p - buf);
-#endif /* CONFIG_IEEE80211R */
return 0;
}
@@ -281,6 +298,7 @@
} else if (hapd->conf->wps_state) {
#ifdef CONFIG_WPS
struct wpabuf *wps;
+
if (req_ies)
wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
WPS_IE_VENDOR_TYPE);
@@ -297,8 +315,8 @@
if (wps) {
sta->flags |= WLAN_STA_WPS;
if (wps_is_20(wps)) {
- wpa_printf(MSG_DEBUG, "WPS: STA supports "
- "WPS 2.0");
+ wpa_printf(MSG_DEBUG,
+ "WPS: STA supports WPS 2.0");
sta->flags |= WLAN_STA_WPS2;
}
} else
@@ -320,8 +338,8 @@
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_WARNING, "Failed to initialize WPA "
- "state machine");
+ wpa_printf(MSG_WARNING,
+ "Failed to initialize WPA state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
@@ -393,8 +411,8 @@
* was running, so better make sure we stop processing such an
* event here.
*/
- wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event "
- "with no address");
+ wpa_printf(MSG_DEBUG,
+ "hostapd_notif_disassoc: Skip event with no address");
return;
}
@@ -403,8 +421,9 @@
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
- wpa_printf(MSG_DEBUG, "Disassociation notification for "
- "unknown STA " MACSTR, MAC2STR(addr));
+ wpa_printf(MSG_DEBUG,
+ "Disassociation notification for unknown STA "
+ MACSTR, MAC2STR(addr));
return;
}
@@ -425,8 +444,8 @@
return;
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
- "missing ACKs");
+ HOSTAPD_LEVEL_INFO,
+ "disconnected due to excessive missing ACKs");
hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
if (sta)
ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
@@ -437,7 +456,8 @@
int offset, int width, int cf1, int cf2)
{
#ifdef NEED_AP_MLME
- int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
+ int channel, chwidth, is_dfs;
+ u8 seg0_idx = 0, seg1_idx = 0;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
@@ -450,8 +470,8 @@
channel = hostapd_hw_get_channel(hapd, freq);
if (!channel) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING, "driver switched to "
- "bad channel!");
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel!");
return;
}
@@ -481,8 +501,8 @@
seg1_idx = (cf2 - 5000) / 5;
break;
default:
- seg0_idx = hostapd_hw_get_channel(hapd, cf1);
- seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+ ieee80211_freq_to_chan(cf1, &seg0_idx);
+ ieee80211_freq_to_chan(cf2, &seg1_idx);
break;
}
@@ -688,8 +708,8 @@
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (sta->wpa_sm == NULL) {
- wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
- "state machine");
+ wpa_printf(MSG_DEBUG,
+ "FT: Failed to initialize WPA state machine");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -724,7 +744,7 @@
if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
return; /* handled by the driver */
- wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
+ wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
mgmt->u.action.category, (int) plen);
sta = ap_get_sta(hapd, mgmt->sa);
@@ -735,6 +755,7 @@
#ifdef CONFIG_IEEE80211R
if (mgmt->u.action.category == WLAN_ACTION_FT) {
const u8 *payload = drv_mgmt->frame + 24 + 1;
+
wpa_ft_action_rx(sta->wpa_sm, payload, plen);
}
#endif /* CONFIG_IEEE80211R */
@@ -751,6 +772,13 @@
ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
}
#endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+ if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+ fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+ return;
+ }
+#endif /* CONFIG_FST */
+
}
@@ -802,6 +830,7 @@
if (hapd->ext_mgmt_frame_handling) {
size_t hex_len = 2 * rx_mgmt->frame_len + 1;
char *hex = os_malloc(hex_len);
+
if (hex) {
wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
rx_mgmt->frame_len);
@@ -819,8 +848,7 @@
hapd = get_hapd_bssid(iface, bssid);
if (hapd == NULL) {
- u16 fc;
- fc = le_to_host16(hdr->frame_control);
+ u16 fc = le_to_host16(hdr->frame_control);
/*
* Drop frames to unknown BSSIDs except for Beacon frames which
@@ -839,6 +867,7 @@
if (hapd == HAPD_BROADCAST) {
size_t i;
+
ret = 0;
for (i = 0; i < iface->num_bss; i++) {
/* if bss is set, driver will call this function for
@@ -865,6 +894,7 @@
size_t len, u16 stype, int ok)
{
struct ieee80211_hdr *hdr;
+
hdr = (struct ieee80211_hdr *) buf;
hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
if (hapd == NULL || hapd == HAPD_BROADCAST)
@@ -878,6 +908,7 @@
static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta = ap_get_sta(hapd, addr);
+
if (sta)
return 0;
@@ -904,11 +935,10 @@
size_t j;
for (j = 0; j < iface->num_bss; j++) {
- if ((sta = ap_get_sta(iface->bss[j], src))) {
- if (sta->flags & WLAN_STA_ASSOC) {
- hapd = iface->bss[j];
- break;
- }
+ sta = ap_get_sta(iface->bss[j], src);
+ if (sta && sta->flags & WLAN_STA_ASSOC) {
+ hapd = iface->bss[j];
+ break;
}
}
@@ -968,7 +998,8 @@
if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
return;
- wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+ 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);
@@ -1102,6 +1133,7 @@
data->rx_mgmt.frame_len >= 24) {
const struct ieee80211_hdr *hdr;
u16 fc;
+
hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
fc = le_to_host16(hdr->frame_control);
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 9d19f98..4bcdf6f 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -167,27 +167,107 @@
#endif /* CONFIG_HS20 */
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+ u16 infoid)
+{
+ struct anqp_element *elem;
+
+ dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+ list) {
+ if (elem->infoid == infoid)
+ return elem;
+ }
+
+ return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+ u16 infoid)
+{
+ struct anqp_element *elem;
+
+ elem = get_anqp_elem(hapd, infoid);
+ if (!elem)
+ return;
+ if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+ wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+ infoid);
+ return;
+ }
+
+ wpabuf_put_le16(buf, infoid);
+ wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+ wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+ u16 infoid)
+{
+ if (get_anqp_elem(hapd, infoid)) {
+ anqp_add_elem(hapd, buf, infoid);
+ return 1;
+ }
+
+ return 0;
+}
+
+
static void anqp_add_capab_list(struct hostapd_data *hapd,
struct wpabuf *buf)
{
u8 *len;
+ u16 id;
+
+ if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+ return;
len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
- if (hapd->conf->venue_name)
+ if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
wpabuf_put_le16(buf, ANQP_VENUE_NAME);
- if (hapd->conf->network_auth_type)
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+ if (hapd->conf->network_auth_type ||
+ get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
- if (hapd->conf->roaming_consortium)
+ if (hapd->conf->roaming_consortium ||
+ get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
- if (hapd->conf->ipaddr_type_configured)
+ if (hapd->conf->ipaddr_type_configured ||
+ get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
- if (hapd->conf->nai_realm_data)
+ if (hapd->conf->nai_realm_data ||
+ get_anqp_elem(hapd, ANQP_NAI_REALM))
wpabuf_put_le16(buf, ANQP_NAI_REALM);
- if (hapd->conf->anqp_3gpp_cell_net)
+ if (hapd->conf->anqp_3gpp_cell_net ||
+ get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
- if (hapd->conf->domain_name)
+ if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+ wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+ if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+ wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+ if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+ wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+ if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+ if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+ wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+ if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+ wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+ for (id = 273; id < 277; id++) {
+ if (get_anqp_elem(hapd, id))
+ wpabuf_put_le16(buf, id);
+ }
+ if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+ wpabuf_put_le16(buf, ANQP_VENUE_URL);
+ if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+ wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+ if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+ wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
#ifdef CONFIG_HS20
anqp_add_hs_capab_list(hapd, buf);
#endif /* CONFIG_HS20 */
@@ -197,6 +277,9 @@
static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
{
+ if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+ return;
+
if (hapd->conf->venue_name) {
u8 *len;
unsigned int i;
@@ -218,6 +301,9 @@
static void anqp_add_network_auth_type(struct hostapd_data *hapd,
struct wpabuf *buf)
{
+ if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+ return;
+
if (hapd->conf->network_auth_type) {
wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
@@ -233,6 +319,9 @@
unsigned int i;
u8 *len;
+ if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+ return;
+
len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
struct hostapd_roaming_consortium *rc;
@@ -247,6 +336,9 @@
static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
struct wpabuf *buf)
{
+ if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+ return;
+
if (hapd->conf->ipaddr_type_configured) {
wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
wpabuf_put_le16(buf, 1);
@@ -309,7 +401,7 @@
pos = home_realm;
end = pos + home_realm_len;
- if (pos + 1 > end) {
+ if (end - pos < 1) {
wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
home_realm, home_realm_len);
return -1;
@@ -317,7 +409,7 @@
num_realms = *pos++;
for (i = 0; i < num_realms && num_matching < 10; i++) {
- if (pos + 2 > end) {
+ if (end - pos < 2) {
wpa_hexdump(MSG_DEBUG,
"Truncated NAI Home Realm Query",
home_realm, home_realm_len);
@@ -325,7 +417,7 @@
}
encoding = *pos++;
realm_len = *pos++;
- if (pos + realm_len > end) {
+ if (realm_len > end - pos) {
wpa_hexdump(MSG_DEBUG,
"Truncated NAI Home Realm Query",
home_realm, home_realm_len);
@@ -391,6 +483,10 @@
const u8 *home_realm, size_t home_realm_len,
int nai_realm, int nai_home_realm)
{
+ if (nai_realm && !nai_home_realm &&
+ anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+ return;
+
if (nai_realm && hapd->conf->nai_realm_data) {
u8 *len;
unsigned int i, j;
@@ -424,6 +520,9 @@
static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
struct wpabuf *buf)
{
+ if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+ return;
+
if (hapd->conf->anqp_3gpp_cell_net) {
wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
wpabuf_put_le16(buf,
@@ -436,6 +535,9 @@
static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
{
+ if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+ return;
+
if (hapd->conf->domain_name) {
wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
wpabuf_put_le16(buf, hapd->conf->domain_name_len);
@@ -687,16 +789,20 @@
gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
unsigned int request,
const u8 *home_realm, size_t home_realm_len,
- const u8 *icon_name, size_t icon_name_len)
+ const u8 *icon_name, size_t icon_name_len,
+ const u16 *extra_req,
+ unsigned int num_extra_req)
{
struct wpabuf *buf;
size_t len;
+ unsigned int i;
len = 1400;
if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
len += 1000;
if (request & ANQP_REQ_ICON_REQUEST)
len += 65536;
+ len += num_extra_req * 1000;
buf = wpabuf_alloc(len);
if (buf == NULL)
@@ -706,6 +812,8 @@
anqp_add_capab_list(hapd, buf);
if (request & ANQP_REQ_VENUE_NAME)
anqp_add_venue_name(hapd, buf);
+ if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
anqp_add_network_auth_type(hapd, buf);
if (request & ANQP_REQ_ROAMING_CONSORTIUM)
@@ -718,8 +826,23 @@
request & ANQP_REQ_NAI_HOME_REALM);
if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
anqp_add_3gpp_cellular_network(hapd, buf);
+ if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+ anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+ if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+ anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+ if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+ anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
if (request & ANQP_REQ_DOMAIN_NAME)
anqp_add_domain_name(hapd, buf);
+ if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+ if (request & ANQP_REQ_TDLS_CAPABILITY)
+ anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+ if (request & ANQP_REQ_EMERGENCY_NAI)
+ anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+ for (i = 0; i < num_extra_req; i++)
+ anqp_add_elem(hapd, buf, extra_req[i]);
#ifdef CONFIG_HS20
if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -742,6 +865,8 @@
}
+#define ANQP_MAX_EXTRA_REQ 20
+
struct anqp_query_info {
unsigned int request;
const u8 *home_realm_query;
@@ -749,6 +874,8 @@
const u8 *icon_name;
size_t icon_name_len;
int p2p_sd;
+ u16 extra_req[ANQP_MAX_EXTRA_REQ];
+ unsigned int num_extra_req;
};
@@ -776,6 +903,11 @@
set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
hapd->conf->venue_name != NULL, qi);
break;
+ case ANQP_EMERGENCY_CALL_NUMBER:
+ set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+ "Emergency Call Number",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
case ANQP_NETWORK_AUTH_TYPE:
set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
hapd->conf->network_auth_type != NULL, qi);
@@ -798,13 +930,55 @@
"3GPP Cellular Network",
hapd->conf->anqp_3gpp_cell_net != NULL, qi);
break;
+ case ANQP_AP_GEOSPATIAL_LOCATION:
+ set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+ "AP Geospatial Location",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_AP_CIVIC_LOCATION:
+ set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+ "AP Civic Location",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_AP_LOCATION_PUBLIC_URI:
+ set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+ "AP Location Public URI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
case ANQP_DOMAIN_NAME:
set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
hapd->conf->domain_name != NULL, qi);
break;
+ case ANQP_EMERGENCY_ALERT_URI:
+ set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+ "Emergency Alert URI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_TDLS_CAPABILITY:
+ set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+ "TDLS Capability",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
+ case ANQP_EMERGENCY_NAI:
+ set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+ "Emergency NAI",
+ get_anqp_elem(hapd, info_id) != NULL, qi);
+ break;
default:
- wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
- info_id);
+ if (!get_anqp_elem(hapd, info_id)) {
+ wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+ info_id);
+ break;
+ }
+ if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: No more room for extra requests - ignore Info Id %u",
+ info_id);
+ break;
+ }
+ wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+ qi->extra_req[qi->num_extra_req] = info_id;
+ qi->num_extra_req++;
break;
}
}
@@ -817,7 +991,7 @@
wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
(unsigned int) (end - pos) / 2);
- while (pos + 2 <= end) {
+ while (end - pos >= 2) {
rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
pos += 2;
}
@@ -906,7 +1080,7 @@
u32 oui;
u8 subtype;
- if (pos + 4 > end) {
+ if (end - pos < 4) {
wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
"Query element");
return;
@@ -942,7 +1116,7 @@
}
pos++;
- if (pos + 1 >= end)
+ if (end - pos <= 1)
return;
subtype = *pos++;
@@ -980,7 +1154,8 @@
buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
qi->home_realm_query,
qi->home_realm_query_len,
- qi->icon_name, qi->icon_name_len);
+ qi->icon_name, qi->icon_name_len,
+ qi->extra_req, qi->num_extra_req);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf);
if (!buf)
@@ -1069,12 +1244,12 @@
adv_proto = pos++;
slen = *pos++;
- next = pos + slen;
- if (next > end || slen < 2) {
+ if (slen > end - pos || slen < 2) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"GAS: Invalid IE in GAS Initial Request");
return;
}
+ next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -1101,11 +1276,11 @@
pos = next;
/* Query Request */
- if (pos + 2 > end)
+ if (end - pos < 2)
return;
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end)
+ if (slen > end - pos)
return;
end = pos + slen;
@@ -1113,7 +1288,7 @@
while (pos < end) {
u16 info_id, elen;
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
info_id = WPA_GET_LE16(pos);
@@ -1121,7 +1296,7 @@
elen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + elen > end) {
+ if (elen > end - pos) {
wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
return;
}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4ec3201..9051e4f 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -9,10 +9,13 @@
#ifndef GAS_SERV_H
#define GAS_SERV_H
+/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
#define ANQP_REQ_CAPABILITY_LIST \
(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
#define ANQP_REQ_VENUE_NAME \
(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
+ (1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
#define ANQP_REQ_NETWORK_AUTH_TYPE \
(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
#define ANQP_REQ_ROAMING_CONSORTIUM \
@@ -23,8 +26,24 @@
(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
#define ANQP_REQ_3GPP_CELLULAR_NETWORK \
(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
+ (1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_CIVIC_LOCATION \
+ (1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
+ (1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
#define ANQP_REQ_DOMAIN_NAME \
(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_ALERT_URI \
+ (1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_TDLS_CAPABILITY \
+ (1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_NAI \
+ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+/*
+ * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * optimized bitmap.
+ */
#define ANQP_REQ_HS_CAPABILITY_LIST \
(0x10000 << HS20_STYPE_CAPABILITY_LIST)
#define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5abe5ed..b10b454 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -12,11 +12,13 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
#include "radius/radius_client.h"
#include "radius/radius_das.h"
#include "eap_server/tncs.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
#include "hostapd.h"
#include "authsrv.h"
#include "sta_info.h"
@@ -260,6 +262,7 @@
{
os_free(hapd->probereq_cb);
hapd->probereq_cb = NULL;
+ hapd->num_probereq_cb = 0;
#ifdef CONFIG_P2P
wpabuf_free(hapd->p2p_beacon_ie);
@@ -354,6 +357,22 @@
}
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+ struct hostapd_sta_info *info;
+
+ if (!iface->num_sta_seen)
+ return;
+
+ while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+ list))) {
+ dl_list_del(&info->list);
+ iface->num_sta_seen--;
+ os_free(info);
+ }
+}
+
+
static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
@@ -369,6 +388,7 @@
os_free(iface->basic_rates);
iface->basic_rates = NULL;
ap_list_deinit(iface);
+ sta_track_deinit(iface);
}
@@ -1364,15 +1384,134 @@
}
-/**
- * hostapd_setup_interface_complete - Complete interface setup
- *
- * This function is called when previous steps in the interface setup has been
- * completed. This can also start operations, e.g., DFS, that will require
- * additional processing before interface is ready to be enabled. Such
- * operations will call this function from eloop callbacks when finished.
- */
-int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ struct hostapd_data *hapd = ctx;
+
+ *hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+ struct hostapd_data *hapd = ctx;
+
+ if (hapd->iface->fst_ies != fst_ies) {
+ hapd->iface->fst_ies = fst_ies;
+ if (ieee802_11_set_beacon(hapd))
+ wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+ }
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+ struct wpabuf *buf)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+ wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta = ap_get_sta(hapd, addr);
+
+ if (sta) {
+ struct mb_ies_info info;
+
+ if (!mb_ies_info_by_ies(&info, buf, size)) {
+ wpabuf_free(sta->mb_ies);
+ sta->mb_ies = mb_ies_by_info(&info);
+ }
+ }
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ struct sta_info *s = (struct sta_info *) *get_ctx;
+
+ if (mb_only) {
+ for (; s && !s->mb_ies; s = s->next)
+ ;
+ }
+
+ if (s) {
+ *get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+ return s->addr;
+ }
+
+ *get_ctx = NULL;
+ return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ struct hostapd_data *hapd = ctx;
+
+ *get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+ return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ struct fst_wpa_obj *iface_obj)
+{
+ iface_obj->ctx = hapd;
+ iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+ iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+ iface_obj->set_ies = fst_hostapd_set_ies_cb;
+ iface_obj->send_action = fst_hostapd_send_action_cb;
+ iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+ iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+ iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+ iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+
+static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+ int err)
{
struct hostapd_data *hapd = iface->bss[0];
size_t j;
@@ -1496,6 +1635,7 @@
hostapd_tx_queue_params(iface);
ap_list_init(iface);
+ dl_list_init(&iface->sta_seen);
hostapd_set_acl(hapd);
@@ -1529,6 +1669,22 @@
#ifdef NEED_AP_MLME
dfs_offload:
#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+ if (hapd->iconf->fst_cfg.group_id[0]) {
+ struct fst_wpa_obj iface_obj;
+
+ fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+ iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+ &iface_obj, &hapd->iconf->fst_cfg);
+ if (!iface->fst) {
+ wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+ hapd->iconf->fst_cfg.group_id);
+ goto fail;
+ }
+ }
+#endif /* CONFIG_FST */
+
hostapd_set_state(iface, HAPD_IFACE_ENABLED);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
if (hapd->setup_complete_cb)
@@ -1545,6 +1701,12 @@
wpa_printf(MSG_ERROR, "Interface initialization failed");
hostapd_set_state(iface, HAPD_IFACE_DISABLED);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_FST
+ if (iface->fst) {
+ fst_detach(iface->fst);
+ iface->fst = NULL;
+ }
+#endif /* CONFIG_FST */
if (iface->interfaces && iface->interfaces->terminate_on_error)
eloop_terminate();
return -1;
@@ -1552,6 +1714,89 @@
/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+ struct hapd_interfaces *interfaces = iface->interfaces;
+ struct hostapd_data *hapd = iface->bss[0];
+ unsigned int i;
+ int not_ready_in_sync_ifaces = 0;
+
+ if (!iface->need_to_start_in_sync)
+ return hostapd_setup_interface_complete_sync(iface, err);
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ iface->need_to_start_in_sync = 0;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (interfaces && interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
+ }
+
+ if (iface->ready_to_start_in_sync) {
+ /* Already in ready and waiting. should never happpen */
+ return 0;
+ }
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (interfaces->iface[i]->need_to_start_in_sync &&
+ !interfaces->iface[i]->ready_to_start_in_sync)
+ not_ready_in_sync_ifaces++;
+ }
+
+ /*
+ * Check if this is the last interface, if yes then start all the other
+ * waiting interfaces. If not, add this interface to the waiting list.
+ */
+ if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
+ /*
+ * If this interface went through CAC, do not synchronize, just
+ * start immediately.
+ */
+ iface->need_to_start_in_sync = 0;
+ wpa_printf(MSG_INFO,
+ "%s: Finished CAC - bypass sync and start interface",
+ iface->bss[0]->conf->iface);
+ return hostapd_setup_interface_complete_sync(iface, err);
+ }
+
+ if (not_ready_in_sync_ifaces > 1) {
+ /* need to wait as there are other interfaces still coming up */
+ iface->ready_to_start_in_sync = 1;
+ wpa_printf(MSG_INFO,
+ "%s: Interface waiting to sync with other interfaces",
+ iface->bss[0]->conf->iface);
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO,
+ "%s: Last interface to sync - starting all interfaces",
+ iface->bss[0]->conf->iface);
+ iface->need_to_start_in_sync = 0;
+ hostapd_setup_interface_complete_sync(iface, err);
+ for (i = 0; i < interfaces->count; i++) {
+ if (interfaces->iface[i]->need_to_start_in_sync &&
+ interfaces->iface[i]->ready_to_start_in_sync) {
+ hostapd_setup_interface_complete_sync(
+ interfaces->iface[i], 0);
+ /* Only once the interfaces are sync started */
+ interfaces->iface[i]->need_to_start_in_sync = 0;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
* hostapd_setup_interface - Setup of an interface
* @iface: Pointer to interface data.
* Returns: 0 on success, -1 on failure
@@ -1644,6 +1889,13 @@
eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
iface->wait_channel_update = 0;
+#ifdef CONFIG_FST
+ if (iface->fst) {
+ fst_detach(iface->fst);
+ iface->fst = NULL;
+ }
+#endif /* CONFIG_FST */
+
for (j = iface->num_bss - 1; j >= 0; j--)
hostapd_bss_deinit(iface->bss[j]);
}
@@ -2030,7 +2282,7 @@
static struct hostapd_config *
hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
- const char *ctrl_iface)
+ const char *ctrl_iface, const char *driver)
{
struct hostapd_bss_config *bss;
struct hostapd_config *conf;
@@ -2043,6 +2295,21 @@
return NULL;
}
+ if (driver) {
+ int j;
+
+ for (j = 0; wpa_drivers[j]; j++) {
+ if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+ conf->driver = wpa_drivers[j];
+ goto skip;
+ }
+ }
+
+ wpa_printf(MSG_ERROR,
+ "Invalid/unknown driver '%s' - registering the default driver",
+ driver);
+ }
+
conf->driver = wpa_drivers[0];
if (conf->driver == NULL) {
wpa_printf(MSG_ERROR, "No driver wrappers registered!");
@@ -2050,6 +2317,7 @@
return NULL;
}
+skip:
bss = conf->last_bss = conf->bss[0];
os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
@@ -2210,8 +2478,14 @@
if (conf && conf->bss)
os_strlcpy(conf->bss[0]->iface, buf,
sizeof(conf->bss[0]->iface));
- } else
- conf = hostapd_config_alloc(interfaces, buf, ptr);
+ } else {
+ char *driver = os_strchr(ptr, ' ');
+
+ if (driver)
+ *driver++ = '\0';
+ conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+ }
+
if (conf == NULL || conf->bss == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for configuration", __func__);
@@ -2436,6 +2710,17 @@
}
+int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+ unsigned int i;
+
+ for (i = 0; i < iface->num_bss; i++)
+ if (iface->bss[i]->csa_in_progress)
+ return 1;
+ return 0;
+}
+
+
#ifdef NEED_AP_MLME
static void free_beacon_data(struct beacon_data *beacon)
@@ -2547,9 +2832,9 @@
/*
- * TODO: This flow currently supports only changing frequency within the
- * same hw_mode. Any other changes to MAC parameters or provided settings (even
- * width) are not supported.
+ * TODO: This flow currently supports only changing channel and width within
+ * the same hw_mode. Any other changes to MAC parameters or provided settings
+ * are not supported.
*/
static int hostapd_change_config_freq(struct hostapd_data *hapd,
struct hostapd_config *conf,
@@ -2568,15 +2853,44 @@
return -1;
/* if a pointer to old_params is provided we save previous state */
- if (old_params) {
- old_params->channel = conf->channel;
- old_params->ht_enabled = conf->ieee80211n;
- old_params->sec_channel_offset = conf->secondary_channel;
+ if (old_params &&
+ hostapd_set_freq_params(old_params, conf->hw_mode,
+ hostapd_hw_get_freq(hapd, conf->channel),
+ conf->channel, conf->ieee80211n,
+ conf->ieee80211ac,
+ conf->secondary_channel,
+ conf->vht_oper_chwidth,
+ conf->vht_oper_centr_freq_seg0_idx,
+ conf->vht_oper_centr_freq_seg1_idx,
+ conf->vht_capab))
+ return -1;
+
+ switch (params->bandwidth) {
+ case 0:
+ case 20:
+ case 40:
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+ break;
+ case 80:
+ if (params->center_freq2)
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+ else
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+ break;
+ case 160:
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+ break;
+ default:
+ return -1;
}
conf->channel = channel;
conf->ieee80211n = params->ht_enabled;
conf->secondary_channel = params->sec_channel_offset;
+ ieee80211_freq_to_chan(params->center_freq1,
+ &conf->vht_oper_centr_freq_seg0_idx);
+ ieee80211_freq_to_chan(params->center_freq2,
+ &conf->vht_oper_centr_freq_seg1_idx);
/* TODO: maybe call here hostapd_config_check here? */
@@ -2590,11 +2904,43 @@
struct hostapd_iface *iface = hapd->iface;
struct hostapd_freq_params old_freq;
int ret;
+ u8 chan, vht_bandwidth;
os_memset(&old_freq, 0, sizeof(old_freq));
if (!iface || !iface->freq || hapd->csa_in_progress)
return -1;
+ switch (settings->freq_params.bandwidth) {
+ case 80:
+ if (settings->freq_params.center_freq2)
+ vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
+ else
+ vht_bandwidth = VHT_CHANWIDTH_80MHZ;
+ break;
+ case 160:
+ vht_bandwidth = VHT_CHANWIDTH_160MHZ;
+ break;
+ default:
+ vht_bandwidth = VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+
+ if (ieee80211_freq_to_channel_ext(
+ settings->freq_params.freq,
+ settings->freq_params.sec_channel_offset,
+ vht_bandwidth,
+ &hapd->iface->cs_oper_class,
+ &chan) == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_DEBUG,
+ "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
+ settings->freq_params.freq,
+ settings->freq_params.sec_channel_offset,
+ settings->freq_params.vht_enabled);
+ return -1;
+ }
+
+ settings->freq_params.channel = chan;
+
ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
&settings->freq_params,
&old_freq);
@@ -2621,8 +2967,10 @@
return ret;
}
- settings->counter_offset_beacon = hapd->cs_c_off_beacon;
- settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+ settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+ settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+ settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+ settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
return 0;
}
@@ -2636,6 +2984,8 @@
hapd->cs_c_off_beacon = 0;
hapd->cs_c_off_proberesp = 0;
hapd->csa_in_progress = 0;
+ hapd->cs_c_off_ecsa_beacon = 0;
+ hapd->cs_c_off_ecsa_proberesp = 0;
}
@@ -2723,4 +3073,43 @@
hostapd_enable_iface(iface);
}
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname)
+{
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ return hapd;
+ }
+ }
+
+ return NULL;
+}
+
#endif /* NEED_AP_MLME */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+ size_t i;
+
+ ap_list_timer(iface);
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+
+ if (!hapd->started)
+ continue;
+
+#ifndef CONFIG_NO_RADIUS
+ hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+ }
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dc71694..8161a59 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -228,6 +228,8 @@
unsigned int cs_c_off_beacon;
unsigned int cs_c_off_proberesp;
int csa_in_progress;
+ unsigned int cs_c_off_ecsa_beacon;
+ unsigned int cs_c_off_ecsa_proberesp;
/* BSS Load */
unsigned int bss_load_update_timeout;
@@ -269,6 +271,7 @@
/** Key used for generating SAE anti-clogging tokens */
u8 sae_token_key[8];
struct os_reltime last_sae_token_key_update;
+ int dot11RSNASAERetransPeriod; /* msec */
#endif /* CONFIG_SAE */
#ifdef CONFIG_TESTING_OPTIONS
@@ -280,6 +283,12 @@
};
+struct hostapd_sta_info {
+ struct dl_list list;
+ u8 addr[ETH_ALEN];
+ struct os_reltime last_seen;
+};
+
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
@@ -309,6 +318,10 @@
unsigned int wait_channel_update:1;
unsigned int cac_started:1;
+#ifdef CONFIG_FST
+ struct fst_iface *fst;
+ const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
/*
* When set, indicates that the driver will handle the AP
@@ -316,6 +329,15 @@
*/
unsigned int driver_ap_teardown:1;
+ /*
+ * When set, indicates that this interface is part of list of
+ * interfaces that need to be started together (synchronously).
+ */
+ unsigned int need_to_start_in_sync:1;
+
+ /* Ready to start but waiting for other interfaces to become ready. */
+ unsigned int ready_to_start_in_sync:1;
+
int num_ap; /* number of entries in ap_list */
struct ap_info *ap_list; /* AP info list head */
struct ap_info *ap_hash[STA_HASH_SIZE];
@@ -391,6 +413,9 @@
u64 last_channel_time_busy;
u8 channel_utilization;
+ /* eCSA IE will be added only if operating class is specified */
+ u8 cs_oper_class;
+
unsigned int dfs_cac_ms;
struct os_reltime dfs_cac_start;
@@ -404,6 +429,9 @@
void (*scan_cb)(struct hostapd_iface *iface);
int num_ht40_scan_tries;
+
+ struct dl_list sta_seen; /* struct hostapd_sta_info */
+ unsigned int num_sta_seen;
};
/* hostapd.c */
@@ -435,12 +463,14 @@
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_csa_in_progress(struct hostapd_iface *iface);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params);
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -468,4 +498,12 @@
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2);
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+ const char *ifname);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+ struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 28324a8..3607066 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -260,8 +260,14 @@
res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
- if (res == 2)
- ieee80211n_switch_pri_sec(iface);
+ if (res == 2) {
+ if (iface->conf->no_pri_sec_switch) {
+ wpa_printf(MSG_DEBUG,
+ "Cannot switch PRI/SEC channels due to local constraint");
+ } else {
+ ieee80211n_switch_pri_sec(iface);
+ }
+ }
return !!res;
}
@@ -466,8 +472,9 @@
struct wpa_driver_scan_params params;
int ret;
- if (!iface->conf->secondary_channel)
- return 0; /* HT40 not used */
+ /* Check that HT40 is used and PRI / SEC switch is allowed */
+ if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+ return 0;
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -726,6 +733,15 @@
int ret;
if (!iface->conf->ieee80211n)
return 0;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
+ (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+ wpa_printf(MSG_DEBUG,
+ "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
+ iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
+ }
+
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#ifdef CONFIG_IEEE80211AC
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index db20c86..01b514e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -23,6 +23,7 @@
#include "radius/radius_client.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
+#include "fst/fst.h"
#include "hostapd.h"
#include "beacon.h"
#include "ieee802_11_auth.h"
@@ -38,6 +39,7 @@
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "wnm_ap.h"
+#include "hw_features.h"
#include "ieee802_11.h"
#include "dfs.h"
@@ -191,6 +193,7 @@
}
+#ifndef CONFIG_NO_RC4
static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
u16 auth_transaction, const u8 *challenge,
int iswep)
@@ -244,6 +247,7 @@
return 0;
}
+#endif /* CONFIG_NO_RC4 */
static void send_auth_reply(struct hostapd_data *hapd,
@@ -313,7 +317,6 @@
#ifdef CONFIG_SAE
-#define dot11RSNASAERetransPeriod 40 /* msec */
#define dot11RSNASAESync 5 /* attempts */
@@ -496,12 +499,14 @@
switch (sta->sae->state) {
case SAE_COMMITTED:
ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
- eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
case SAE_CONFIRMED:
ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
- eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ eloop_register_timeout(0,
+ hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
default:
@@ -527,7 +532,7 @@
return;
eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
- eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
}
@@ -925,6 +930,16 @@
challenge ? " challenge" : "",
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+#ifdef CONFIG_NO_RC4
+ if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+ wpa_printf(MSG_INFO,
+ "Unsupported authentication algorithm (%d)",
+ auth_alg);
+ resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+ goto fail;
+ }
+#endif /* CONFIG_NO_RC4 */
+
if (hapd->tkip_countermeasures) {
resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
goto fail;
@@ -963,6 +978,61 @@
goto fail;
}
+ if (hapd->conf->no_auth_if_seen_on) {
+ struct hostapd_data *other;
+
+ other = sta_track_seen_on(hapd->iface, mgmt->sa,
+ hapd->conf->no_auth_if_seen_on);
+ if (other) {
+ u8 *pos;
+ u32 info;
+ u8 op_class, channel, phytype;
+
+ wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
+ MACSTR " since STA has been seen on %s",
+ hapd->conf->iface, MAC2STR(mgmt->sa),
+ hapd->conf->no_auth_if_seen_on);
+
+ resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
+ pos = &resp_ies[0];
+ *pos++ = WLAN_EID_NEIGHBOR_REPORT;
+ *pos++ = 13;
+ os_memcpy(pos, other->own_addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ info = 0; /* TODO: BSSID Information */
+ WPA_PUT_LE32(pos, info);
+ pos += 4;
+ if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+ phytype = 8; /* dmg */
+ else if (other->iconf->ieee80211ac)
+ phytype = 9; /* vht */
+ else if (other->iconf->ieee80211n)
+ phytype = 7; /* ht */
+ else if (other->iconf->hw_mode ==
+ HOSTAPD_MODE_IEEE80211A)
+ phytype = 4; /* ofdm */
+ else if (other->iconf->hw_mode ==
+ HOSTAPD_MODE_IEEE80211G)
+ phytype = 6; /* erp */
+ else
+ phytype = 5; /* hrdsss */
+ if (ieee80211_freq_to_channel_ext(
+ hostapd_hw_get_freq(other,
+ other->iconf->channel),
+ other->iconf->secondary_channel,
+ other->iconf->ieee80211ac,
+ &op_class, &channel) == NUM_HOSTAPD_MODES) {
+ op_class = 0;
+ channel = other->iconf->channel;
+ }
+ *pos++ = op_class;
+ *pos++ = channel;
+ *pos++ = phytype;
+ resp_ies_len = pos - &resp_ies[0];
+ goto fail;
+ }
+ }
+
res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
&session_timeout,
&acct_interim_interval, &vlan_id,
@@ -1072,6 +1142,7 @@
sta->auth_alg = WLAN_AUTH_OPEN;
mlme_authenticate_indication(hapd, sta);
break;
+#ifndef CONFIG_NO_RC4
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
fc & WLAN_FC_ISWEP);
@@ -1085,6 +1156,7 @@
resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
}
break;
+#endif /* CONFIG_NO_RC4 */
#ifdef CONFIG_IEEE80211R
case WLAN_AUTH_FT:
sta->auth_alg = WLAN_AUTH_FT;
@@ -1138,7 +1210,7 @@
}
-static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
{
int i, j = 32, aid;
@@ -1255,6 +1327,9 @@
}
#endif /* CONFIG_INTERWORKING */
+ if (ext_capab_ie_len > 0)
+ sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+
return WLAN_STATUS_SUCCESS;
}
@@ -1301,13 +1376,15 @@
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
- resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
- if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ if (hapd->iconf->ieee80211ac) {
+ resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
- resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
- if (resp != WLAN_STATUS_SUCCESS)
- return resp;
+ resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
!(sta->flags & WLAN_STA_VHT)) {
@@ -1535,6 +1612,14 @@
sta->hs20_ie = NULL;
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+ if (hapd->iface->fst)
+ sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+ else
+ sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
return WLAN_STATUS_SUCCESS;
}
@@ -1623,6 +1708,14 @@
if (sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
+#ifdef CONFIG_FST
+ if (hapd->iface->fst_ies) {
+ os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
+ wpabuf_len(hapd->iface->fst_ies));
+ p += wpabuf_len(hapd->iface->fst_ies);
+ }
+#endif /* CONFIG_FST */
+
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
p = hostapd_eid_vendor_vht(hapd, p);
@@ -2101,6 +2194,15 @@
ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
return 1;
#endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+ case WLAN_ACTION_FST:
+ if (hapd->iface->fst)
+ fst_rx_action(hapd->iface->fst, mgmt, len);
+ else
+ wpa_printf(MSG_DEBUG,
+ "FST: Ignore FST Action frame - no FST attached");
+ return 1;
+#endif /* CONFIG_FST */
case WLAN_ACTION_PUBLIC:
case WLAN_ACTION_PROTECTED_DUAL:
#ifdef CONFIG_IEEE80211N
@@ -2238,6 +2340,9 @@
return 0;
}
+ if (hapd->iconf->track_sta_max_num)
+ sta_track_add(hapd->iface, mgmt->sa);
+
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 44c1bff..0020ff5 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -49,9 +49,13 @@
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id);
@@ -61,6 +65,7 @@
void hostapd_get_vht_capab(struct hostapd_data *hapd,
struct ieee80211_vht_capabilities *vht_cap,
struct ieee80211_vht_capabilities *neg_vht_cap);
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 0238257..b7e7ce3 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -213,6 +213,32 @@
/**
+ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id)
+{
+ if (hostapd_maclist_found(hapd->conf->accept_mac,
+ hapd->conf->num_accept_mac, addr, vlan_id))
+ return HOSTAPD_ACL_ACCEPT;
+
+ if (hostapd_maclist_found(hapd->conf->deny_mac,
+ hapd->conf->num_deny_mac, addr, vlan_id))
+ return HOSTAPD_ACL_REJECT;
+
+ if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+ return HOSTAPD_ACL_ACCEPT;
+ if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+ return HOSTAPD_ACL_REJECT;
+
+ return HOSTAPD_ACL_PENDING;
+}
+
+
+/**
* hostapd_allowed_address - Check whether a specified STA can be authenticated
* @hapd: hostapd BSS data
* @addr: MAC address of the STA
@@ -235,6 +261,8 @@
struct hostapd_sta_wpa_psk_short **psk,
char **identity, char **radius_cui)
{
+ int res;
+
if (session_timeout)
*session_timeout = 0;
if (acct_interim_interval)
@@ -248,18 +276,9 @@
if (radius_cui)
*radius_cui = NULL;
- if (hostapd_maclist_found(hapd->conf->accept_mac,
- hapd->conf->num_accept_mac, addr, vlan_id))
- return HOSTAPD_ACL_ACCEPT;
-
- if (hostapd_maclist_found(hapd->conf->deny_mac,
- hapd->conf->num_deny_mac, addr, vlan_id))
- return HOSTAPD_ACL_REJECT;
-
- if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
- return HOSTAPD_ACL_ACCEPT;
- if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
- return HOSTAPD_ACL_REJECT;
+ res = hostapd_check_acl(hapd, addr, vlan_id);
+ if (res != HOSTAPD_ACL_PENDING)
+ return res;
if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
#ifdef CONFIG_NO_RADIUS
@@ -268,10 +287,9 @@
struct hostapd_acl_query_data *query;
/* Check whether ACL cache has an entry for this station */
- int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
- acct_interim_interval,
- vlan_id, psk,
- identity, radius_cui);
+ res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+ acct_interim_interval, vlan_id, psk,
+ identity, radius_cui);
if (res == HOSTAPD_ACL_ACCEPT ||
res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
return res;
@@ -399,19 +417,15 @@
/**
* hostapd_acl_expire - ACL cache expiration callback
- * @eloop_ctx: struct hostapd_data *
- * @timeout_ctx: Not used
+ * @hapd: struct hostapd_data *
*/
-static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
+void hostapd_acl_expire(struct hostapd_data *hapd)
{
- struct hostapd_data *hapd = eloop_ctx;
struct os_reltime now;
os_get_reltime(&now);
hostapd_acl_expire_cache(hapd, &now);
hostapd_acl_expire_queries(hapd, &now);
-
- eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
}
@@ -615,8 +629,6 @@
if (radius_client_register(hapd->radius, RADIUS_AUTH,
hostapd_acl_recv_radius, hapd))
return -1;
-
- eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
#endif /* CONFIG_NO_RADIUS */
return 0;
@@ -632,8 +644,6 @@
struct hostapd_acl_query_data *query, *prev;
#ifndef CONFIG_NO_RADIUS
- eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
-
hostapd_acl_cache_free(hapd->acl_cache);
#endif /* CONFIG_NO_RADIUS */
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 2bc1065..da81c14 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -16,6 +16,7 @@
HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
};
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id);
int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
const u8 *msg, size_t len, u32 *session_timeout,
u32 *acct_interim_interval, int *vlan_id,
@@ -24,5 +25,6 @@
int hostapd_acl_init(struct hostapd_data *hapd);
void hostapd_acl_deinit(struct hostapd_data *hapd);
void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
#endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 11fde2a..5eb1060 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -108,6 +108,29 @@
}
+u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 sec_ch;
+
+ if (!hapd->cs_freq_params.channel ||
+ !hapd->cs_freq_params.sec_channel_offset)
+ return eid;
+
+ if (hapd->cs_freq_params.sec_channel_offset == -1)
+ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+ else if (hapd->cs_freq_params.sec_channel_offset == 1)
+ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+ else
+ return eid;
+
+ *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+ *eid++ = 1;
+ *eid++ = sec_ch;
+
+ return eid;
+}
+
+
/*
op_mode
Set to 0 (HT pure) under the followign conditions
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index d462ac8..9e3363e 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -172,6 +172,8 @@
case 0: /* Bits 0-7 */
if (hapd->iconf->obss_interval)
*pos |= 0x01; /* Bit 0 - Coexistence management */
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
+ *pos |= 0x04; /* Bit 2 - Extended Channel Switching */
break;
case 1: /* Bits 8-15 */
if (hapd->conf->proxy_arp)
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 5bf1b5d..3236016 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -17,6 +17,7 @@
#include "sta_info.h"
#include "beacon.h"
#include "ieee802_11.h"
+#include "dfs.h"
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -131,6 +132,171 @@
}
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 bw, chan1, chan2 = 0;
+ int freq1;
+
+ if (!hapd->cs_freq_params.channel ||
+ !hapd->cs_freq_params.vht_enabled)
+ return eid;
+
+ /* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
+ switch (hapd->cs_freq_params.bandwidth) {
+ case 40:
+ bw = 0;
+ break;
+ case 80:
+ /* check if it's 80+80 */
+ if (!hapd->cs_freq_params.center_freq2)
+ bw = 1;
+ else
+ bw = 3;
+ break;
+ case 160:
+ bw = 2;
+ break;
+ default:
+ /* not valid VHT bandwidth or not in CSA */
+ return eid;
+ }
+
+ freq1 = hapd->cs_freq_params.center_freq1 ?
+ hapd->cs_freq_params.center_freq1 :
+ hapd->cs_freq_params.freq;
+ if (ieee80211_freq_to_chan(freq1, &chan1) !=
+ HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ if (hapd->cs_freq_params.center_freq2 &&
+ ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+ &chan2) != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ *eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
+ *eid++ = 5; /* Length of Channel Switch Wrapper */
+ *eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
+ *eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+ *eid++ = bw; /* New Channel Width */
+ *eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+ *eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_config *iconf = iface->conf;
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ struct hostapd_channel_data *chan;
+ int dfs, i;
+ u8 channel, tx_pwr_count, local_pwr_constraint;
+ int max_tx_power;
+ u8 tx_pwr;
+
+ if (!mode)
+ return eid;
+
+ if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
+ return eid;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ if (mode->channels[i].freq == iface->freq)
+ break;
+ }
+ if (i == mode->num_channels)
+ return eid;
+
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ if (iconf->secondary_channel == 0) {
+ /* Max Transmit Power count = 0 (20 MHz) */
+ tx_pwr_count = 0;
+ } else {
+ /* Max Transmit Power count = 1 (20, 40 MHz) */
+ tx_pwr_count = 1;
+ }
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ /* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
+ tx_pwr_count = 2;
+ break;
+ case VHT_CHANWIDTH_80P80MHZ:
+ case VHT_CHANWIDTH_160MHZ:
+ /* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
+ tx_pwr_count = 3;
+ break;
+ default:
+ return eid;
+ }
+
+ /*
+ * Below local_pwr_constraint logic is referred from
+ * hostapd_eid_pwr_constraint.
+ *
+ * Check if DFS is required by regulatory.
+ */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0)
+ dfs = 0;
+
+ /*
+ * In order to meet regulations when TPC is not implemented using
+ * a transmit power that is below the legal maximum (including any
+ * mitigation factor) should help. In this case, indicate 3 dB below
+ * maximum allowed transmit power.
+ */
+ if (hapd->iconf->local_pwr_constraint == -1)
+ local_pwr_constraint = (dfs == 0) ? 0 : 3;
+ else
+ local_pwr_constraint = hapd->iconf->local_pwr_constraint;
+
+ /*
+ * A STA that is not an AP shall use a transmit power less than or
+ * equal to the local maximum transmit power level for the channel.
+ * The local maximum transmit power can be calculated from the formula:
+ * local max TX pwr = max TX pwr - local pwr constraint
+ * Where max TX pwr is maximum transmit power level specified for
+ * channel in Country element and local pwr constraint is specified
+ * for channel in this Power Constraint element.
+ */
+ chan = &mode->channels[i];
+ max_tx_power = chan->max_tx_power - local_pwr_constraint;
+
+ /*
+ * Local Maximum Transmit power is encoded as two's complement
+ * with a 0.5 dB step.
+ */
+ max_tx_power *= 2; /* in 0.5 dB steps */
+ if (max_tx_power > 127) {
+ /* 63.5 has special meaning of 63.5 dBm or higher */
+ max_tx_power = 127;
+ }
+ if (max_tx_power < -128)
+ max_tx_power = -128;
+ if (max_tx_power < 0)
+ tx_pwr = 0x80 + max_tx_power + 128;
+ else
+ tx_pwr = max_tx_power;
+
+ *eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
+ *eid++ = 2 + tx_pwr_count;
+
+ /*
+ * Max Transmit Power count and
+ * Max Transmit Power units = 0 (EIRP)
+ */
+ *eid++ = tx_pwr_count;
+
+ for (i = 0; i <= tx_pwr_count; i++)
+ *eid++ = tx_pwr;
+
+ return eid;
+}
+
+
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_capab)
{
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d45b98f..f566603 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -125,6 +125,9 @@
}
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+
static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
struct sta_info *sta,
int idx, int broadcast,
@@ -204,7 +207,7 @@
}
-void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
{
struct eapol_authenticator *eapol = hapd->eapol_auth;
struct eapol_state_machine *sm = sta->eapol_sm;
@@ -259,6 +262,9 @@
}
}
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+
const char *radius_mode_txt(struct hostapd_data *hapd)
{
@@ -1100,6 +1106,7 @@
sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
sta->eapol_sm->authSuccess = TRUE;
sta->eapol_sm->authFail = FALSE;
+ sta->eapol_sm->portValid = TRUE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
/* TODO: get vlan_id from R0KH using RRB message */
@@ -1709,15 +1716,6 @@
ieee802_1x_check_hs20(hapd, sta, msg,
session_timeout_set ?
(int) session_timeout : -1);
- if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
- !sta->hs20_deauth_requested &&
- wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
- session_timeout_set ?
- (int) session_timeout : -1, sm) == 0) {
- hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
- HOSTAPD_LEVEL_DEBUG,
- "Added PMKSA cache entry");
- }
break;
case RADIUS_CODE_ACCESS_REJECT:
sm->eap_if->aaaFail = TRUE;
@@ -2023,9 +2021,13 @@
static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
{
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
ieee802_1x_tx_key(hapd, sta);
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
}
@@ -2096,6 +2098,7 @@
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.tls_session_lifetime = hapd->conf->tls_session_lifetime;
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;
@@ -2179,7 +2182,7 @@
{
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
- if (hapd->driver != NULL &&
+ if (hapd->driver && hapd->drv_priv &&
(hapd->conf->ieee802_1x || hapd->conf->wpa))
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
@@ -2573,7 +2576,7 @@
session_timeout = dot11RSNAConfigPMKLifetime;
if (success && key && len >= PMK_LEN && !sta->remediation &&
!sta->hs20_deauth_requested &&
- wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
+ wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
sta->eapol_sm) == 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG,
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index de6e0e7..14d6955 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -23,7 +23,6 @@
void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
void ieee802_1x_free_station(struct sta_info *sta);
-void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 0adcc97..4a87721 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -98,7 +98,7 @@
{
struct hostapd_data *hapd = ctx;
struct icmpv6_ndmsg *msg;
- struct in6_addr *saddr;
+ struct in6_addr saddr;
struct sta_info *sta;
int res;
char addrtxt[INET6_ADDRSTRLEN + 1];
@@ -113,25 +113,30 @@
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)) {
+ /*
+ * IPv6 header may not be 32-bit aligned in the buffer, so use
+ * a local copy to avoid unaligned reads.
+ */
+ os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
+ 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))
+ if (sta_has_ip6addr(sta, &saddr))
return;
- if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
- == NULL)
+ 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,
+ 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,
@@ -140,7 +145,7 @@
return;
}
- if (sta_ip6addr_add(sta, saddr))
+ if (sta_ip6addr_add(sta, &saddr))
return;
}
break;
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 877affe..83e4bda 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -258,7 +258,7 @@
struct rsn_pmksa_cache_entry *entry, *pos;
struct os_reltime now;
- if (pmk_len > PMK_LEN)
+ if (pmk_len > PMK_LEN_MAX)
return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck)
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 8b7be12..b2da379 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -17,7 +17,7 @@
struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next, *hnext;
u8 pmkid[PMKID_LEN];
- u8 pmk[PMK_LEN];
+ u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 20847d5..500beff 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -16,6 +16,7 @@
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
+#include "fst/fst.h"
#include "hostapd.h"
#include "accounting.h"
#include "ieee802_1x.h"
@@ -171,19 +172,6 @@
!(sta->flags & WLAN_STA_PREAUTH))
hostapd_drv_sta_remove(hapd, sta->addr);
-#ifndef CONFIG_NO_VLAN
- if (sta->vlan_id_bound) {
- /*
- * Need to remove the STA entry before potentially removing the
- * VLAN.
- */
- if (hapd->iface->driver_ap_teardown &&
- !(sta->flags & WLAN_STA_PREAUTH))
- hostapd_drv_sta_remove(hapd, sta->addr);
- vlan_remove_dynamic(hapd, sta->vlan_id_bound);
- }
-#endif /* CONFIG_NO_VLAN */
-
ap_sta_hash_del(hapd, sta);
ap_sta_list_del(hapd, sta);
@@ -273,6 +261,24 @@
radius_client_flush_auth(hapd->radius, sta->addr);
#endif /* CONFIG_NO_RADIUS */
+#ifndef CONFIG_NO_VLAN
+ /*
+ * sta->wpa_sm->group needs to be released before so that
+ * vlan_remove_dynamic() can check that no stations are left on the
+ * AP_VLAN netdev.
+ */
+ if (sta->vlan_id_bound) {
+ /*
+ * Need to remove the STA entry before potentially removing the
+ * VLAN.
+ */
+ if (hapd->iface->driver_ap_teardown &&
+ !(sta->flags & WLAN_STA_PREAUTH))
+ hostapd_drv_sta_remove(hapd, sta->addr);
+ vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+ }
+#endif /* CONFIG_NO_VLAN */
+
os_free(sta->challenge);
#ifdef CONFIG_IEEE80211W
@@ -296,6 +302,9 @@
wpabuf_free(sta->wps_ie);
wpabuf_free(sta->p2p_ie);
wpabuf_free(sta->hs20_ie);
+#ifdef CONFIG_FST
+ wpabuf_free(sta->mb_ies);
+#endif /* CONFIG_FST */
os_free(sta->ht_capabilities);
os_free(sta->vht_capabilities);
@@ -838,41 +847,17 @@
}
iface = vlan->ifname;
- if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "could not "
- "configure encryption for dynamic VLAN "
- "interface for vlan_id=%d",
- sta->vlan_id);
- }
-
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
"interface '%s'", iface);
- } else if (vlan && vlan->vlan_id == sta->vlan_id) {
- if (vlan->dynamic_vlan > 0) {
- vlan->dynamic_vlan++;
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "updated existing "
- "dynamic VLAN interface '%s'", iface);
- }
-
- /*
- * Update encryption configuration for statically generated
- * VLAN interface. This is only used for static WEP
- * configuration for the case where hostapd did not yet know
- * which keys are to be used when the interface was added.
- */
- if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "could not "
- "configure encryption for VLAN "
- "interface for vlan_id=%d",
- sta->vlan_id);
- }
+ } else if (vlan && vlan->vlan_id == sta->vlan_id &&
+ vlan->dynamic_vlan > 0) {
+ vlan->dynamic_vlan++;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "updated existing dynamic VLAN interface '%s'",
+ iface);
}
/* ref counters have been increased, so mark the station */
@@ -1060,6 +1045,16 @@
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
AP_STA_DISCONNECTED "%s", buf);
}
+
+#ifdef CONFIG_FST
+ if (hapd->iface->fst) {
+ if (authorized)
+ fst_notify_peer_connected(hapd->iface->fst, sta->addr);
+ else
+ fst_notify_peer_disconnected(hapd->iface->fst,
+ sta->addr);
+ }
+#endif /* CONFIG_FST */
}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 52a9997..09deac6 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -86,6 +86,7 @@
unsigned int hs20_deauth_requested:1;
unsigned int session_timeout_set:1;
unsigned int radius_das_match:1;
+ unsigned int ecsa_supported:1;
u16 auth_alg;
@@ -153,6 +154,9 @@
struct wpabuf *hs20_deauth_req;
char *hs20_session_info_url;
int hs20_disassoc_timer;
+#ifdef CONFIG_FST
+ struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
struct os_reltime connected_time;
@@ -177,7 +181,7 @@
* AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
* after AP_DEAUTH_DELAY seconds has passed after disassociation. */
#define AP_MAX_INACTIVITY (5 * 60)
-#define AP_DISASSOC_DELAY (1)
+#define AP_DISASSOC_DELAY (3)
#define AP_DEAUTH_DELAY (1)
/* Number of seconds to keep STA entry with Authenticated flag after it has
* been disassociated. */
diff --git a/src/ap/utils.c b/src/ap/utils.c
index d60555a..fcb371b 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "fst/fst.h"
#include "sta_info.h"
#include "hostapd.h"
@@ -55,6 +56,14 @@
ohapd = iface->bss[j];
if (ohapd == data->hapd)
continue;
+#ifdef CONFIG_FST
+ /* Don't prune STAs belong to same FST */
+ if (ohapd->iface->fst &&
+ data->hapd->iface->fst &&
+ fst_are_ifaces_aggregated(ohapd->iface->fst,
+ data->hapd->iface->fst))
+ continue;
+#endif /* CONFIG_FST */
osta = ap_get_sta(ohapd, data->addr);
if (!osta)
continue;
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index fd1c8dd..e3df164 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -9,9 +9,9 @@
*/
#include "utils/includes.h"
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
#include <net/if.h>
#include <sys/ioctl.h>
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
#include <linux/sockios.h>
#include <linux/if_vlan.h>
#include <linux/if_bridge.h>
@@ -21,6 +21,7 @@
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wpa_auth.h"
#include "vlan_init.h"
#include "vlan_util.h"
@@ -119,6 +120,8 @@
return clean;
}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
static int ifconfig_helper(const char *if_name, int up)
{
@@ -167,6 +170,67 @@
}
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ int existsok)
+{
+ int ret, i;
+
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (!hapd->conf->ssid.wep.key[i])
+ continue;
+ wpa_printf(MSG_ERROR,
+ "VLAN: Refusing to set up VLAN iface %s with WEP",
+ vlan->ifname);
+ return -1;
+ }
+
+ if (!if_nametoindex(vlan->ifname))
+ ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+ else if (!existsok)
+ return -1;
+ else
+ ret = 0;
+
+ if (ret)
+ return ret;
+
+ ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+ if (hapd->wpa_auth)
+ ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+ if (ret == 0)
+ return ret;
+
+ wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+ if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
+ wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+ /* group state machine setup failed */
+ if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+ wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+ return ret;
+}
+
+
+static int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ int ret;
+
+ ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
+ if (ret)
+ wpa_printf(MSG_ERROR,
+ "WPA deinitialization for VLAN %d failed (%d)",
+ vlan->vlan_id, ret);
+
+ return hostapd_vlan_if_remove(hapd, vlan->ifname);
+}
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
static int ifconfig_down(const char *if_name)
{
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
@@ -882,48 +946,19 @@
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
-{
- int i;
-
- if (dyn_vlan == NULL)
- return 0;
-
- /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
- * functions for setting up dynamic broadcast keys. */
- for (i = 0; i < 4; i++) {
- if (hapd->conf->ssid.wep.key[i] &&
- hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
- i == hapd->conf->ssid.wep.idx, NULL, 0,
- hapd->conf->ssid.wep.key[i],
- hapd->conf->ssid.wep.len[i]))
- {
- wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
- "encryption for dynamic VLAN");
- return -1;
- }
- }
-
- return 0;
-}
-
-
static int vlan_dynamic_add(struct hostapd_data *hapd,
struct hostapd_vlan *vlan)
{
while (vlan) {
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
- if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
- if (errno != EEXIST) {
- wpa_printf(MSG_ERROR, "VLAN: Could "
- "not add VLAN %s: %s",
- vlan->ifname,
- strerror(errno));
- return -1;
- }
+ if (vlan_if_add(hapd, vlan, 1)) {
+ wpa_printf(MSG_ERROR,
+ "VLAN: Could not add VLAN %s: %s",
+ vlan->ifname, strerror(errno));
+ return -1;
}
#ifdef CONFIG_FULL_DYNAMIC_VLAN
- ifconfig_up(vlan->ifname);
+ vlan_newlink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
}
@@ -943,7 +978,7 @@
next = vlan->next;
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
- hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+ vlan_if_remove(hapd, vlan)) {
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
"iface: %s: %s",
vlan->ifname, strerror(errno));
@@ -1031,19 +1066,17 @@
os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
pos);
- if (hostapd_vlan_if_add(hapd, n->ifname)) {
+ n->next = hapd->conf->vlan;
+ hapd->conf->vlan = n;
+
+ /* hapd->conf->vlan needs this new VLAN here for WPA setup */
+ if (vlan_if_add(hapd, n, 0)) {
+ hapd->conf->vlan = n->next;
os_free(n);
n = NULL;
goto free_ifname;
}
- n->next = hapd->conf->vlan;
- hapd->conf->vlan = n;
-
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
- ifconfig_up(n->ifname);
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
free_ifname:
os_free(ifname);
return n;
@@ -1073,7 +1106,7 @@
return 1;
if (vlan->dynamic_vlan == 0) {
- hostapd_vlan_if_remove(hapd, vlan->ifname);
+ vlan_if_remove(hapd, vlan);
#ifdef CONFIG_FULL_DYNAMIC_VLAN
vlan_dellink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index fc39443..aeb2dc6 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -17,8 +17,6 @@
struct hostapd_vlan *vlan,
int vlan_id);
int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
-int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
- const char *dyn_vlan);
#else /* CONFIG_NO_VLAN */
static inline int vlan_init(struct hostapd_data *hapd)
{
@@ -40,12 +38,6 @@
{
return -1;
}
-
-static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
- const char *dyn_vlan)
-{
- return -1;
-}
#endif /* CONFIG_NO_VLAN */
#endif /* VLAN_INIT_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index f23a57a..c2c5693 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -44,7 +44,8 @@
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);
+ const u8 *pmk, unsigned int pmk_len,
+ struct wpa_ptk *ptk);
static void wpa_group_free(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@@ -827,6 +828,7 @@
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
+ unsigned int pmk_len;
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -834,10 +836,13 @@
sm->p2p_dev_addr, pmk);
if (pmk == NULL)
break;
- } else
+ pmk_len = PMK_LEN;
+ } else {
pmk = sm->PMK;
+ pmk_len = sm->pmk_len;
+ }
- wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+ wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
== 0) {
@@ -1540,6 +1545,7 @@
else
WPA_PUT_BE16(key->key_data_length,
key_data_len);
+#ifndef CONFIG_NO_RC4
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
os_memcpy(key->key_iv,
@@ -1555,6 +1561,7 @@
else
WPA_PUT_BE16(key->key_data_length,
key_data_len);
+#endif /* CONFIG_NO_RC4 */
} else {
os_free(hdr);
os_free(buf);
@@ -1669,7 +1676,7 @@
}
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
{
int remove_ptk = 1;
@@ -1757,6 +1764,14 @@
wpa_remove_ptk(sm);
}
+ if (sm->in_step_loop) {
+ /*
+ * wpa_sm_step() is already running - avoid recursive call to
+ * it by making the existing loop process the new update.
+ */
+ sm->changed = TRUE;
+ return 0;
+ }
return wpa_sm_step(sm);
}
@@ -1841,9 +1856,13 @@
group->reject_4way_hs_for_entropy = FALSE;
}
- wpa_group_init_gmk_and_counter(wpa_auth, group);
- wpa_gtk_update(wpa_auth, group);
- wpa_group_config_group_keys(wpa_auth, group);
+ if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+ wpa_gtk_update(wpa_auth, group) < 0 ||
+ wpa_group_config_group_keys(wpa_auth, group) < 0) {
+ wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+ group->first_sta_seen = FALSE;
+ group->reject_4way_hs_for_entropy = TRUE;
+ }
}
@@ -1890,11 +1909,27 @@
#endif /* CONFIG_IEEE80211R */
if (sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
- os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+ os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->pmk_len = sm->pmksa->pmk_len;
} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+ unsigned int pmk_len;
+
+ if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ pmk_len = PMK_LEN_SUITE_B_192;
+ else
+ pmk_len = PMK_LEN;
wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
- "(len=%lu)", (unsigned long) len);
- os_memcpy(sm->PMK, msk, PMK_LEN);
+ "(MSK len=%lu PMK len=%u)", (unsigned long) len,
+ pmk_len);
+ if (len < pmk_len) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: MSK not long enough (%u) to create PMK (%u)",
+ (unsigned int) len, (unsigned int) pmk_len);
+ sm->Disconnect = TRUE;
+ return;
+ }
+ os_memcpy(sm->PMK, msk, pmk_len);
+ sm->pmk_len = pmk_len;
#ifdef CONFIG_IEEE80211R
if (len >= 2 * PMK_LEN) {
os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
@@ -1929,6 +1964,7 @@
psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
#ifdef CONFIG_IEEE80211R
os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN;
@@ -1980,7 +2016,7 @@
* Calculate PMKID since no PMKSA cache entry was
* available with pre-calculated PMKID.
*/
- rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
}
@@ -1992,14 +2028,15 @@
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
- const u8 *pmk, struct wpa_ptk *ptk)
+ const u8 *pmk, unsigned int pmk_len,
+ struct wpa_ptk *ptk)
{
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
#endif /* CONFIG_IEEE80211R */
- return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
ptk, sm->wpa_key_mgmt, sm->pairwise);
}
@@ -2010,6 +2047,7 @@
struct wpa_ptk PTK;
int ok = 0, psk_found = 0;
const u8 *pmk = NULL;
+ unsigned int pmk_len;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
@@ -2025,10 +2063,13 @@
if (pmk == NULL)
break;
psk_found = 1;
- } else
+ pmk_len = PMK_LEN;
+ } else {
pmk = sm->PMK;
+ pmk_len = sm->pmk_len;
+ }
- wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
+ wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
sm->last_rx_eapol_key,
@@ -2078,6 +2119,7 @@
* state machine data based on whatever PSK was selected here.
*/
os_memcpy(sm->PMK, pmk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
}
sm->MICVerified = TRUE;
@@ -3229,13 +3271,21 @@
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+ unsigned int pmk_len,
int session_timeout, struct eapol_state_machine *eapol)
{
if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
- if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+ if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ if (pmk_len > PMK_LEN_SUITE_B_192)
+ pmk_len = PMK_LEN_SUITE_B_192;
+ } else if (pmk_len > PMK_LEN) {
+ pmk_len = PMK_LEN;
+ }
+
+ if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len,
sm->PTK.kck, sm->PTK.kck_len,
sm->wpa_auth->addr, sm->addr, session_timeout,
eapol, sm->wpa_key_mgmt))
@@ -3374,6 +3424,98 @@
}
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+
+ if (wpa_auth == NULL)
+ return 0;
+
+ group = wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (group == NULL) {
+ group = wpa_auth_add_group(wpa_auth, vlan_id);
+ if (group == NULL)
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Ensure group state machine running for VLAN ID %d",
+ vlan_id);
+
+ wpa_group_get(wpa_auth, group);
+ group->num_setup_iface++;
+
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ * -2 if wpa_group is still referenced
+ * 0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+ struct wpa_group *group;
+ int ret = 0;
+
+ if (wpa_auth == NULL)
+ return 0;
+
+ group = wpa_auth->group;
+ while (group) {
+ if (group->vlan_id == vlan_id)
+ break;
+ group = group->next;
+ }
+
+ if (group == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Try stopping group state machine for VLAN ID %d",
+ vlan_id);
+
+ if (group->num_setup_iface <= 0) {
+ wpa_printf(MSG_ERROR,
+ "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+ vlan_id);
+ return -1;
+ }
+ group->num_setup_iface--;
+
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ ret = -1;
+
+ if (group->references > 1) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+ vlan_id);
+ ret = -2;
+ }
+
+ wpa_group_put(wpa_auth, group);
+
+ return ret;
+}
+
+
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
{
struct wpa_group *group;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e747806..75b73f0 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -14,6 +14,8 @@
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
+#define MAX_OWN_IE_OVERRIDE 256
+
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
@@ -164,6 +166,8 @@
int ap_mlme;
#ifdef CONFIG_TESTING_OPTIONS
double corrupt_gtk_rekey_mic_probability;
+ u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+ size_t own_ie_override_len;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
u8 ip_addr_go[4];
@@ -252,12 +256,12 @@
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len);
-typedef enum {
+enum wpa_event {
WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
WPA_REAUTH_EAPOL, WPA_ASSOC_FT
-} wpa_event;
+};
void wpa_remove_ptk(struct wpa_state_machine *sm);
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
void wpa_auth_sm_notify(struct wpa_state_machine *sm);
void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
@@ -275,6 +279,7 @@
const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
size_t *len);
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+ unsigned int pmk_len,
int session_timeout, struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
const u8 *pmk, size_t len, const u8 *sta_addr,
@@ -321,4 +326,7 @@
struct radius_das_attrs *attr);
void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7cd0b6c..ffd0790 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -92,6 +92,13 @@
#ifdef CONFIG_TESTING_OPTIONS
wconf->corrupt_gtk_rekey_mic_probability =
iconf->corrupt_gtk_rekey_mic_probability;
+ if (conf->own_ie_override &&
+ wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
+ wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
+ os_memcpy(wconf->own_ie_override,
+ wpabuf_head(conf->own_ie_override),
+ wconf->own_ie_override_len);
+ }
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_P2P
os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -630,7 +637,8 @@
}
#ifdef CONFIG_IEEE80211R
- if (!hostapd_drv_none(hapd)) {
+ if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
+ wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
hapd->conf->bridge :
hapd->conf->iface, NULL, ETH_P_RRB,
@@ -666,13 +674,14 @@
wpa_deinit(hapd->wpa_auth);
hapd->wpa_auth = NULL;
- if (hostapd_set_privacy(hapd, 0)) {
+ if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) {
wpa_printf(MSG_DEBUG, "Could not disable "
"PrivacyInvoked for interface %s",
hapd->conf->iface);
}
- if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+ if (hapd->drv_priv &&
+ hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
wpa_printf(MSG_DEBUG, "Could not remove generic "
"information element from interface %s",
hapd->conf->iface);
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 57b098f..72b7eb3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -60,7 +60,8 @@
u8 SNonce[WPA_NONCE_LEN];
u8 alt_SNonce[WPA_NONCE_LEN];
u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
- u8 PMK[PMK_LEN];
+ u8 PMK[PMK_LEN_MAX];
+ unsigned int pmk_len;
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@@ -171,6 +172,7 @@
#endif /* CONFIG_IEEE80211W */
/* Number of references except those in struct wpa_group->next */
unsigned int references;
+ unsigned int num_setup_iface;
};
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f287297..52ccac3 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -251,7 +251,7 @@
pos += 2;
if (pmkid) {
- if (pos + 2 + PMKID_LEN > buf + len)
+ if (2 + PMKID_LEN > buf + len - pos)
return -1;
/* PMKID Count */
WPA_PUT_LE16(pos, 1);
@@ -261,8 +261,9 @@
}
#ifdef CONFIG_IEEE80211W
- if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- if (pos + 2 + 4 > buf + len)
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+ if (2 + 4 > buf + len - pos)
return -1;
if (pmkid == NULL) {
/* PMKID Count */
@@ -377,6 +378,23 @@
u8 *pos, buf[128];
int res;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_auth->conf.own_ie_override_len) {
+ wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+ wpa_auth->conf.own_ie_override,
+ wpa_auth->conf.own_ie_override_len);
+ os_free(wpa_auth->wpa_ie);
+ wpa_auth->wpa_ie =
+ os_malloc(wpa_auth->conf.own_ie_override_len);
+ if (wpa_auth->wpa_ie == NULL)
+ return -1;
+ os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
+ wpa_auth->conf.own_ie_override_len);
+ wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
pos = buf;
if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
@@ -773,7 +791,7 @@
return 0;
}
- if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ if (1 + RSN_SELECTOR_LEN < end - pos &&
pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -869,13 +887,13 @@
int ret = 0;
os_memset(ie, 0, sizeof(*ie));
- for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
if (pos[0] == 0xdd &&
((pos == buf + len - 1) || pos[1] == 0)) {
/* Ignore padding */
break;
}
- if (pos + 2 + pos[1] > end) {
+ if (2 + pos[1] > end - pos) {
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
"underflow (ie=%d len=%d pos=%d)",
pos[0], pos[1], (int) (pos - buf));
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index caed01e..66a43eb 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -452,6 +452,11 @@
os_free(hapd->wps->network_key);
hapd->wps->network_key = NULL;
hapd->wps->network_key_len = 0;
+ } else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
+ (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
+ wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
+ (unsigned long) cred->key_len);
+ return -1;
} else {
if (hapd->wps->network_key == NULL ||
hapd->wps->network_key_len < cred->key_len) {
@@ -867,7 +872,8 @@
hapd->wps_probe_resp_ie = NULL;
if (deinit_only) {
- hostapd_reset_ap_wps_ie(hapd);
+ if (hapd->drv_priv)
+ hostapd_reset_ap_wps_ie(hapd);
return;
}
@@ -1299,30 +1305,53 @@
}
+struct wps_button_pushed_ctx {
+ const u8 *p2p_dev_addr;
+ unsigned int count;
+};
+
static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
{
- const u8 *p2p_dev_addr = ctx;
- if (hapd->wps == NULL)
- return -1;
- return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+ struct wps_button_pushed_ctx *data = ctx;
+
+ if (hapd->wps) {
+ data->count++;
+ return wps_registrar_button_pushed(hapd->wps->registrar,
+ data->p2p_dev_addr);
+ }
+
+ return 0;
}
int hostapd_wps_button_pushed(struct hostapd_data *hapd,
const u8 *p2p_dev_addr)
{
- return hostapd_wps_for_each(hapd, wps_button_pushed,
- (void *) p2p_dev_addr);
+ struct wps_button_pushed_ctx ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.p2p_dev_addr = p2p_dev_addr;
+ ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
+ if (ret == 0 && !ctx.count)
+ ret = -1;
+ return ret;
}
+struct wps_cancel_ctx {
+ unsigned int count;
+};
+
static int wps_cancel(struct hostapd_data *hapd, void *ctx)
{
- if (hapd->wps == NULL)
- return -1;
+ struct wps_cancel_ctx *data = ctx;
- wps_registrar_wps_cancel(hapd->wps->registrar);
- ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+ if (hapd->wps) {
+ data->count++;
+ wps_registrar_wps_cancel(hapd->wps->registrar);
+ ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+ }
return 0;
}
@@ -1330,7 +1359,14 @@
int hostapd_wps_cancel(struct hostapd_data *hapd)
{
- return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+ struct wps_cancel_ctx ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
+ if (ret == 0 && !ctx.count)
+ ret = -1;
+ return ret;
}
@@ -1560,6 +1596,10 @@
static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
{
struct wps_ap_pin_data *data = ctx;
+
+ if (!hapd->wps)
+ return 0;
+
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = os_strdup(data->pin_txt);
#ifdef CONFIG_WPS_UPNP
diff --git a/src/common/defs.h b/src/common/defs.h
index eb080ea..6aea375 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -174,7 +174,7 @@
/**
* WPA_INTERFACE_DISABLED - Interface disabled
*
- * This stat eis entered if the network interface is disabled, e.g.,
+ * This state is entered if the network interface is disabled, e.g.,
* due to rfkill. wpa_supplicant refuses any new operations that would
* use the radio until the interface has been enabled.
*/
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index e589a1a..9c37ea6 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -272,10 +272,8 @@
int affected_start, affected_end;
size_t i;
- if (!mode || !scan_res || !pri_chan || !sec_chan)
- return 0;
-
- if (pri_chan == sec_chan)
+ if (!mode || !scan_res || !pri_chan || !sec_chan ||
+ pri_chan == sec_chan)
return 0;
pri_freq = hw_get_freq(mode, pri_chan);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5385faf..8dee883 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "defs.h"
#include "wpa_common.h"
+#include "qca-vendor.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
@@ -147,6 +148,20 @@
}
break;
+ case OUI_QCA:
+ switch (pos[3]) {
+ case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
+ elems->pref_freq_list = pos;
+ elems->pref_freq_list_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE,
+ "Unknown QCA information element ignored (type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
default:
wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
"information element ignored (vendor OUI "
@@ -339,6 +354,18 @@
/* after mic everything is encrypted, so stop. */
left = elen;
break;
+ case WLAN_EID_MULTI_BAND:
+ if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
+ wpa_printf(MSG_MSGDUMP,
+ "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
+ id, elen);
+ break;
+ }
+
+ elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
+ elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
+ elems->mb_ies.nof_ies++;
+ break;
default:
unknown++;
if (!show_errors)
@@ -371,8 +398,8 @@
pos = ies;
end = ies + ies_len;
- while (pos + 2 <= end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos >= 2) {
+ if (2 + pos[1] > end - pos)
break;
count++;
pos += 2 + pos[1];
@@ -392,8 +419,8 @@
end = ies + ies_len;
ie = NULL;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
return NULL;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
WPA_GET_BE32(&pos[2]) == oui_type) {
@@ -414,8 +441,8 @@
* There may be multiple vendor IEs in the message, so need to
* concatenate their data fields.
*/
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
WPA_GET_BE32(&pos[2]) == oui_type)
@@ -541,26 +568,166 @@
enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
{
- enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
+ u8 op_class;
+
+ return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+ &op_class, channel);
+}
+
+
+/**
+ * ieee80211_freq_to_channel_ext - Convert frequency into channel info
+ * for HT40 and VHT. DFS channels are not covered.
+ * @freq: Frequency (MHz) to convert
+ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
+ * @vht: VHT channel width (VHT_CHANWIDTH_*)
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+ */
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel)
+{
+ u8 vht_opclass;
+
+ /* TODO: more operating classes */
+
+ if (sec_channel > 1 || sec_channel < -1)
+ return NUM_HOSTAPD_MODES;
if (freq >= 2412 && freq <= 2472) {
- mode = HOSTAPD_MODE_IEEE80211G;
+ if ((freq - 2407) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht)
+ return NUM_HOSTAPD_MODES;
+
+ /* 2.407 GHz, channels 1..13 */
+ if (sec_channel == 1)
+ *op_class = 83;
+ else if (sec_channel == -1)
+ *op_class = 84;
+ else
+ *op_class = 81;
+
*channel = (freq - 2407) / 5;
- } else if (freq == 2484) {
- mode = HOSTAPD_MODE_IEEE80211B;
- *channel = 14;
- } else if (freq >= 4900 && freq < 5000) {
- mode = HOSTAPD_MODE_IEEE80211A;
- *channel = (freq - 4000) / 5;
- } else if (freq >= 5000 && freq < 5900) {
- mode = HOSTAPD_MODE_IEEE80211A;
- *channel = (freq - 5000) / 5;
- } else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
- mode = HOSTAPD_MODE_IEEE80211AD;
- *channel = (freq - 56160) / 2160;
+
+ return HOSTAPD_MODE_IEEE80211G;
}
- return mode;
+ if (freq == 2484) {
+ if (sec_channel || vht)
+ return NUM_HOSTAPD_MODES;
+
+ *op_class = 82; /* channel 14 */
+ *channel = 14;
+
+ return HOSTAPD_MODE_IEEE80211B;
+ }
+
+ if (freq >= 4900 && freq < 5000) {
+ if ((freq - 4000) % 5)
+ return NUM_HOSTAPD_MODES;
+ *channel = (freq - 4000) / 5;
+ *op_class = 0; /* TODO */
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ switch (vht) {
+ case VHT_CHANWIDTH_80MHZ:
+ vht_opclass = 128;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ vht_opclass = 129;
+ break;
+ case VHT_CHANWIDTH_80P80MHZ:
+ vht_opclass = 130;
+ break;
+ default:
+ vht_opclass = 0;
+ break;
+ }
+
+ /* 5 GHz, channels 36..48 */
+ if (freq >= 5180 && freq <= 5240) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht_opclass)
+ *op_class = vht_opclass;
+ else if (sec_channel == 1)
+ *op_class = 116;
+ else if (sec_channel == -1)
+ *op_class = 117;
+ else
+ *op_class = 115;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 5 GHz, channels 149..169 */
+ if (freq >= 5745 && freq <= 5845) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht_opclass)
+ *op_class = vht_opclass;
+ else if (sec_channel == 1)
+ *op_class = 126;
+ else if (sec_channel == -1)
+ *op_class = 127;
+ else if (freq <= 5805)
+ *op_class = 124;
+ else
+ *op_class = 125;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 5 GHz, channels 100..140 */
+ if (freq >= 5000 && freq <= 5700) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+
+ if (vht_opclass)
+ *op_class = vht_opclass;
+ else if (sec_channel == 1)
+ *op_class = 122;
+ else if (sec_channel == -1)
+ *op_class = 123;
+ else
+ *op_class = 121;
+
+ *channel = (freq - 5000) / 5;
+
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ if (freq >= 5000 && freq < 5900) {
+ if ((freq - 5000) % 5)
+ return NUM_HOSTAPD_MODES;
+ *channel = (freq - 5000) / 5;
+ *op_class = 0; /* TODO */
+ return HOSTAPD_MODE_IEEE80211A;
+ }
+
+ /* 56.16 GHz, channel 1..4 */
+ if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+ if (sec_channel || vht)
+ return NUM_HOSTAPD_MODES;
+
+ *channel = (freq - 56160) / 2160;
+ *op_class = 180;
+
+ return HOSTAPD_MODE_IEEE80211AD;
+ }
+
+ return NUM_HOSTAPD_MODES;
}
@@ -946,3 +1113,62 @@
return "WLAN_FC_TYPE_UNKNOWN";
#undef C2S
}
+
+
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+ size_t ies_len)
+{
+ os_memset(info, 0, sizeof(*info));
+
+ while (ies_buf && ies_len >= 2 &&
+ info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+ size_t len = 2 + ies_buf[1];
+
+ if (len > ies_len) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IEs",
+ ies_buf, ies_len);
+ return -1;
+ }
+
+ if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
+ wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
+ info->ies[info->nof_ies].ie = ies_buf + 2;
+ info->ies[info->nof_ies].ie_len = ies_buf[1];
+ info->nof_ies++;
+ }
+
+ ies_len -= len;
+ ies_buf += len;
+ }
+
+ return 0;
+}
+
+
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
+{
+ struct wpabuf *mb_ies = NULL;
+
+ WPA_ASSERT(info != NULL);
+
+ if (info->nof_ies) {
+ u8 i;
+ size_t mb_ies_size = 0;
+
+ for (i = 0; i < info->nof_ies; i++)
+ mb_ies_size += 2 + info->ies[i].ie_len;
+
+ mb_ies = wpabuf_alloc(mb_ies_size);
+ if (mb_ies) {
+ for (i = 0; i < info->nof_ies; i++) {
+ wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
+ wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
+ wpabuf_put_data(mb_ies,
+ info->ies[i].ie,
+ info->ies[i].ie_len);
+ }
+ }
+ }
+
+ return mb_ies;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index c84d8a7..55ce022 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -9,6 +9,16 @@
#ifndef IEEE802_11_COMMON_H
#define IEEE802_11_COMMON_H
+#define MAX_NOF_MB_IES_SUPPORTED 5
+
+struct mb_ies_info {
+ struct {
+ const u8 *ie;
+ u8 ie_len;
+ } ies[MAX_NOF_MB_IES_SUPPORTED];
+ u8 nof_ies;
+};
+
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
@@ -48,6 +58,7 @@
const u8 *osen;
const u8 *ampe;
const u8 *mic;
+ const u8 *pref_freq_list;
u8 ssid_len;
u8 supp_rates_len;
@@ -76,6 +87,8 @@
u8 osen_len;
u8 ampe_len;
u8 mic_len;
+ u8 pref_freq_list_len;
+ struct mb_ies_info mb_ies;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -101,9 +114,15 @@
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);
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+ int sec_channel, int vht,
+ u8 *op_class, u8 *channel);
int ieee80211_is_dfs(int freq);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+ size_t ies_len);
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
const char * fc2str(u16 fc);
#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 47b15de..5b8d130 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -165,7 +165,10 @@
#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
#define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
@@ -244,6 +247,7 @@
#define WLAN_EID_TIMEOUT_INTERVAL 56
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
+#define WLAN_EID_EXT_CHANSWITCH_ANN 60
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
#define WLAN_EID_WAPI 68
@@ -271,6 +275,8 @@
#define WLAN_EID_AMPE 139
#define WLAN_EID_MIC 140
#define WLAN_EID_CCKM 156
+#define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_SESSION_TRANSITION 164
#define WLAN_EID_VHT_CAP 191
#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
@@ -299,6 +305,7 @@
#define WLAN_ACTION_TDLS 12
#define WLAN_ACTION_SELF_PROTECTED 15
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_FST 18
#define WLAN_ACTION_VENDOR_SPECIFIC 127
/* Public action codes */
@@ -401,7 +408,12 @@
ANQP_AP_LOCATION_PUBLIC_URI = 267,
ANQP_DOMAIN_NAME = 268,
ANQP_EMERGENCY_ALERT_URI = 269,
+ ANQP_TDLS_CAPABILITY = 270,
ANQP_EMERGENCY_NAI = 271,
+ ANQP_NEIGHBOR_REPORT = 272,
+ ANQP_VENUE_URL = 277,
+ ANQP_ADVICE_OF_CHARGE = 278,
+ ANQP_LOCAL_CONTENT = 279,
ANQP_VENDOR_SPECIFIC = 56797
};
@@ -615,6 +627,10 @@
u8 action; /* 15 */
u8 variable[];
} STRUCT_PACKED slf_prot_action;
+ struct {
+ u8 action;
+ u8 variable[];
+ } STRUCT_PACKED fst_action;
} u;
} STRUCT_PACKED action;
} u;
@@ -1067,6 +1083,15 @@
#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
+/* P2PS Coordination Protocol Transport Bitmap */
+#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
+#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
+
+struct p2ps_feature_capab {
+ u8 cpt;
+ u8 reserved;
+} STRUCT_PACKED;
+
/* Invitation Flags */
#define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -1358,4 +1383,60 @@
#define SSID_MAX_LEN 32
+/* IEEE Std 802.11ad-2012 - Multi-band element */
+struct multi_band_ie {
+ u8 eid; /* WLAN_EID_MULTI_BAND */
+ u8 len;
+ u8 mb_ctrl;
+ u8 band_id;
+ u8 op_class;
+ u8 chan;
+ u8 bssid[ETH_ALEN];
+ le16 beacon_int;
+ u8 tsf_offs[8];
+ u8 mb_connection_capability;
+ u8 fst_session_tmout;
+ /* Optional:
+ * STA MAC Address
+ * Pairwise Cipher Suite Count
+ * Pairwise Cipher Suite List
+ */
+ u8 variable[0];
+} STRUCT_PACKED;
+
+enum mb_ctrl_sta_role {
+ MB_STA_ROLE_AP = 0,
+ MB_STA_ROLE_TDLS_STA = 1,
+ MB_STA_ROLE_IBSS_STA = 2,
+ MB_STA_ROLE_PCP = 3,
+ MB_STA_ROLE_NON_PCP_NON_AP = 4
+};
+
+#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
+#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
+#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
+
+enum mb_band_id {
+ MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
+ MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
+ MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
+};
+
+#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
+#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
+#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
+#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
+#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
+
+/* IEEE Std 802.11ad-2014 - FST Action field */
+enum fst_action {
+ FST_ACTION_SETUP_REQUEST = 0,
+ FST_ACTION_SETUP_RESPONSE = 1,
+ FST_ACTION_TEAR_DOWN = 2,
+ FST_ACTION_ACK_REQUEST = 3,
+ FST_ACTION_ACK_RESPONSE = 4,
+ FST_ACTION_ON_CHANNEL_TUNNEL = 5,
+};
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index c6a472d..8dff303 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -26,6 +26,25 @@
PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
PRIVSEP_CMD_L2_SEND,
PRIVSEP_CMD_SET_COUNTRY,
+ PRIVSEP_CMD_AUTHENTICATE,
+};
+
+struct privsep_cmd_authenticate
+{
+ int freq;
+ u8 bssid[ETH_ALEN];
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len;
+ int auth_alg;
+ size_t ie_len;
+ u8 wep_key[4][16];
+ size_t wep_key_len[4];
+ int wep_tx_keyidx;
+ int local_state_change;
+ int p2p;
+ size_t sae_data_len;
+ /* followed by ie_len bytes of ie */
+ /* followed by sae_data_len bytes of sae_data */
};
struct privsep_cmd_associate
@@ -68,6 +87,18 @@
PRIVSEP_EVENT_STKSTART,
PRIVSEP_EVENT_FT_RESPONSE,
PRIVSEP_EVENT_RX_EAPOL,
+ PRIVSEP_EVENT_SCAN_STARTED,
+ PRIVSEP_EVENT_AUTH,
+};
+
+struct privsep_event_auth {
+ u8 peer[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u16 auth_type;
+ u16 auth_transaction;
+ u16 status_code;
+ size_t ies_len;
+ /* followed by ies_len bytes of ies */
};
#endif /* PRIVSEP_COMMANDS_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2a6e242..a3ee91d 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -156,6 +156,12 @@
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+ QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
+ QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
+ QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108,
+ QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
+ /* 110..114 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
};
@@ -205,6 +211,7 @@
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,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -223,6 +230,7 @@
QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ACS_MAX =
@@ -246,11 +254,14 @@
* after roaming, rather than having the user space wpa_supplicant do it.
* @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
* band selection based on channel selection results.
+ * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
+ * simultaneous off-channel operations.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
+ QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -323,4 +334,132 @@
QCA_SETBAND_2G,
};
+/* IEEE 802.11 Vendor Specific elements */
+
+/**
+ * enum qca_vendor_element_id - QCA Vendor Specific element types
+ *
+ * These values are used to identify QCA Vendor Specific elements. The
+ * payload of the element starts with the three octet OUI (OUI_QCA) and
+ * is followed by a single octet type which is defined by this enum.
+ *
+ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
+ * This element can be used to specify preference order for supported
+ * channels. The channels in this list are in preference order (the first
+ * one has the highest preference) and are described as a pair of
+ * (global) Operating Class and Channel Number (each one octet) fields.
+ *
+ * This extends the standard P2P functionality by providing option to have
+ * more than one preferred operating channel. When this element is present,
+ * it replaces the preference indicated in the Operating Channel attribute.
+ * For supporting other implementations, the Operating Channel attribute is
+ * expected to be used with the highest preference channel. Similarly, all
+ * the channels included in this Preferred channel list element are
+ * expected to be included in the Channel List attribute.
+ *
+ * This vendor element may be included in GO Negotiation Request, P2P
+ * Invitation Request, and Provision Discovery Request frames.
+ */
+enum qca_vendor_element_id {
+ QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
+ * with frequencies to be scanned (in MHz)
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
+ * rates to be included
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
+ * at non CCK rate in 2GHz band
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
+ * driver for the specific scan request
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
+ * request decoded as in enum scan_status
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
+ * scan flag is set
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
+ * randomisation
+ */
+enum qca_wlan_vendor_attr_scan {
+ QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
+ QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
+ QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
+ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
+};
+
+/**
+ * enum scan_status - Specifies the valid values the vendor scan attribute
+ * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ *
+ * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
+ * new scan results
+ * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
+ */
+enum scan_status {
+ VENDOR_SCAN_STATUS_NEW_RESULTS,
+ VENDOR_SCAN_STATUS_ABORTED,
+ VENDOR_SCAN_STATUS_MAX,
+};
+
+/**
+ * enum qca_vendor_attr_ota_test - Specifies the values for vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST
+ * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test
+ */
+enum qca_vendor_attr_ota_test {
+ QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID,
+ /* 8-bit unsigned value to indicate if OTA test is enabled */
+ QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX =
+ QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_scale - vendor sub commands index
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value
+ */
+enum qca_vendor_attr_txpower_scale {
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID,
+ /* 8-bit unsigned value to indicate the scaling of tx power */
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease
+ *
+ * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB.
+ */
+enum qca_vendor_attr_txpower_decr_db {
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
+ /* 8-bit unsigned value to indicate the reduction of TX power in dB for
+ * a virtual interface. */
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX =
+ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 503fa1d..b962ea2 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -923,7 +923,7 @@
const u8 *end, const u8 **token,
size_t *token_len)
{
- if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+ if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
sae->tmp->prime_len);
wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
@@ -946,7 +946,7 @@
{
struct crypto_bignum *peer_scalar;
- if (*pos + sae->tmp->prime_len > end) {
+ if (sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -994,7 +994,7 @@
{
u8 prime[SAE_MAX_ECC_PRIME_LEN];
- if (pos + 2 * sae->tmp->prime_len > end) {
+ if (2 * sae->tmp->prime_len > end - pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1040,7 +1040,7 @@
struct crypto_bignum *res, *one;
const u8 one_bin[1] = { 0x01 };
- if (pos + sae->tmp->prime_len > end) {
+ if (sae->tmp->prime_len > end - pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1098,7 +1098,7 @@
u16 res;
/* Check Finite Cyclic Group */
- if (pos + 2 > end)
+ if (end - pos < 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
if (res != WLAN_STATUS_SUCCESS)
diff --git a/src/common/version.h b/src/common/version.h
index 5ddf617..ae5c9d4 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,10 @@
#define VERSION_STR_POSTFIX ""
#endif /* VERSION_STR_POSTFIX */
-#define VERSION_STR "2.5-devel" VERSION_STR_POSTFIX
+#ifndef GIT_VERSION_STR_POSTFIX
+#define GIT_VERSION_STR_POSTFIX ""
+#endif /* GIT_VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.6-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index e485b5b..4091bed 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -170,6 +170,12 @@
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+#ifdef CONFIG_SUITEB192
+ if (wpa_key_mgmt_sha384(akmp))
+ sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ tmp, ptk_len);
+ else
+#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_IEEE80211W
if (wpa_key_mgmt_sha256(akmp))
sha256_prf(pmk, pmk_len, label, data, sizeof(data),
@@ -286,38 +292,47 @@
pos = ie + sizeof(struct rsn_ftie);
end = ie + ie_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
+ while (end - pos >= 2) {
+ u8 id, len;
+
+ id = *pos++;
+ len = *pos++;
+ if (len > end - pos)
+ break;
+
+ switch (id) {
case FTIE_SUBELEM_R1KH_ID:
- if (pos[1] != FT_R1KH_ID_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
- "length in FTIE: %d", pos[1]);
+ if (len != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid R1KH-ID length in FTIE: %d",
+ len);
return -1;
}
- parse->r1kh_id = pos + 2;
+ parse->r1kh_id = pos;
break;
case FTIE_SUBELEM_GTK:
- parse->gtk = pos + 2;
- parse->gtk_len = pos[1];
+ parse->gtk = pos;
+ parse->gtk_len = len;
break;
case FTIE_SUBELEM_R0KH_ID:
- if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
- wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
- "length in FTIE: %d", pos[1]);
+ if (len < 1 || len > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Invalid R0KH-ID length in FTIE: %d",
+ len);
return -1;
}
- parse->r0kh_id = pos + 2;
- parse->r0kh_id_len = pos[1];
+ parse->r0kh_id = pos;
+ parse->r0kh_id_len = len;
break;
#ifdef CONFIG_IEEE80211W
case FTIE_SUBELEM_IGTK:
- parse->igtk = pos + 2;
- parse->igtk_len = pos[1];
+ parse->igtk = pos;
+ parse->igtk_len = len;
break;
#endif /* CONFIG_IEEE80211W */
}
- pos += 2 + pos[1];
+ pos += len;
}
return 0;
@@ -339,11 +354,18 @@
pos = ies;
end = ies + ies_len;
- while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
- switch (pos[0]) {
+ while (end - pos >= 2) {
+ u8 id, len;
+
+ id = *pos++;
+ len = *pos++;
+ if (len > end - pos)
+ break;
+
+ switch (id) {
case WLAN_EID_RSN:
- parse->rsn = pos + 2;
- parse->rsn_len = pos[1];
+ parse->rsn = pos;
+ parse->rsn_len = len;
ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
parse->rsn_len + 2,
&data);
@@ -356,32 +378,32 @@
parse->rsn_pmkid = data.pmkid;
break;
case WLAN_EID_MOBILITY_DOMAIN:
- if (pos[1] < sizeof(struct rsn_mdie))
+ if (len < sizeof(struct rsn_mdie))
return -1;
- parse->mdie = pos + 2;
- parse->mdie_len = pos[1];
+ parse->mdie = pos;
+ parse->mdie_len = len;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
- if (pos[1] < sizeof(*ftie))
+ if (len < sizeof(*ftie))
return -1;
- ftie = (const struct rsn_ftie *) (pos + 2);
+ ftie = (const struct rsn_ftie *) pos;
prot_ie_count = ftie->mic_control[1];
- if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ if (wpa_ft_parse_ftie(pos, len, parse) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
- if (pos[1] != 5)
+ if (len != 5)
break;
- parse->tie = pos + 2;
- parse->tie_len = pos[1];
+ parse->tie = pos;
+ parse->tie_len = len;
break;
case WLAN_EID_RIC_DATA:
if (parse->ric == NULL)
- parse->ric = pos;
+ parse->ric = pos - 2;
break;
}
- pos += 2 + pos[1];
+ pos += len;
}
if (prot_ie_count == 0)
@@ -410,13 +432,15 @@
}
/* Determine the end of the RIC IE(s) */
- pos = parse->ric;
- while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
- prot_ie_count) {
- prot_ie_count--;
- pos += 2 + pos[1];
+ if (parse->ric) {
+ pos = parse->ric;
+ while (end - pos >= 2 && 2 + pos[1] <= end - pos &&
+ prot_ie_count) {
+ prot_ie_count--;
+ pos += 2 + pos[1];
+ }
+ parse->ric_len = pos - parse->ric;
}
- parse->ric_len = pos - parse->ric;
if (prot_ie_count) {
wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
"frame", (int) prot_ie_count);
@@ -492,7 +516,7 @@
}
-static int wpa_cipher_valid_group(int cipher)
+int wpa_cipher_valid_group(int cipher)
{
return wpa_cipher_valid_pairwise(cipher) ||
cipher == WPA_CIPHER_GTK_NOT_USED;
@@ -1292,6 +1316,9 @@
os_memmove(rpos + 2, rpos, end - rpos);
*rpos++ = 0;
*rpos++ = 0;
+ added += 2;
+ start[1] += 2;
+ rend = rpos;
} else {
/* Skip RSN Capabilities */
rpos += 2;
@@ -1304,7 +1331,7 @@
if (rpos == rend) {
/* No PMKID-Count field included; add it */
- os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
+ os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
WPA_PUT_LE16(rpos, 1);
rpos += 2;
os_memcpy(rpos, pmkid, PMKID_LEN);
@@ -1319,7 +1346,7 @@
}
WPA_PUT_LE16(rpos, 1);
rpos += 2;
- os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
+ os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
os_memcpy(rpos, pmkid, PMKID_LEN);
added += PMKID_LEN;
start[1] += PMKID_LEN;
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index d7a590f..ee71bfc 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -12,6 +12,8 @@
/* IEEE 802.11i */
#define PMKID_LEN 16
#define PMK_LEN 32
+#define PMK_LEN_SUITE_B_192 48
+#define PMK_LEN_MAX 48
#define WPA_REPLAY_COUNTER_LEN 8
#define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8
@@ -435,6 +437,7 @@
int wpa_cipher_key_len(int cipher);
int wpa_cipher_rsc_len(int cipher);
int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_group(int cipher);
int wpa_cipher_valid_pairwise(int cipher);
int wpa_cipher_valid_mgmt_group(int cipher);
u32 wpa_cipher_to_suite(int proto, int cipher);
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 82d4655..5733aa6 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -85,6 +85,13 @@
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
{
+ return wpa_ctrl_open2(ctrl_path, NULL);
+}
+
+
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path,
+ const char *cli_path)
+{
struct wpa_ctrl *ctrl;
static int counter = 0;
int ret;
@@ -108,10 +115,18 @@
ctrl->local.sun_family = AF_UNIX;
counter++;
try_again:
- ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
- CONFIG_CTRL_IFACE_CLIENT_DIR "/"
- CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
- (int) getpid(), counter);
+ if (cli_path && cli_path[0] == '/') {
+ ret = os_snprintf(ctrl->local.sun_path,
+ sizeof(ctrl->local.sun_path),
+ "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+ cli_path, (int) getpid(), counter);
+ } else {
+ ret = os_snprintf(ctrl->local.sun_path,
+ sizeof(ctrl->local.sun_path),
+ CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+ CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+ (int) getpid(), counter);
+ }
if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
close(ctrl->s);
os_free(ctrl);
@@ -137,6 +152,8 @@
#ifdef ANDROID
chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ /* Set group even if we do not have privileges to change owner */
+ chown(ctrl->local.sun_path, -1, AID_WIFI);
chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index e700523..3e0a7ec 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -28,6 +28,8 @@
#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
/** Association rejected during connection attempt */
#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
+/** Authentication rejected during connection attempt */
+#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
/** wpa_supplicant is exiting */
#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
/** Password change was completed successfully */
@@ -75,6 +77,19 @@
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+/** IP subnet status change notification
+ *
+ * When using an offloaded roaming mechanism where driver/firmware takes care
+ * of roaming and IP subnet validation checks post-roaming, this event can
+ * indicate whether IP subnet has changed.
+ *
+ * The event has a status=<0/1/2> parameter where
+ * 0 = unknown
+ * 1 = IP subnet unchanged (can continue to use the old IP address)
+ * 2 = IP subnet changed (need to get a new IP address)
+ */
+#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
+
/** RSN IBSS 4-way handshakes completed with specified peer */
#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
@@ -279,6 +294,7 @@
#define WPA_BSS_MASK_MESH_SCAN BIT(18)
#define WPA_BSS_MASK_SNR BIT(19)
#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
+#define WPA_BSS_MASK_FST BIT(21)
/* VENDOR_ELEM_* frame id values */
@@ -315,6 +331,20 @@
*/
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+/**
+ * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
+ * is used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd
+ * when the socket path for client need to be specified explicitly. Default
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
+ * socket path is /tmp.
+ */
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
+
/**
* wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index 28913b9..f159421 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -172,7 +172,8 @@
if (ctrl == NULL)
return -1;
len = sizeof(buf);
- if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+ if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len,
+ NULL) < 0) {
wpa_ctrl_close(ctrl);
return -1;
}
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index 3e90350..d181e72 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -47,7 +47,9 @@
sha256.o \
sha256-prf.o \
sha256-tlsprf.o \
- sha256-internal.o
+ sha256-internal.o \
+ sha384-internal.o \
+ sha512-internal.o
LIB_OBJS += crypto_internal.o
LIB_OBJS += crypto_internal-cipher.o
diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c
index 2833cfc..0835f2c 100644
--- a/src/crypto/aes-cbc.c
+++ b/src/crypto/aes-cbc.c
@@ -28,6 +28,9 @@
u8 *pos = data;
int i, j, blocks;
+ if (TEST_FAIL())
+ return -1;
+
ctx = aes_encrypt_init(key, 16);
if (ctx == NULL)
return -1;
@@ -61,6 +64,9 @@
u8 *pos = data;
int i, j, blocks;
+ if (TEST_FAIL())
+ return -1;
+
ctx = aes_decrypt_init(key, 16);
if (ctx == NULL)
return -1;
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
index 375db57..8642516 100644
--- a/src/crypto/aes-omac1.c
+++ b/src/crypto/aes-omac1.c
@@ -48,6 +48,9 @@
const u8 *pos, *end;
size_t i, e, left, total_len;
+ if (TEST_FAIL())
+ return -1;
+
ctx = aes_encrypt_init(key, key_len);
if (ctx == NULL)
return -1;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 534c4bd..bdc3ba6 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -80,6 +80,28 @@
u8 *mac);
/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+
+/**
* des_encrypt - Encrypt one block with DES
* @clear: 8 octets (in)
* @key: 7 octets (in) (no parity bits included)
@@ -135,7 +157,8 @@
enum crypto_hash_alg {
CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
- CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+ CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
+ CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
};
struct crypto_hash;
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index f3602da..d391f48 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -11,6 +11,8 @@
#include "common.h"
#include "crypto.h"
#include "sha256_i.h"
+#include "sha384_i.h"
+#include "sha512_i.h"
#include "sha1_i.h"
#include "md5_i.h"
@@ -22,6 +24,12 @@
#ifdef CONFIG_SHA256
struct sha256_state sha256;
#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+ struct sha384_state sha384;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+ struct sha512_state sha512;
+#endif /* CONFIG_INTERNAL_SHA512 */
} u;
u8 key[64];
size_t key_len;
@@ -54,6 +62,16 @@
sha256_init(&ctx->u.sha256);
break;
#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+ case CRYPTO_HASH_ALG_SHA384:
+ sha384_init(&ctx->u.sha384);
+ break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+ case CRYPTO_HASH_ALG_SHA512:
+ sha512_init(&ctx->u.sha512);
+ break;
+#endif /* CONFIG_INTERNAL_SHA512 */
case CRYPTO_HASH_ALG_HMAC_MD5:
if (key_len > sizeof(k_pad)) {
MD5Init(&ctx->u.md5);
@@ -142,6 +160,16 @@
sha256_process(&ctx->u.sha256, data, len);
break;
#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+ case CRYPTO_HASH_ALG_SHA384:
+ sha384_process(&ctx->u.sha384, data, len);
+ break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+ case CRYPTO_HASH_ALG_SHA512:
+ sha512_process(&ctx->u.sha512, data, len);
+ break;
+#endif /* CONFIG_INTERNAL_SHA512 */
default:
break;
}
@@ -191,6 +219,28 @@
sha256_done(&ctx->u.sha256, mac);
break;
#endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+ case CRYPTO_HASH_ALG_SHA384:
+ if (*len < 48) {
+ *len = 48;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 48;
+ sha384_done(&ctx->u.sha384, mac);
+ break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+ case CRYPTO_HASH_ALG_SHA512:
+ if (*len < 64) {
+ *len = 64;
+ os_free(ctx);
+ return -1;
+ }
+ *len = 64;
+ sha512_done(&ctx->u.sha512, mac);
+ break;
+#endif /* CONFIG_INTERNAL_SHA512 */
case CRYPTO_HASH_ALG_HMAC_MD5:
if (*len < 16) {
*len = 16;
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 1d613c9..087953b 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -516,6 +516,7 @@
0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
};
+#ifndef CONFIG_BORINGSSL
/* RFC 3394 - Test vector 4.2 */
u8 kek42[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -531,6 +532,7 @@
0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
};
+#endif /* CONFIG_BORINGSSL */
/* RFC 3394 - Test vector 4.3 */
u8 kek43[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -547,6 +549,7 @@
0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
};
+#ifndef CONFIG_BORINGSSL
/* RFC 3394 - Test vector 4.4 */
u8 kek44[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -564,6 +567,7 @@
0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
};
+#endif /* CONFIG_BORINGSSL */
/* RFC 3394 - Test vector 4.5 */
u8 kek45[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -624,6 +628,7 @@
ret++;
}
+#ifndef CONFIG_BORINGSSL
wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
result)) {
@@ -643,6 +648,7 @@
wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
ret++;
}
+#endif /* CONFIG_BORINGSSL */
wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
@@ -664,6 +670,7 @@
ret++;
}
+#ifndef CONFIG_BORINGSSL
wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
result)) {
@@ -683,6 +690,7 @@
wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
ret++;
}
+#endif /* CONFIG_BORINGSSL */
wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
@@ -733,6 +741,7 @@
static int test_md5(void)
{
+#ifndef CONFIG_FIPS
struct {
char *data;
char *hash;
@@ -811,6 +820,10 @@
wpa_printf(MSG_INFO, "MD5 test cases passed");
return errors;
+#else /* CONFIG_FIPS */
+ wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS");
+ return 0;
+#endif /* CONFIG_FIPS */
}
@@ -842,6 +855,7 @@
0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
};
+#ifndef CONFIG_FIPS
const u8 key_block[] = {
0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
@@ -858,6 +872,7 @@
0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
};
+#endif /* CONFIG_FIPS */
const u8 sks[] = {
0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
@@ -932,6 +947,7 @@
errors++;
}
+#ifndef CONFIG_FIPS
wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
"key expansion", seed, sizeof(seed),
@@ -940,6 +956,7 @@
wpa_printf(MSG_INFO, "PRF test - FAILED!");
errors++;
}
+#endif /* CONFIG_FIPS */
wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
@@ -1486,6 +1503,7 @@
const u8 *addr[2];
size_t len[2];
int errors = 0;
+ u8 *key;
for (i = 0; i < ARRAY_SIZE(tests); i++) {
wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
@@ -1556,14 +1574,69 @@
hash, sizeof(hash));
/* TODO: add proper test case for this */
+ key = os_malloc(8161);
+ if (key) {
+#ifdef CONFIG_HMAC_SHA256_KDF
+ int res;
+
+ res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+ (u8 *) "seed", 4, key, 8160);
+ if (res) {
+ wpa_printf(MSG_INFO,
+ "Unexpected hmac_sha256_kdf(outlen=8160) failure");
+ errors++;
+ }
+
+ res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+ (u8 *) "seed", 4, key, 8161);
+ if (res == 0) {
+ wpa_printf(MSG_INFO,
+ "Unexpected hmac_sha256_kdf(outlen=8161) success");
+ errors++;
+ }
+#endif /* CONFIG_HMAC_SHA256_KDF */
+
+ os_free(key);
+ }
+
if (!errors)
wpa_printf(MSG_INFO, "SHA256 test cases passed");
return errors;
}
+static int test_fips186_2_prf(void)
+{
+ /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+ u8 xkey[] = {
+ 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+ 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+ 0xeb, 0x5a, 0x38, 0xb6
+ };
+ u8 w[] = {
+ 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+ 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+ 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+ 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+ 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+ };
+ u8 buf[40];
+
+ wpa_printf(MSG_INFO,
+ "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)");
+ if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 ||
+ os_memcmp(w, buf, sizeof(w)) != 0) {
+ wpa_printf(MSG_INFO, "fips186_2_prf failed");
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int test_ms_funcs(void)
{
+#ifndef CONFIG_FIPS
/* Test vector from RFC2759 example */
char *username = "User";
char *password = "clientPass";
@@ -1656,6 +1729,10 @@
wpa_printf(MSG_INFO, "ms_funcs test cases passed");
return errors;
+#else /* CONFIG_FIPS */
+ wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS");
+ return 0;
+#endif /* CONFIG_FIPS */
}
@@ -1673,6 +1750,7 @@
test_md5() ||
test_sha1() ||
test_sha256() ||
+ test_fips186_2_prf() ||
test_ms_funcs())
ret = -1;
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 3703b93..ad2d2d4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -69,6 +69,9 @@
size_t i;
unsigned int mac_len;
+ if (TEST_FAIL())
+ return -1;
+
EVP_MD_CTX_init(&ctx);
if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
@@ -93,10 +96,12 @@
}
+#ifndef CONFIG_FIPS
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
+#endif /* CONFIG_FIPS */
void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
@@ -120,6 +125,7 @@
}
+#ifndef CONFIG_NO_RC4
int rc4_skip(const u8 *key, size_t keylen, size_t skip,
u8 *data, size_t data_len)
{
@@ -155,12 +161,15 @@
return res;
#endif /* OPENSSL_NO_RC4 */
}
+#endif /* CONFIG_NO_RC4 */
+#ifndef CONFIG_FIPS
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
+#endif /* CONFIG_FIPS */
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
@@ -297,6 +306,9 @@
}
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+
int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
{
AES_KEY actx;
@@ -323,6 +335,9 @@
return res <= 0 ? -1 : 0;
}
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
{
@@ -330,6 +345,9 @@
int clen, len;
u8 buf[16];
+ if (TEST_FAIL())
+ return -1;
+
EVP_CIPHER_CTX_init(&ctx);
if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
return -1;
@@ -355,6 +373,9 @@
int plen, len;
u8 buf[16];
+ if (TEST_FAIL())
+ return -1;
+
EVP_CIPHER_CTX_init(&ctx);
if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
return -1;
@@ -430,11 +451,13 @@
return NULL;
switch (alg) {
+#ifndef CONFIG_NO_RC4
#ifndef OPENSSL_NO_RC4
case CRYPTO_CIPHER_ALG_RC4:
cipher = EVP_rc4();
break;
#endif /* OPENSSL_NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
#ifndef OPENSSL_NO_AES
case CRYPTO_CIPHER_ALG_AES:
switch (key_len) {
@@ -757,6 +780,9 @@
size_t i;
int res;
+ if (TEST_FAIL())
+ return -1;
+
HMAC_CTX_init(&ctx);
#if OPENSSL_VERSION_NUMBER < 0x00909000
HMAC_Init_ex(&ctx, key, key_len, type, NULL);
@@ -878,6 +904,9 @@
int ret = -1;
size_t outlen, i;
+ if (TEST_FAIL())
+ return -1;
+
ctx = CMAC_CTX_new();
if (ctx == NULL)
return -1;
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index ccdbfc8..425c848 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -15,6 +15,7 @@
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
+ wpabuf_free(*publ);
*publ = dh_init(dh_groups_get(5), priv);
if (*publ == NULL)
return NULL;
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 3aeb2bb..7912361 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1218,14 +1218,19 @@
pv_len = dh->prime_len;
pv = wpabuf_alloc(pv_len);
- if (pv == NULL)
+ if (pv == NULL) {
+ wpabuf_clear_free(*priv);
+ *priv = NULL;
return NULL;
+ }
if (crypto_mod_exp(dh->generator, dh->generator_len,
wpabuf_head(*priv), wpabuf_len(*priv),
dh->prime, dh->prime_len, wpabuf_mhead(pv),
&pv_len) < 0) {
wpabuf_clear_free(pv);
wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+ wpabuf_clear_free(*priv);
+ *priv = NULL;
return NULL;
}
wpabuf_put(pv, pv_len);
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index fb03efc..9d094b8 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -17,6 +17,19 @@
{
SHA_CTX context;
os_memset(&context, 0, sizeof(context));
+#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID)
+ context.h[0] = state[0];
+ context.h[1] = state[1];
+ context.h[2] = state[2];
+ context.h[3] = state[3];
+ context.h[4] = state[4];
+ SHA1_Transform(&context, data);
+ state[0] = context.h[0];
+ state[1] = context.h[1];
+ state[2] = context.h[2];
+ state[3] = context.h[3];
+ state[4] = context.h[4];
+#else
context.h0 = state[0];
context.h1 = state[1];
context.h2 = state[2];
@@ -28,6 +41,7 @@
state[2] = context.h2;
state[3] = context.h3;
state[4] = context.h4;
+#endif
}
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
index cd5e6ca..d9c737a 100644
--- a/src/crypto/md4-internal.c
+++ b/src/crypto/md4-internal.c
@@ -31,6 +31,9 @@
MD4_CTX ctx;
size_t i;
+ if (TEST_FAIL())
+ return -1;
+
MD4Init(&ctx);
for (i = 0; i < num_elem; i++)
MD4Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
index f0a2a5d..944698a 100644
--- a/src/crypto/md5-internal.c
+++ b/src/crypto/md5-internal.c
@@ -33,6 +33,9 @@
MD5_CTX ctx;
size_t i;
+ if (TEST_FAIL())
+ return -1;
+
MD5Init(&ctx);
for (i = 0; i < num_elem; i++)
MD5Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index 5f57656..d0d6a96 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -48,7 +48,7 @@
WPA_PUT_LE16(ucs2_buffer + j,
((c & 0x1F) << 6) | (c2 & 0x3F));
j += 2;
- } else if (i == utf8_string_len ||
+ } else if (i == utf8_string_len - 1 ||
j >= ucs2_buffer_size - 1) {
/* incomplete surrogate */
return -1;
@@ -174,9 +174,8 @@
u8 password_hash[16];
if (challenge_hash(peer_challenge, auth_challenge, username,
- username_len, challenge))
- return -1;
- if (nt_password_hash(password, password_len, password_hash))
+ username_len, challenge) ||
+ nt_password_hash(password, password_len, password_hash))
return -1;
challenge_response(challenge, password_hash, response);
return 0;
@@ -256,12 +255,9 @@
addr2[1] = challenge;
addr2[2] = magic2;
- if (hash_nt_password_hash(password_hash, password_hash_hash))
- return -1;
- if (sha1_vector(3, addr1, len1, response))
- return -1;
-
- if (challenge_hash(peer_challenge, auth_challenge, username,
+ if (hash_nt_password_hash(password_hash, password_hash_hash) ||
+ sha1_vector(3, addr1, len1, response) ||
+ challenge_hash(peer_challenge, auth_challenge, username,
username_len, challenge))
return -1;
return sha1_vector(3, addr2, len2, response);
@@ -416,6 +412,8 @@
}
+#ifndef CONFIG_NO_RC4
+
#define PWBLOCK_LEN 516
/**
@@ -435,10 +433,8 @@
os_memset(pw_block, 0, PWBLOCK_LEN);
- if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
- return -1;
-
- if (ucs2_len > 256)
+ if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+ || ucs2_len > 256)
return -1;
offset = (256 - ucs2_len) * 2;
@@ -483,6 +479,8 @@
return 0;
}
+#endif /* CONFIG_NO_RC4 */
+
/**
* nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
diff --git a/src/crypto/random.c b/src/crypto/random.c
index bc758aa..3a86a93 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -181,6 +181,7 @@
#ifdef CONFIG_FIPS
/* Mix in additional entropy from the crypto module */
+ bytes = buf;
left = len;
while (left) {
size_t siz, i;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index 24bc3ff..f6658e6 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -33,6 +33,9 @@
SHA1_CTX ctx;
size_t i;
+ if (TEST_FAIL())
+ return -1;
+
SHA1Init(&ctx);
for (i = 0; i < num_elem; i++)
SHA1Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index 35299b0..86a548e 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -28,6 +28,9 @@
struct sha256_state ctx;
size_t i;
+ if (TEST_FAIL())
+ return -1;
+
sha256_init(&ctx);
for (i = 0; i < num_elem; i++)
if (sha256_process(&ctx, addr[i], len[i]))
diff --git a/src/crypto/sha384-internal.c b/src/crypto/sha384-internal.c
new file mode 100644
index 0000000..646f729
--- /dev/null
+++ b/src/crypto/sha384-internal.c
@@ -0,0 +1,92 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha384_state ctx;
+ size_t i;
+
+ sha384_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha384_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha384_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA384 implementation ===== */
+
+/* This is based on SHA384 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+void sha384_init(struct sha384_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = CONST64(0xcbbb9d5dc1059ed8);
+ md->state[1] = CONST64(0x629a292a367cd507);
+ md->state[2] = CONST64(0x9159015a3070dd17);
+ md->state[3] = CONST64(0x152fecd8f70e5939);
+ md->state[4] = CONST64(0x67332667ffc00b31);
+ md->state[5] = CONST64(0x8eb44a8768581511);
+ md->state[6] = CONST64(0xdb0c2e0d64f98fa7);
+ md->state[7] = CONST64(0x47b5481dbefa4fa4);
+}
+
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ return sha512_process(md, in, inlen);
+}
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (48 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha384_done(struct sha384_state *md, unsigned char *out)
+{
+ unsigned char buf[64];
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ if (sha512_done(md, buf) != 0)
+ return -1;
+
+ os_memcpy(out, buf, 48);
+ return 0;
+}
+
+/* ===== end - public domain SHA384 implementation ===== */
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
new file mode 100644
index 0000000..653920b
--- /dev/null
+++ b/src/crypto/sha384-prf.c
@@ -0,0 +1,100 @@
+/*
+ * SHA384-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+ sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits)
+{
+ u16 counter = 1;
+ size_t pos, plen;
+ u8 hash[SHA384_MAC_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 counter_le[2], length_le[2];
+ size_t buf_len = (buf_len_bits + 7) / 8;
+
+ addr[0] = counter_le;
+ len[0] = 2;
+ addr[1] = (u8 *) label;
+ len[1] = os_strlen(label);
+ addr[2] = data;
+ len[2] = data_len;
+ addr[3] = length_le;
+ len[3] = sizeof(length_le);
+
+ WPA_PUT_LE16(length_le, buf_len_bits);
+ pos = 0;
+ while (pos < buf_len) {
+ plen = buf_len - pos;
+ WPA_PUT_LE16(counter_le, counter);
+ if (plen >= SHA384_MAC_LEN) {
+ hmac_sha384_vector(key, key_len, 4, addr, len,
+ &buf[pos]);
+ pos += SHA384_MAC_LEN;
+ } else {
+ hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ break;
+ }
+ counter++;
+ }
+
+ /*
+ * Mask out unused bits in the last octet if it does not use all the
+ * bits.
+ */
+ if (buf_len_bits % 8) {
+ u8 mask = 0xff << (8 - buf_len_bits % 8);
+ buf[pos - 1] &= mask;
+ }
+
+ os_memset(hash, 0, sizeof(hash));
+}
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index e6a1fe4..3deafa5 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -15,5 +15,10 @@
const u8 *addr[], const size_t *len, u8 *mac);
int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac);
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len_bits);
#endif /* SHA384_H */
diff --git a/src/crypto/sha384_i.h b/src/crypto/sha384_i.h
new file mode 100644
index 0000000..a00253f
--- /dev/null
+++ b/src/crypto/sha384_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA-384 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_I_H
+#define SHA384_I_H
+
+#include "sha512_i.h"
+
+#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
+
+#define sha384_state sha512_state
+
+void sha384_init(struct sha384_state *md);
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha384_done(struct sha384_state *md, unsigned char *out);
+
+#endif /* SHA384_I_H */
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
new file mode 100644
index 0000000..66ef331
--- /dev/null
+++ b/src/crypto/sha512-internal.c
@@ -0,0 +1,264 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ struct sha512_state ctx;
+ size_t i;
+
+ sha512_init(&ctx);
+ for (i = 0; i < num_elem; i++)
+ if (sha512_process(&ctx, addr[i], len[i]))
+ return -1;
+ if (sha512_done(&ctx, mac))
+ return -1;
+ return 0;
+}
+
+
+/* ===== start - public domain SHA512 implementation ===== */
+
+/* This is based on SHA512 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/* the K array */
+static const u64 K[80] = {
+ CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+ CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+ CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+ CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+ CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+ CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+ CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+ CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+ CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+ CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+ CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+ CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+ CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+ CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+ CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+ CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+ CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+ CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+ CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+ CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+ CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+ CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+ CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+ CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+ CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+ CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+ CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+ CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+ CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+ CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+ CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+ CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+ CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+ CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+ CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+ CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+ CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+ CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+ CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+ CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+#define Ch(x,y,z) (z ^ (x & (y ^ z)))
+#define Maj(x,y,z) (((x | y) & z) | (x & y))
+#define S(x, n) ROR64c(x, n)
+#define R(x, n) (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n))
+#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define ROR64c(x, y) \
+ ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
+ ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \
+ CONST64(0xFFFFFFFFFFFFFFFF))
+
+/* compress 1024-bits */
+static int sha512_compress(struct sha512_state *md, unsigned char *buf)
+{
+ u64 S[8], W[80], t0, t1;
+ int i;
+
+ /* copy state into S */
+ for (i = 0; i < 8; i++) {
+ S[i] = md->state[i];
+ }
+
+ /* copy the state into 1024-bits into W[0..15] */
+ for (i = 0; i < 16; i++)
+ W[i] = WPA_GET_BE64(buf + (8 * i));
+
+ /* fill W[16..79] */
+ for (i = 16; i < 80; i++) {
+ W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+ W[i - 16];
+ }
+
+ /* Compress */
+ for (i = 0; i < 80; i++) {
+ t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+ t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+ S[7] = S[6];
+ S[6] = S[5];
+ S[5] = S[4];
+ S[4] = S[3] + t0;
+ S[3] = S[2];
+ S[2] = S[1];
+ S[1] = S[0];
+ S[0] = t0 + t1;
+ }
+
+ /* feedback */
+ for (i = 0; i < 8; i++) {
+ md->state[i] = md->state[i] + S[i];
+ }
+
+ return 0;
+}
+
+
+/**
+ Initialize the hash state
+ @param md The hash state you wish to initialize
+ @return CRYPT_OK if successful
+*/
+void sha512_init(struct sha512_state *md)
+{
+ md->curlen = 0;
+ md->length = 0;
+ md->state[0] = CONST64(0x6a09e667f3bcc908);
+ md->state[1] = CONST64(0xbb67ae8584caa73b);
+ md->state[2] = CONST64(0x3c6ef372fe94f82b);
+ md->state[3] = CONST64(0xa54ff53a5f1d36f1);
+ md->state[4] = CONST64(0x510e527fade682d1);
+ md->state[5] = CONST64(0x9b05688c2b3e6c1f);
+ md->state[6] = CONST64(0x1f83d9abfb41bd6b);
+ md->state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+
+/**
+ Process a block of memory though the hash
+ @param md The hash state
+ @param in The data to hash
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+ unsigned long inlen)
+{
+ unsigned long n;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ while (inlen > 0) {
+ if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) {
+ if (sha512_compress(md, (unsigned char *) in) < 0)
+ return -1;
+ md->length += SHA512_BLOCK_SIZE * 8;
+ in += SHA512_BLOCK_SIZE;
+ inlen -= SHA512_BLOCK_SIZE;
+ } else {
+ n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen));
+ os_memcpy(md->buf + md->curlen, in, n);
+ md->curlen += n;
+ in += n;
+ inlen -= n;
+ if (md->curlen == SHA512_BLOCK_SIZE) {
+ if (sha512_compress(md, md->buf) < 0)
+ return -1;
+ md->length += 8 * SHA512_BLOCK_SIZE;
+ md->curlen = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ Terminate the hash to get the digest
+ @param md The hash state
+ @param out [out] The destination of the hash (64 bytes)
+ @return CRYPT_OK if successful
+*/
+int sha512_done(struct sha512_state *md, unsigned char *out)
+{
+ int i;
+
+ if (md->curlen >= sizeof(md->buf))
+ return -1;
+
+ /* increase the length of the message */
+ md->length += md->curlen * CONST64(8);
+
+ /* append the '1' bit */
+ md->buf[md->curlen++] = (unsigned char) 0x80;
+
+ /* if the length is currently above 112 bytes we append zeros
+ * then compress. Then we can fall back to padding zeros and length
+ * encoding like normal.
+ */
+ if (md->curlen > 112) {
+ while (md->curlen < 128) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+ sha512_compress(md, md->buf);
+ md->curlen = 0;
+ }
+
+ /* pad upto 120 bytes of zeroes
+ * note: that from 112 to 120 is the 64 MSB of the length. We assume
+ * that you won't hash > 2^64 bits of data... :-)
+ */
+ while (md->curlen < 120) {
+ md->buf[md->curlen++] = (unsigned char) 0;
+ }
+
+ /* store length */
+ WPA_PUT_BE64(md->buf + 120, md->length);
+ sha512_compress(md, md->buf);
+
+ /* copy output */
+ for (i = 0; i < 8; i++)
+ WPA_PUT_BE64(out + (8 * i), md->state[i]);
+
+ return 0;
+}
+
+/* ===== end - public domain SHA512 implementation ===== */
diff --git a/src/crypto/sha512_i.h b/src/crypto/sha512_i.h
new file mode 100644
index 0000000..1089589
--- /dev/null
+++ b/src/crypto/sha512_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-512 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_I_H
+#define SHA512_I_H
+
+#define SHA512_BLOCK_SIZE 128
+
+struct sha512_state {
+ u64 length, state[8];
+ u32 curlen;
+ u8 buf[SHA512_BLOCK_SIZE];
+};
+
+void sha512_init(struct sha512_state *md);
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+ unsigned long inlen);
+int sha512_done(struct sha512_state *md, unsigned char *out);
+
+#endif /* SHA512_I_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index d13657e..2e56233 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -11,7 +11,7 @@
struct tls_connection;
-struct tls_keys {
+struct tls_random {
const u8 *client_random;
size_t client_random_len;
const u8 *server_random;
@@ -79,6 +79,7 @@
int fips_mode;
int cert_in_cb;
const char *openssl_ciphers;
+ unsigned int tls_session_lifetime;
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
@@ -93,6 +94,7 @@
#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
#define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
/**
* struct tls_connection_params - Parameters for TLS connection
@@ -304,22 +306,28 @@
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
* @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
* Returns: 0 on success, -1 on failure
*/
int __must_check tls_connection_set_verify(void *tls_ctx,
struct tls_connection *conn,
- int verify_peer);
+ int verify_peer,
+ unsigned int flags,
+ const u8 *session_ctx,
+ size_t session_ctx_len);
/**
- * tls_connection_get_keys - Get random data from TLS connection
+ * tls_connection_get_random - Get random data from TLS connection
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
- * @keys: Structure of client/server random data (filled on success)
+ * @data: Structure of client/server random data (filled on success)
* Returns: 0 on success, -1 on failure
*/
-int __must_check tls_connection_get_keys(void *tls_ctx,
+int __must_check tls_connection_get_random(void *tls_ctx,
struct tls_connection *conn,
- struct tls_keys *keys);
+ struct tls_random *data);
/**
* tls_connection_prf - Use TLS-PRF to derive keying material
@@ -333,14 +341,11 @@
* @out_len: Length of the output buffer
* Returns: 0 on success, -1 on failure
*
- * This function is optional to implement if tls_connection_get_keys() provides
- * access to master secret and server/client random values. If these values are
- * not exported from the TLS library, tls_connection_prf() is required so that
- * further keying material can be derived from the master secret. If not
- * implemented, the function will still need to be defined, but it can just
- * return -1. Example implementation of this function is in tls_prf_sha1_md5()
- * when it is called with seed set to client_random|server_random (or
- * server_random|client_random).
+ * tls_connection_prf() is required so that further keying material can be
+ * derived from the master secret. Example implementation of this function is in
+ * tls_prf_sha1_md5() when it is called with seed set to
+ * client_random|server_random (or server_random|client_random). For TLSv1.2 and
+ * newer, a different PRF is needed, though.
*/
int __must_check tls_connection_prf(void *tls_ctx,
struct tls_connection *conn,
@@ -466,6 +471,19 @@
u8 *ciphers);
/**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen);
+
+/**
* tls_get_cipher - Get current cipher name
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
@@ -532,13 +550,6 @@
int tls_connection_get_write_alerts(void *tls_ctx,
struct tls_connection *conn);
-/**
- * tls_capabilities - Get supported TLS capabilities
- * @tls_ctx: TLS context data from tls_init()
- * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
- */
-unsigned int tls_capabilities(void *tls_ctx);
-
typedef int (*tls_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
const u8 *server_random, u8 *master_secret);
@@ -564,4 +575,14 @@
int tls_get_library_version(char *buf, size_t buf_len);
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
#endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index c7f6464..f994379 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -708,7 +708,8 @@
int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
- int verify_peer)
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
{
if (conn == NULL || conn->session == NULL)
return -1;
@@ -722,8 +723,8 @@
}
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
- struct tls_keys *keys)
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_random *keys)
{
#if GNUTLS_VERSION_NUMBER >= 0x030012
gnutls_datum_t client, server;
@@ -1426,6 +1427,14 @@
}
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ /* TODO */
+ return -1;
+}
+
+
int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
@@ -1476,12 +1485,6 @@
}
-unsigned int tls_capabilities(void *tls_ctx)
-{
- return 0;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb, void *ctx)
@@ -1495,3 +1498,26 @@
return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
GNUTLS_VERSION, gnutls_check_version(NULL));
}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index afd4695..dcbb31d 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -23,6 +23,11 @@
int server;
struct tlsv1_credentials *server_cred;
int check_crl;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
+ int cert_in_cb;
};
struct tls_connection {
@@ -51,6 +56,11 @@
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
+ if (conf) {
+ global->event_cb = conf->event_cb;
+ global->cb_ctx = conf->cb_ctx;
+ global->cert_in_cb = conf->cert_in_cb;
+ }
return global;
}
@@ -64,10 +74,12 @@
tlsv1_client_global_deinit();
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
- tlsv1_cred_free(global->server_cred);
tlsv1_server_global_deinit();
#endif /* CONFIG_TLS_INTERNAL_SERVER */
}
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ tlsv1_cred_free(global->server_cred);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
os_free(global);
}
@@ -95,6 +107,8 @@
os_free(conn);
return NULL;
}
+ tlsv1_client_set_cb(conn->client, global->event_cb,
+ global->cb_ctx, global->cert_in_cb);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -259,8 +273,7 @@
return -1;
}
- tlsv1_client_set_time_checks(
- conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+ tlsv1_client_set_flags(conn->client, params->flags);
return 0;
#else /* CONFIG_TLS_INTERNAL_CLIENT */
@@ -328,7 +341,8 @@
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
- int verify_peer)
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
{
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
@@ -338,16 +352,16 @@
}
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
- struct tls_keys *keys)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+ struct tls_random *data)
{
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client)
- return tlsv1_client_get_keys(conn->client, keys);
+ return tlsv1_client_get_random(conn->client, data);
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server)
- return tlsv1_server_get_keys(conn->server, keys);
+ return tlsv1_server_get_random(conn->server, data);
#endif /* CONFIG_TLS_INTERNAL_SERVER */
return -1;
}
@@ -389,14 +403,14 @@
if (conn->client) {
ret = tlsv1_client_prf(conn->client, label,
server_random_first,
- _out, out_len);
+ _out, skip + out_len);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
ret = tlsv1_server_prf(conn->server, label,
server_random_first,
- _out, out_len);
+ _out, skip + out_len);
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
if (ret == 0 && skip_keyblock)
@@ -617,6 +631,19 @@
}
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ if (conn == NULL)
+ return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+ if (conn->client)
+ return tlsv1_client_get_version(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+ return -1;
+}
+
+
int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
@@ -674,12 +701,6 @@
}
-unsigned int tls_capabilities(void *tls_ctx)
-{
- return 0;
-}
-
-
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
@@ -705,3 +726,26 @@
{
return os_snprintf(buf, buf_len, "internal");
}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 1b1ba56..ae392ad 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -72,14 +72,15 @@
int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
- int verify_peer)
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
{
return -1;
}
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
- struct tls_keys *keys)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+ struct tls_random *data)
{
return -1;
}
@@ -140,6 +141,13 @@
}
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ return -1;
+}
+
+
int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
@@ -181,13 +189,30 @@
}
-unsigned int tls_capabilities(void *tls_ctx)
-{
- return 0;
-}
-
-
int tls_get_library_version(char *buf, size_t buf_len)
{
return os_snprintf(buf, buf_len, "none");
}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index db2d73e..471ae2b 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -1,6 +1,6 @@
/*
* SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -23,11 +23,19 @@
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif /* OPENSSL_NO_ENGINE */
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
#include "common.h"
#include "crypto.h"
#include "sha1.h"
+#include "sha256.h"
#include "tls.h"
+#include "tls_openssl.h"
#if OPENSSL_VERSION_NUMBER < 0x10000000L
/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
@@ -64,10 +72,10 @@
free(value);
return bio;
}
-
#endif /* ANDROID */
static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
struct tls_context {
void (*event_cb)(void *ctx, enum tls_event ev,
@@ -80,6 +88,11 @@
static struct tls_context *tls_global = NULL;
+struct tls_data {
+ SSL_CTX *ssl;
+ unsigned int tls_session_lifetime;
+};
+
struct tls_connection {
struct tls_context *context;
SSL_CTX *ssl_ctx;
@@ -103,6 +116,7 @@
unsigned int cert_probe:1;
unsigned int server_cert_only:1;
unsigned int invalid_hb_used:1;
+ unsigned int success_data:1;
u8 srv_cert_hash[32];
@@ -111,6 +125,11 @@
X509 *peer_cert;
X509 *peer_issuer;
X509 *peer_issuer_issuer;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char server_random[SSL3_RANDOM_SIZE];
+#endif
};
@@ -733,8 +752,27 @@
#endif /* OPENSSL_NO_ENGINE */
+static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
+{
+ struct wpabuf *buf;
+
+ if (tls_ex_idx_session < 0)
+ return;
+ buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (!buf)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Free application session data %p (sess %p)",
+ buf, sess);
+ wpabuf_free(buf);
+
+ SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
void * tls_init(const struct tls_config *conf)
{
+ struct tls_data *data;
SSL_CTX *ssl;
struct tls_context *context;
const char *ciphers;
@@ -746,7 +784,9 @@
#ifdef CONFIG_FIPS
#ifdef OPENSSL_FIPS
if (conf && conf->fips_mode) {
- if (!FIPS_mode_set(1)) {
+ static int fips_enabled = 0;
+
+ if (!fips_enabled && !FIPS_mode_set(1)) {
wpa_printf(MSG_ERROR, "Failed to enable FIPS "
"mode");
ERR_load_crypto_strings();
@@ -754,8 +794,10 @@
os_free(tls_global);
tls_global = NULL;
return NULL;
- } else
+ } else {
wpa_printf(MSG_INFO, "Running in FIPS mode");
+ fips_enabled = 1;
+ }
}
#else /* OPENSSL_FIPS */
if (conf && conf->fips_mode) {
@@ -795,7 +837,11 @@
}
tls_openssl_ref_count++;
- ssl = SSL_CTX_new(SSLv23_method());
+ data = os_zalloc(sizeof(*data));
+ if (data)
+ ssl = SSL_CTX_new(SSLv23_method());
+ else
+ ssl = NULL;
if (ssl == NULL) {
tls_openssl_ref_count--;
if (context != tls_global)
@@ -806,12 +852,37 @@
}
return NULL;
}
+ data->ssl = ssl;
+ if (conf)
+ data->tls_session_lifetime = conf->tls_session_lifetime;
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);
SSL_CTX_set_app_data(ssl, context);
+ if (data->tls_session_lifetime > 0) {
+ SSL_CTX_set_quiet_shutdown(ssl, 1);
+ /*
+ * Set default context here. In practice, this will be replaced
+ * by the per-EAP method context in tls_connection_set_verify().
+ */
+ SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
+ SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
+ SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
+ SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
+ } else {
+ SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
+ }
+
+ if (tls_ex_idx_session < 0) {
+ tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
+ 0, NULL, NULL, NULL, NULL);
+ if (tls_ex_idx_session < 0) {
+ tls_deinit(data);
+ return NULL;
+ }
+ }
#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
@@ -824,7 +895,7 @@
if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
conf->pkcs11_module_path)) {
- tls_deinit(ssl);
+ tls_deinit(data);
return NULL;
}
}
@@ -838,20 +909,23 @@
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to set cipher string '%s'",
ciphers);
- tls_deinit(ssl);
+ tls_deinit(data);
return NULL;
}
- return ssl;
+ return data;
}
void tls_deinit(void *ssl_ctx)
{
- SSL_CTX *ssl = ssl_ctx;
+ struct tls_data *data = ssl_ctx;
+ SSL_CTX *ssl = data->ssl;
struct tls_context *context = SSL_CTX_get_app_data(ssl);
if (context != tls_global)
os_free(context);
+ if (data->tls_session_lifetime > 0)
+ SSL_CTX_flush_sessions(ssl, 0);
SSL_CTX_free(ssl);
tls_openssl_ref_count--;
@@ -868,12 +942,10 @@
os_free(tls_global);
tls_global = NULL;
}
+
+ os_free(data);
}
-#ifdef ANDROID
-/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
-EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
-#endif
#ifndef OPENSSL_NO_ENGINE
@@ -896,6 +968,11 @@
#endif /* OPENSSL_NO_ENGINE */
+#ifdef ANDROID
+/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
+EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
+#endif /* ANDROID */
+
static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
const char *pin, const char *key_id,
const char *cert_id, const char *ca_cert_id)
@@ -904,7 +981,8 @@
#if !defined(OPENSSL_NO_ENGINE)
#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
#endif
-
+ if (!key_id)
+ return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
conn->engine = NULL;
conn->private_key = EVP_PKEY_from_keystore(key_id);
if (!conn->private_key) {
@@ -914,7 +992,7 @@
ERR_error_string(ERR_get_error(), NULL));
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
}
-#endif
+#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
#ifndef OPENSSL_NO_ENGINE
int ret = -1;
@@ -1022,10 +1100,10 @@
if (conn->engine) {
#if !defined(OPENSSL_IS_BORINGSSL)
ENGINE_finish(conn->engine);
-#endif
+#endif /* !OPENSSL_IS_BORINGSSL */
conn->engine = NULL;
}
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID || !OPENSSL_NO_ENGINE */
}
@@ -1044,14 +1122,83 @@
}
+static const char * openssl_content_type(int content_type)
+{
+ switch (content_type) {
+ case 20:
+ return "change cipher spec";
+ case 21:
+ return "alert";
+ case 22:
+ return "handshake";
+ case 23:
+ return "application data";
+ case 24:
+ return "heartbeat";
+ case 256:
+ return "TLS header info"; /* pseudo content type */
+ default:
+ return "?";
+ }
+}
+
+
+static const char * openssl_handshake_type(int content_type, const u8 *buf,
+ size_t len)
+{
+ if (content_type != 22 || !buf || len == 0)
+ return "";
+ switch (buf[0]) {
+ case 0:
+ return "hello request";
+ case 1:
+ return "client hello";
+ case 2:
+ return "server hello";
+ case 4:
+ return "new session ticket";
+ case 11:
+ return "certificate";
+ case 12:
+ return "server key exchange";
+ case 13:
+ return "certificate request";
+ case 14:
+ return "server hello done";
+ case 15:
+ return "certificate verify";
+ case 16:
+ return "client key exchange";
+ case 20:
+ return "finished";
+ case 21:
+ return "certificate url";
+ case 22:
+ return "certificate status";
+ default:
+ return "?";
+ }
+}
+
+
static void tls_msg_cb(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg)
{
struct tls_connection *conn = arg;
const u8 *pos = buf;
- wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
- write_p ? "TX" : "RX", version, content_type);
+ if (write_p == 2) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: session ver=0x%x content_type=%d",
+ version, content_type);
+ wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
+ write_p ? "TX" : "RX", version, content_type,
+ openssl_content_type(content_type),
+ openssl_handshake_type(content_type, buf, len));
wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
if (content_type == 24 && len >= 3 && pos[0] == 1) {
size_t payload_len = WPA_GET_BE16(pos + 1);
@@ -1065,7 +1212,8 @@
struct tls_connection * tls_connection_init(void *ssl_ctx)
{
- SSL_CTX *ssl = ssl_ctx;
+ struct tls_data *data = ssl_ctx;
+ SSL_CTX *ssl = data->ssl;
struct tls_connection *conn;
long options;
struct tls_context *context = SSL_CTX_get_app_data(ssl);
@@ -1073,7 +1221,7 @@
conn = os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
- conn->ssl_ctx = ssl_ctx;
+ conn->ssl_ctx = ssl;
conn->ssl = SSL_new(ssl);
if (conn->ssl == NULL) {
tls_show_errors(MSG_INFO, __func__,
@@ -1122,6 +1270,14 @@
{
if (conn == NULL)
return;
+ if (conn->success_data) {
+ /*
+ * Make sure ssl_clear_bad_session() does not remove this
+ * session.
+ */
+ SSL_set_quiet_shutdown(conn->ssl, 1);
+ SSL_shutdown(conn->ssl);
+ }
SSL_free(conn->ssl);
tls_engine_deinit(conn);
os_free(conn->subject_match);
@@ -1639,6 +1795,32 @@
TLS_FAIL_SERVER_CHAIN_PROBE);
}
+#ifdef OPENSSL_IS_BORINGSSL
+ if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+ preverify_ok) {
+ enum ocsp_result res;
+
+ res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+ conn->peer_issuer,
+ conn->peer_issuer_issuer);
+ if (res == OCSP_REVOKED) {
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "certificate revoked",
+ TLS_FAIL_REVOKED);
+ if (err == X509_V_OK)
+ X509_STORE_CTX_set_error(
+ x509_ctx, X509_V_ERR_CERT_REVOKED);
+ } else if (res != OCSP_GOOD &&
+ (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "bad certificate status response",
+ TLS_FAIL_UNSPECIFIED);
+ }
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+
if (preverify_ok && context->event_cb != NULL)
context->event_cb(context->cb_ctx,
TLS_CERT_CHAIN_SUCCESS, NULL);
@@ -1648,9 +1830,9 @@
#ifndef OPENSSL_NO_STDIO
-static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
{
- SSL_CTX *ssl_ctx = _ssl_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
X509_LOOKUP *lookup;
int ret = 0;
@@ -1680,11 +1862,12 @@
#endif /* OPENSSL_NO_STDIO */
-static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
+static int tls_connection_ca_cert(struct tls_data *data,
+ struct tls_connection *conn,
const char *ca_cert, const u8 *ca_cert_blob,
size_t ca_cert_blob_len, const char *ca_path)
{
- SSL_CTX *ssl_ctx = _ssl_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
X509_STORE *store;
/*
@@ -1819,7 +2002,7 @@
tls_show_errors(MSG_WARNING, __func__,
"Failed to load root certificates");
if (ca_cert &&
- tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
+ tls_load_ca_der(data, ca_cert) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
"DER format CA certificate",
__func__);
@@ -1828,7 +2011,7 @@
} else {
wpa_printf(MSG_DEBUG, "TLS: Trusted root "
"certificate(s) loaded");
- tls_get_errors(ssl_ctx);
+ tls_get_errors(data);
}
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
@@ -1845,8 +2028,10 @@
}
-static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
+static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
{
+ SSL_CTX *ssl_ctx = data->ssl;
+
if (ca_cert) {
if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
{
@@ -1874,7 +2059,8 @@
int flags;
if (check_crl) {
- X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
+ struct tls_data *data = ssl_ctx;
+ X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
if (cs == NULL) {
tls_show_errors(MSG_INFO, __func__, "Failed to get "
"certificate store when enabling "
@@ -1932,10 +2118,44 @@
}
+static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+{
+#ifdef SSL_OP_NO_TICKET
+ if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
+ SSL_set_options(ssl, SSL_OP_NO_TICKET);
+#ifdef SSL_clear_options
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+#endif /* SSL_clear_options */
+#endif /* SSL_OP_NO_TICKET */
+
+#ifdef SSL_OP_NO_TLSv1
+ if (flags & TLS_CONN_DISABLE_TLSv1_0)
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
+#endif /* SSL_OP_NO_TLSv1 */
+#ifdef SSL_OP_NO_TLSv1_1
+ if (flags & TLS_CONN_DISABLE_TLSv1_1)
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+ if (flags & TLS_CONN_DISABLE_TLSv1_2)
+ SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+ else
+ SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+}
+
+
int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
- int verify_peer)
+ int verify_peer, unsigned int flags,
+ const u8 *session_ctx, size_t session_ctx_len)
{
static int counter = 0;
+ struct tls_data *data = ssl_ctx;
if (conn == NULL)
return -1;
@@ -1950,20 +2170,25 @@
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
}
+ tls_set_conn_flags(conn->ssl, flags);
+ conn->flags = flags;
+
SSL_set_accept_state(conn->ssl);
- /*
- * Set session id context in order to avoid fatal errors when client
- * tries to resume a session. However, set the context to a unique
- * value in order to effectively disable session resumption for now
- * since not all areas of the server code are ready for it (e.g.,
- * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
- * handshake).
- */
- counter++;
- SSL_set_session_id_context(conn->ssl,
- (const unsigned char *) &counter,
- sizeof(counter));
+ if (data->tls_session_lifetime == 0) {
+ /*
+ * Set session id context to a unique value to make sure
+ * session resumption cannot be used either through session
+ * caching or TLS ticket extension.
+ */
+ counter++;
+ SSL_set_session_id_context(conn->ssl,
+ (const unsigned char *) &counter,
+ sizeof(counter));
+ } else if (session_ctx) {
+ SSL_set_session_id_context(conn->ssl, session_ctx,
+ session_ctx_len);
+ }
return 0;
}
@@ -2035,9 +2260,12 @@
}
-static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
+static int tls_global_client_cert(struct tls_data *data,
+ const char *client_cert)
{
#ifndef OPENSSL_NO_STDIO
+ SSL_CTX *ssl_ctx = data->ssl;
+
if (client_cert == NULL)
return 0;
@@ -2071,7 +2299,7 @@
#ifdef PKCS12_FUNCS
-static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
+static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
const char *passwd)
{
EVP_PKEY *pkey;
@@ -2083,6 +2311,8 @@
pkey = NULL;
cert = NULL;
certs = NULL;
+ if (!passwd)
+ passwd = "";
if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to parse PKCS12 file");
@@ -2100,7 +2330,7 @@
if (SSL_use_certificate(ssl, cert) != 1)
res = -1;
} else {
- if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+ if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
res = -1;
}
X509_free(cert);
@@ -2112,13 +2342,52 @@
if (SSL_use_PrivateKey(ssl, pkey) != 1)
res = -1;
} else {
- if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+ if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
res = -1;
}
EVP_PKEY_free(pkey);
}
if (certs) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+ SSL_clear_chain_certs(ssl);
+ while ((cert = sk_X509_pop(certs)) != NULL) {
+ X509_NAME_oneline(X509_get_subject_name(cert), buf,
+ sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+ " from PKCS12: subject='%s'", buf);
+ if (SSL_add1_chain_cert(ssl, cert) != 1) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to add additional certificate");
+ res = -1;
+ break;
+ }
+ }
+ if (!res) {
+ /* Try to continue anyway */
+ }
+ sk_X509_free(certs);
+#ifndef OPENSSL_IS_BORINGSSL
+ res = SSL_build_cert_chain(ssl,
+ SSL_BUILD_CHAIN_FLAG_CHECK |
+ SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+ if (!res) {
+ tls_show_errors(MSG_DEBUG, __func__,
+ "Failed to build certificate chain");
+ } else if (res == 2) {
+ wpa_printf(MSG_DEBUG,
+ "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+ /*
+ * Try to continue regardless of result since it is possible for
+ * the extra certificates not to be required.
+ */
+ res = 0;
+#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ SSL_CTX_clear_extra_chain_certs(data->ssl);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
while ((cert = sk_X509_pop(certs)) != NULL) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
@@ -2128,26 +2397,28 @@
* There is no SSL equivalent for the chain cert - so
* always add it to the context...
*/
- if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
+ if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+ {
res = -1;
break;
}
}
sk_X509_free(certs);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
}
PKCS12_free(p12);
if (res < 0)
- tls_get_errors(ssl_ctx);
+ tls_get_errors(data);
return res;
}
#endif /* PKCS12_FUNCS */
-static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
- const char *passwd)
+static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
+ const char *private_key, const char *passwd)
{
#ifdef PKCS12_FUNCS
FILE *f;
@@ -2166,7 +2437,7 @@
return -1;
}
- return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+ return tls_parse_pkcs12(data, ssl, p12, passwd);
#else /* PKCS12_FUNCS */
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
@@ -2176,7 +2447,7 @@
}
-static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
+static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
const u8 *blob, size_t len, const char *passwd)
{
#ifdef PKCS12_FUNCS
@@ -2189,7 +2460,7 @@
return -1;
}
- return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+ return tls_parse_pkcs12(data, ssl, p12, passwd);
#else /* PKCS12_FUNCS */
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
@@ -2260,13 +2531,13 @@
}
-static int tls_connection_engine_ca_cert(void *_ssl_ctx,
+static int tls_connection_engine_ca_cert(struct tls_data *data,
struct tls_connection *conn,
const char *ca_cert_id)
{
#ifndef OPENSSL_NO_ENGINE
X509 *cert;
- SSL_CTX *ssl_ctx = _ssl_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
X509_STORE *store;
if (tls_engine_get_cert(conn, ca_cert_id, &cert))
@@ -2332,14 +2603,14 @@
}
-static int tls_connection_private_key(void *_ssl_ctx,
+static int tls_connection_private_key(struct tls_data *data,
struct tls_connection *conn,
const char *private_key,
const char *private_key_passwd,
const u8 *private_key_blob,
size_t private_key_blob_len)
{
- SSL_CTX *ssl_ctx = _ssl_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
char *passwd;
int ok;
@@ -2385,7 +2656,7 @@
break;
}
- if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
+ if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
private_key_blob_len, passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
"OK");
@@ -2418,7 +2689,7 @@
__func__);
#endif /* OPENSSL_NO_STDIO */
- if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
+ if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
== 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
"--> OK");
@@ -2457,9 +2728,11 @@
}
-static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
+static int tls_global_private_key(struct tls_data *data,
+ const char *private_key,
const char *private_key_passwd)
{
+ SSL_CTX *ssl_ctx = data->ssl;
char *passwd;
if (private_key == NULL)
@@ -2481,7 +2754,7 @@
SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
SSL_FILETYPE_PEM) != 1 &&
#endif /* OPENSSL_NO_STDIO */
- tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
+ tls_read_pkcs12(data, NULL, private_key, passwd)) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
os_free(passwd);
@@ -2576,7 +2849,7 @@
}
-static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
+static int tls_global_dh(struct tls_data *data, const char *dh_file)
{
#ifdef OPENSSL_NO_DH
if (dh_file == NULL)
@@ -2585,6 +2858,7 @@
"dh_file specified");
return -1;
#else /* OPENSSL_NO_DH */
+ SSL_CTX *ssl_ctx = data->ssl;
DH *dh;
BIO *bio;
@@ -2650,19 +2924,15 @@
}
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
- struct tls_keys *keys)
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+ struct tls_random *keys)
{
-#ifdef CONFIG_FIPS
- wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
- "mode");
- return -1;
-#else /* CONFIG_FIPS */
SSL *ssl;
if (conn == NULL || keys == NULL)
return -1;
ssl = conn->ssl;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
return -1;
@@ -2671,14 +2941,27 @@
keys->client_random_len = SSL3_RANDOM_SIZE;
keys->server_random = ssl->s3->server_random;
keys->server_random_len = SSL3_RANDOM_SIZE;
+#else
+ if (ssl == NULL)
+ return -1;
+
+ os_memset(keys, 0, sizeof(*keys));
+ keys->client_random = conn->client_random;
+ keys->client_random_len = SSL_get_client_random(
+ ssl, conn->client_random, sizeof(conn->client_random));
+ keys->server_random = conn->server_random;
+ keys->server_random_len = SSL_get_server_random(
+ ssl, conn->server_random, sizeof(conn->server_random));
+#endif
return 0;
-#endif /* CONFIG_FIPS */
}
+#ifndef CONFIG_FIPS
static int openssl_get_keyblock_size(SSL *ssl)
{
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
const EVP_CIPHER *c;
const EVP_MD *h;
int md_size;
@@ -2708,10 +2991,38 @@
return 2 * (EVP_CIPHER_key_length(c) +
md_size +
EVP_CIPHER_iv_length(c));
+#else
+ const SSL_CIPHER *ssl_cipher;
+ int cipher, digest;
+ const EVP_CIPHER *c;
+ const EVP_MD *h;
+
+ ssl_cipher = SSL_get_current_cipher(ssl);
+ if (!ssl_cipher)
+ return -1;
+ cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
+ digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
+ wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
+ cipher, digest);
+ if (cipher < 0 || digest < 0)
+ return -1;
+ c = EVP_get_cipherbynid(cipher);
+ h = EVP_get_digestbynid(digest);
+ if (!c || !h)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
+ EVP_CIPHER_key_length(c), EVP_MD_size(h),
+ EVP_CIPHER_iv_length(c));
+ return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
+ EVP_CIPHER_iv_length(c));
+#endif
}
+#endif /* CONFIG_FIPS */
-static int openssl_tls_prf(void *tls_ctx, struct tls_connection *conn,
+static int openssl_tls_prf(struct tls_connection *conn,
const char *label, int server_random_first,
int skip_keyblock, u8 *out, size_t out_len)
{
@@ -2720,12 +3031,14 @@
"mode");
return -1;
#else /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
SSL *ssl;
u8 *rnd;
int ret = -1;
int skip = 0;
u8 *tmp_out = NULL;
u8 *_out = out;
+ const char *ver;
/*
* TLS library did not support key generation, so get the needed TLS
@@ -2739,6 +3052,7 @@
if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
ssl->session->master_key_length <= 0)
return -1;
+ ver = SSL_get_version(ssl);
if (skip_keyblock) {
skip = openssl_get_keyblock_size(ssl);
@@ -2766,19 +3080,103 @@
SSL3_RANDOM_SIZE);
}
- /* TODO: TLSv1.2 may need another PRF. This could use something closer
- * to SSL_export_keying_material() design. */
- if (tls_prf_sha1_md5(ssl->session->master_key,
- ssl->session->master_key_length,
- label, rnd, 2 * SSL3_RANDOM_SIZE,
- _out, skip + out_len) == 0)
+ if (os_strcmp(ver, "TLSv1.2") == 0) {
+ tls_prf_sha256(ssl->session->master_key,
+ ssl->session->master_key_length,
+ label, rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len);
ret = 0;
+ } else if (tls_prf_sha1_md5(ssl->session->master_key,
+ ssl->session->master_key_length,
+ label, rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len) == 0) {
+ ret = 0;
+ }
os_free(rnd);
if (ret == 0 && skip_keyblock)
os_memcpy(out, _out + skip, out_len);
bin_clear_free(tmp_out, skip);
return ret;
+#else
+ SSL *ssl;
+ SSL_SESSION *sess;
+ u8 *rnd;
+ int ret = -1;
+ int skip = 0;
+ u8 *tmp_out = NULL;
+ u8 *_out = out;
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char server_random[SSL3_RANDOM_SIZE];
+ unsigned char master_key[64];
+ size_t master_key_len;
+ const char *ver;
+
+ /*
+ * TLS library did not support key generation, so get the needed TLS
+ * session parameters and use an internal implementation of TLS PRF to
+ * derive the key.
+ */
+
+ if (conn == NULL)
+ return -1;
+ ssl = conn->ssl;
+ if (ssl == NULL)
+ return -1;
+ ver = SSL_get_version(ssl);
+ sess = SSL_get_session(ssl);
+ if (!ver || !sess)
+ return -1;
+
+ if (skip_keyblock) {
+ skip = openssl_get_keyblock_size(ssl);
+ if (skip < 0)
+ return -1;
+ tmp_out = os_malloc(skip + out_len);
+ if (!tmp_out)
+ return -1;
+ _out = tmp_out;
+ }
+
+ rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+ if (!rnd) {
+ os_free(tmp_out);
+ return -1;
+ }
+
+ SSL_get_client_random(ssl, client_random, sizeof(client_random));
+ SSL_get_server_random(ssl, server_random, sizeof(server_random));
+ master_key_len = SSL_SESSION_get_master_key(sess, master_key,
+ sizeof(master_key));
+
+ if (server_random_first) {
+ os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+ os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
+ SSL3_RANDOM_SIZE);
+ } else {
+ os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
+ os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
+ SSL3_RANDOM_SIZE);
+ }
+
+ if (os_strcmp(ver, "TLSv1.2") == 0) {
+ tls_prf_sha256(master_key, master_key_len,
+ label, rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len);
+ ret = 0;
+ } else if (tls_prf_sha1_md5(master_key, master_key_len,
+ label, rnd, 2 * SSL3_RANDOM_SIZE,
+ _out, skip + out_len) == 0) {
+ ret = 0;
+ }
+ os_memset(master_key, 0, sizeof(master_key));
+ os_free(rnd);
+ if (ret == 0 && skip_keyblock)
+ os_memcpy(out, _out + skip, out_len);
+ bin_clear_free(tmp_out, skip);
+
+ return ret;
+#endif
#endif /* CONFIG_FIPS */
}
@@ -2792,7 +3190,7 @@
if (conn == NULL)
return -1;
if (server_random_first || skip_keyblock)
- return openssl_tls_prf(tls_ctx, conn, label,
+ return openssl_tls_prf(conn, label,
server_random_first, skip_keyblock,
out, out_len);
ssl = conn->ssl;
@@ -2802,7 +3200,7 @@
return 0;
}
#endif
- return openssl_tls_prf(tls_ctx, conn, label, server_random_first,
+ return openssl_tls_prf(conn, label, server_random_first,
skip_keyblock, out, out_len);
}
@@ -2818,7 +3216,7 @@
* Give TLS handshake data from the server (if available) to OpenSSL
* for processing.
*/
- if (in_data &&
+ if (in_data && wpabuf_len(in_data) > 0 &&
BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
< 0) {
tls_show_errors(MSG_INFO, __func__,
@@ -2930,8 +3328,14 @@
return NULL;
}
- if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
- *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+ if (SSL_is_init_finished(conn->ssl)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Handshake finished - resumed=%d",
+ tls_connection_resumed(conn->ssl_ctx, conn));
+ if (appl_data && in_data)
+ *appl_data = openssl_get_appl_data(conn,
+ wpabuf_len(in_data));
+ }
if (conn->invalid_hb_used) {
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
@@ -3110,6 +3514,21 @@
wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+ if (os_strstr(buf, ":ADH-")) {
+ /*
+ * Need to drop to security level 0 to allow anonymous
+ * cipher suites for EAP-FAST.
+ */
+ SSL_set_security_level(conn->ssl, 0);
+ } else if (SSL_get_security_level(conn->ssl) == 0) {
+ /* Force at least security level 1 */
+ SSL_set_security_level(conn->ssl, 1);
+ }
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif
+
if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
tls_show_errors(MSG_INFO, __func__,
"Cipher suite configuration failed");
@@ -3120,6 +3539,22 @@
}
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+ char *buf, size_t buflen)
+{
+ const char *name;
+ if (conn == NULL || conn->ssl == NULL)
+ return -1;
+
+ name = SSL_get_version(conn->ssl);
+ if (name == NULL)
+ return -1;
+
+ os_strlcpy(buf, name, buflen);
+ return 0;
+}
+
+
int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
@@ -3442,6 +3877,7 @@
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
const struct tls_connection_params *params)
{
+ struct tls_data *data = tls_ctx;
int ret;
unsigned long err;
int can_pkcs11 = 0;
@@ -3483,6 +3919,8 @@
if (can_pkcs11 == 2 && !engine_id)
engine_id = "pkcs11";
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
if (params->flags & TLS_CONN_EAP_FAST) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Use TLSv1_method() for EAP-FAST");
@@ -3492,6 +3930,8 @@
return -1;
}
}
+#endif
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
while ((err = ERR_get_error())) {
wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
@@ -3513,10 +3953,9 @@
return -1;
if (engine_id && ca_cert_id) {
- if (tls_connection_engine_ca_cert(tls_ctx, conn,
- ca_cert_id))
+ if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
- } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+ } else if (tls_connection_ca_cert(data, conn, params->ca_cert,
params->ca_cert_blob,
params->ca_cert_blob_len,
params->ca_path))
@@ -3534,7 +3973,7 @@
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;
- } else if (tls_connection_private_key(tls_ctx, conn,
+ } else if (tls_connection_private_key(data, conn,
params->private_key,
params->private_key_passwd,
params->private_key_blob,
@@ -3558,40 +3997,36 @@
return -1;
}
-#ifdef SSL_OP_NO_TICKET
- if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
- SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
- else
- SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
-#endif /* SSL_OP_NO_TICKET */
+ tls_set_conn_flags(conn->ssl, params->flags);
-#ifdef SSL_OP_NO_TLSv1_1
- if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
- SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
- else
- SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
-#endif /* SSL_OP_NO_TLSv1_1 */
-#ifdef SSL_OP_NO_TLSv1_2
- if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
- SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
- else
- SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
-#endif /* SSL_OP_NO_TLSv1_2 */
-
+#ifdef OPENSSL_IS_BORINGSSL
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ SSL_enable_ocsp_stapling(conn->ssl);
+ }
+#else /* OPENSSL_IS_BORINGSSL */
#ifdef HAVE_OCSP
if (params->flags & TLS_CONN_REQUEST_OCSP) {
- SSL_CTX *ssl_ctx = tls_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
}
+#else /* HAVE_OCSP */
+ if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: No OCSP support included - reject configuration");
+ return -1;
+ }
+ if (params->flags & TLS_CONN_REQUEST_OCSP) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
+ }
#endif /* HAVE_OCSP */
+#endif /* OPENSSL_IS_BORINGSSL */
conn->flags = params->flags;
- tls_get_errors(tls_ctx);
+ tls_get_errors(data);
return 0;
}
@@ -3600,7 +4035,8 @@
int tls_global_set_params(void *tls_ctx,
const struct tls_connection_params *params)
{
- SSL_CTX *ssl_ctx = tls_ctx;
+ struct tls_data *data = tls_ctx;
+ SSL_CTX *ssl_ctx = data->ssl;
unsigned long err;
while ((err = ERR_get_error())) {
@@ -3608,19 +4044,12 @@
__func__, ERR_error_string(err, NULL));
}
- if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
- return -1;
-
- if (tls_global_client_cert(ssl_ctx, params->client_cert))
- return -1;
-
- if (tls_global_private_key(ssl_ctx, params->private_key,
- params->private_key_passwd))
- return -1;
-
- if (tls_global_dh(ssl_ctx, params->dh_file)) {
- wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
- params->dh_file);
+ if (tls_global_ca_cert(data, params->ca_cert) ||
+ tls_global_client_cert(data, params->client_cert) ||
+ tls_global_private_key(data, params->private_key,
+ params->private_key_passwd) ||
+ tls_global_dh(data, params->dh_file)) {
+ wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
return -1;
}
@@ -3656,12 +4085,6 @@
}
-unsigned int tls_capabilities(void *tls_ctx)
-{
- return 0;
-}
-
-
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
/* Pre-shared secred requires a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
@@ -3680,6 +4103,7 @@
struct tls_connection *conn = arg;
int ret;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
@@ -3688,6 +4112,23 @@
conn->session_ticket_len,
s->s3->client_random,
s->s3->server_random, secret);
+#else
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char server_random[SSL3_RANDOM_SIZE];
+
+ if (conn == NULL || conn->session_ticket_cb == NULL)
+ return 0;
+
+ SSL_get_client_random(s, client_random, sizeof(client_random));
+ SSL_get_server_random(s, server_random, sizeof(server_random));
+
+ ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+ conn->session_ticket,
+ conn->session_ticket_len,
+ client_random,
+ server_random, secret);
+#endif
+
os_free(conn->session_ticket);
conn->session_ticket = NULL;
@@ -3757,7 +4198,80 @@
int tls_get_library_version(char *buf, size_t buf_len)
{
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+ OPENSSL_VERSION_TEXT,
+ OpenSSL_version(OPENSSL_VERSION));
+#else
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
OPENSSL_VERSION_TEXT,
SSLeay_version(SSLEAY_VERSION));
+#endif
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+ struct wpabuf *data)
+{
+ SSL_SESSION *sess;
+ struct wpabuf *old;
+
+ if (tls_ex_idx_session < 0)
+ goto fail;
+ sess = SSL_get_session(conn->ssl);
+ if (!sess)
+ goto fail;
+ old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+ if (old) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
+ old);
+ wpabuf_free(old);
+ }
+ if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
+ conn->success_data = 1;
+ return;
+
+fail:
+ wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
+ wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Success data accepted for resumed session");
+ conn->success_data = 1;
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+ SSL_SESSION *sess;
+
+ if (tls_ex_idx_session < 0 ||
+ !(sess = SSL_get_session(conn->ssl)))
+ return NULL;
+ return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+ SSL_SESSION *sess;
+
+ sess = SSL_get_session(conn->ssl);
+ if (!sess)
+ return;
+
+ if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Session was not cached");
+ else
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Removed cached session to disable session resumption");
}
diff --git a/src/crypto/tls_openssl.h b/src/crypto/tls_openssl.h
new file mode 100644
index 0000000..2a62d5c
--- /dev/null
+++ b/src/crypto/tls_openssl.h
@@ -0,0 +1,19 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_OPENSSL_H
+#define TLS_OPENSSL_H
+
+enum ocsp_result {
+ OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
+};
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+ X509 *issuer, X509 *issuer_issuer);
+
+#endif /* TLS_OPENSSL_H */
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
new file mode 100644
index 0000000..37c87f4
--- /dev/null
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -0,0 +1,843 @@
+/*
+ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#endif /* OPENSSL_IS_BORINGSSL */
+
+#include "common.h"
+#include "tls_openssl.h"
+
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+ unsigned long err;
+
+ wpa_printf(level, "OpenSSL: %s - %s %s",
+ func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+ while ((err = ERR_get_error())) {
+ wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+ ERR_error_string(err, NULL));
+ }
+}
+
+
+/*
+ * CertID ::= SEQUENCE {
+ * hashAlgorithm AlgorithmIdentifier,
+ * issuerNameHash OCTET STRING, -- Hash of Issuer's DN
+ * issuerKeyHash OCTET STRING, -- Hash of Issuer's public key
+ * serialNumber CertificateSerialNumber }
+ */
+typedef struct {
+ X509_ALGOR *hashAlgorithm;
+ ASN1_OCTET_STRING *issuerNameHash;
+ ASN1_OCTET_STRING *issuerKeyHash;
+ ASN1_INTEGER *serialNumber;
+} CertID;
+
+/*
+ * ResponseBytes ::= SEQUENCE {
+ * responseType OBJECT IDENTIFIER,
+ * response OCTET STRING }
+ */
+typedef struct {
+ ASN1_OBJECT *responseType;
+ ASN1_OCTET_STRING *response;
+} ResponseBytes;
+
+/*
+ * OCSPResponse ::= SEQUENCE {
+ * responseStatus OCSPResponseStatus,
+ * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+typedef struct {
+ ASN1_ENUMERATED *responseStatus;
+ ResponseBytes *responseBytes;
+} OCSPResponse;
+
+ASN1_SEQUENCE(ResponseBytes) = {
+ ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
+ ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(ResponseBytes);
+
+ASN1_SEQUENCE(OCSPResponse) = {
+ ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
+ ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
+} ASN1_SEQUENCE_END(OCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
+
+/*
+ * ResponderID ::= CHOICE {
+ * byName [1] Name,
+ * byKey [2] KeyHash }
+ */
+typedef struct {
+ int type;
+ union {
+ X509_NAME *byName;
+ ASN1_OCTET_STRING *byKey;
+ } value;
+} ResponderID;
+
+/*
+ * RevokedInfo ::= SEQUENCE {
+ * revocationTime GeneralizedTime,
+ * revocationReason [0] EXPLICIT CRLReason OPTIONAL }
+ */
+typedef struct {
+ ASN1_GENERALIZEDTIME *revocationTime;
+ ASN1_ENUMERATED *revocationReason;
+} RevokedInfo;
+
+/*
+ * CertStatus ::= CHOICE {
+ * good [0] IMPLICIT NULL,
+ * revoked [1] IMPLICIT RevokedInfo,
+ * unknown [2] IMPLICIT UnknownInfo }
+ */
+typedef struct {
+ int type;
+ union {
+ ASN1_NULL *good;
+ RevokedInfo *revoked;
+ ASN1_NULL *unknown;
+ } value;
+} CertStatus;
+
+/*
+ * SingleResponse ::= SEQUENCE {
+ * certID CertID,
+ * certStatus CertStatus,
+ * thisUpdate GeneralizedTime,
+ * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
+ * singleExtensions [1] EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+ CertID *certID;
+ CertStatus *certStatus;
+ ASN1_GENERALIZEDTIME *thisUpdate;
+ ASN1_GENERALIZEDTIME *nextUpdate;
+ STACK_OF(X509_EXTENSION) *singleExtensions;
+} SingleResponse;
+
+/*
+ * ResponseData ::= SEQUENCE {
+ * version [0] EXPLICIT Version DEFAULT v1,
+ * responderID ResponderID,
+ * producedAt GeneralizedTime,
+ * responses SEQUENCE OF SingleResponse,
+ * responseExtensions [1] EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+ ASN1_INTEGER *version;
+ ResponderID *responderID;
+ ASN1_GENERALIZEDTIME *producedAt;
+ STACK_OF(SingleResponse) *responses;
+ STACK_OF(X509_EXTENSION) *responseExtensions;
+} ResponseData;
+
+/*
+ * BasicOCSPResponse ::= SEQUENCE {
+ * tbsResponseData ResponseData,
+ * signatureAlgorithm AlgorithmIdentifier,
+ * signature BIT STRING,
+ * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+typedef struct {
+ ResponseData *tbsResponseData;
+ X509_ALGOR *signatureAlgorithm;
+ ASN1_BIT_STRING *signature;
+ STACK_OF(X509) *certs;
+} BasicOCSPResponse;
+
+ASN1_SEQUENCE(CertID) = {
+ ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
+ ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(CertID);
+
+ASN1_CHOICE(ResponderID) = {
+ ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
+ ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
+} ASN1_CHOICE_END(ResponderID);
+
+ASN1_SEQUENCE(RevokedInfo) = {
+ ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
+ ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
+} ASN1_SEQUENCE_END(RevokedInfo);
+
+ASN1_CHOICE(CertStatus) = {
+ ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
+ ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
+ ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
+} ASN1_CHOICE_END(CertStatus);
+
+ASN1_SEQUENCE(SingleResponse) = {
+ ASN1_SIMPLE(SingleResponse, certID, CertID),
+ ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
+ ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
+ ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
+ ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
+ X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(SingleResponse);
+
+ASN1_SEQUENCE(ResponseData) = {
+ ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
+ ASN1_SIMPLE(ResponseData, responderID, ResponderID),
+ ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
+ ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
+ ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
+ X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(ResponseData);
+
+ASN1_SEQUENCE(BasicOCSPResponse) = {
+ ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
+ ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
+ ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
+ ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
+} ASN1_SEQUENCE_END(BasicOCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
+
+#define sk_SingleResponse_num(sk) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
+
+#define sk_SingleResponse_value(sk, i) \
+ ((SingleResponse *) \
+ sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
+
+
+static char * mem_bio_to_str(BIO *out)
+{
+ char *txt;
+ size_t rlen;
+ int res;
+
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (!txt) {
+ BIO_free(out);
+ return NULL;
+ }
+
+ res = BIO_read(out, txt, rlen);
+ BIO_free(out);
+ if (res < 0) {
+ os_free(txt);
+ return NULL;
+ }
+
+ txt[res] = '\0';
+ return txt;
+}
+
+
+static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
+{
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ if (!ASN1_GENERALIZEDTIME_print(out, t)) {
+ BIO_free(out);
+ return NULL;
+ }
+
+ return mem_bio_to_str(out);
+}
+
+
+static char * responderid_str(ResponderID *rid)
+{
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ switch (rid->type) {
+ case 0:
+ X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
+ break;
+ case 1:
+ i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
+ break;
+ default:
+ BIO_free(out);
+ return NULL;
+ }
+
+ return mem_bio_to_str(out);
+}
+
+
+static char * octet_string_str(ASN1_OCTET_STRING *o)
+{
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
+ return mem_bio_to_str(out);
+}
+
+
+static char * integer_str(ASN1_INTEGER *i)
+{
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ i2a_ASN1_INTEGER(out, i);
+ return mem_bio_to_str(out);
+}
+
+
+static char * algor_str(X509_ALGOR *alg)
+{
+ BIO *out;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ i2a_ASN1_OBJECT(out, alg->algorithm);
+ return mem_bio_to_str(out);
+}
+
+
+static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
+{
+ BIO *out;
+
+ if (!ext)
+ return NULL;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return NULL;
+
+ if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
+ BIO_free(out);
+ return NULL;
+ }
+ return mem_bio_to_str(out);
+}
+
+
+static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
+ ASN1_GENERALIZEDTIME *nextupd)
+{
+ time_t now, tmp;
+
+ if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Invalid OCSP response thisUpdate");
+ return 0;
+ }
+
+ time(&now);
+ tmp = now + 5 * 60; /* allow five minute clock difference */
+ if (X509_cmp_time(thisupd, &tmp) > 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
+ return 0;
+ }
+
+ if (!nextupd)
+ return 1; /* OK - no limit on response age */
+
+ if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Invalid OCSP response nextUpdate");
+ return 0;
+ }
+
+ tmp = now - 5 * 60; /* allow five minute clock difference */
+ if (X509_cmp_time(nextupd, &tmp) < 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
+ return 0;
+ }
+
+ if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP response nextUpdate before thisUpdate");
+ return 0;
+ }
+
+ /* Both thisUpdate and nextUpdate are valid */
+ return -1;
+}
+
+
+static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
+{
+ X509_NAME *iname;
+ ASN1_BIT_STRING *ikey;
+ const EVP_MD *dgst;
+ unsigned int len;
+ unsigned char md[EVP_MAX_MD_SIZE];
+ ASN1_OCTET_STRING *hash;
+ char *txt;
+
+ dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
+ if (!dgst) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not find matching hash algorithm for OCSP");
+ return -1;
+ }
+
+ iname = X509_get_issuer_name(cert);
+ if (!X509_NAME_digest(iname, dgst, md, &len))
+ return -1;
+ hash = ASN1_OCTET_STRING_new();
+ if (!hash)
+ return -1;
+ if (!ASN1_OCTET_STRING_set(hash, md, len)) {
+ ASN1_OCTET_STRING_free(hash);
+ return -1;
+ }
+
+ txt = octet_string_str(hash);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
+ txt);
+ os_free(txt);
+ }
+
+ if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
+ ASN1_OCTET_STRING_free(hash);
+ return -1;
+ }
+
+ ikey = X509_get0_pubkey_bitstr(issuer);
+ if (!EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
+ !ASN1_OCTET_STRING_set(hash, md, len)) {
+ ASN1_OCTET_STRING_free(hash);
+ return -1;
+ }
+
+ txt = octet_string_str(hash);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
+ txt);
+ os_free(txt);
+ }
+
+ if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
+ ASN1_OCTET_STRING_free(hash);
+ return -1;
+ }
+
+ ASN1_OCTET_STRING_free(hash);
+ return 0;
+}
+
+
+static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
+{
+ unsigned int i;
+ unsigned char hash[SHA_DIGEST_LENGTH];
+
+ if (rid->type == 0) {
+ /* byName */
+ return X509_find_by_subject(certs, rid->value.byName);
+ }
+
+ /* byKey */
+ if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
+ return NULL;
+ for (i = 0; i < sk_X509_num(certs); i++) {
+ X509 *x = sk_X509_value(certs, i);
+
+ X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
+ if (os_memcmp(rid->value.byKey->data, hash,
+ SHA_DIGEST_LENGTH) == 0)
+ return x;
+ }
+
+ return NULL;
+}
+
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+ X509 *issuer, X509 *issuer_issuer)
+{
+ const uint8_t *resp_data;
+ size_t resp_len;
+ OCSPResponse *resp;
+ int status;
+ ResponseBytes *bytes;
+ const u8 *basic_data;
+ size_t basic_len;
+ BasicOCSPResponse *basic;
+ ResponseData *rd;
+ char *txt;
+ int i, num;
+ unsigned int j, num_resp;
+ SingleResponse *matching_resp = NULL, *cmp_sresp;
+ enum ocsp_result result = OCSP_INVALID;
+ X509_STORE *store;
+ STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
+ X509_STORE_CTX ctx;
+ X509 *signer, *tmp_cert;
+ int signer_trusted = 0;
+ EVP_PKEY *skey;
+ int ret;
+ char buf[256];
+
+ txt = integer_str(X509_get_serialNumber(cert));
+ if (txt) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
+ os_free(txt);
+ }
+
+ SSL_get0_ocsp_response(ssl, &resp_data, &resp_len);
+ if (resp_data == NULL || resp_len == 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+ return OCSP_NO_RESPONSE;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
+
+ resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
+ if (!resp) {
+ wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
+ return OCSP_INVALID;
+ }
+
+ status = ASN1_ENUMERATED_get(resp->responseStatus);
+ if (status != 0) {
+ wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
+ status);
+ return OCSP_INVALID;
+ }
+
+ bytes = resp->responseBytes;
+
+ if (!bytes ||
+ OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Could not find BasicOCSPResponse");
+ return OCSP_INVALID;
+ }
+
+ basic_data = ASN1_STRING_data(bytes->response);
+ basic_len = ASN1_STRING_length(bytes->response);
+ wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
+ basic_data, basic_len);
+
+ basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
+ if (!basic) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Could not parse BasicOCSPResponse");
+ OCSPResponse_free(resp);
+ return OCSP_INVALID;
+ }
+
+ rd = basic->tbsResponseData;
+
+ if (basic->certs) {
+ untrusted = sk_X509_dup(basic->certs);
+
+ num = sk_X509_num(basic->certs);
+ for (i = 0; i < num; i++) {
+ X509 *extra_cert;
+
+ extra_cert = sk_X509_value(basic->certs, i);
+ X509_NAME_oneline(X509_get_subject_name(extra_cert),
+ buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: BasicOCSPResponse cert %s", buf);
+
+ if (!sk_X509_push(untrusted, extra_cert)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not add certificate to the untrusted stack");
+ }
+ }
+ }
+
+ store = SSL_CTX_get_cert_store(ssl_ctx);
+ if (issuer) {
+ if (X509_STORE_add_cert(store, issuer) != 1) {
+ tls_show_errors(MSG_INFO, __func__,
+ "OpenSSL: Could not add issuer to certificate store");
+ }
+ certs = sk_X509_new_null();
+ if (certs) {
+ tmp_cert = X509_dup(issuer);
+ if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
+ tls_show_errors(
+ MSG_INFO, __func__,
+ "OpenSSL: Could not add issuer to OCSP responder trust store");
+ X509_free(tmp_cert);
+ sk_X509_free(certs);
+ certs = NULL;
+ }
+ if (certs && issuer_issuer) {
+ tmp_cert = X509_dup(issuer_issuer);
+ if (tmp_cert &&
+ !sk_X509_push(certs, tmp_cert)) {
+ tls_show_errors(
+ MSG_INFO, __func__,
+ "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+ X509_free(tmp_cert);
+ }
+ }
+ }
+ }
+
+ signer = ocsp_find_signer(certs, rd->responderID);
+ if (!signer)
+ signer = ocsp_find_signer(untrusted, rd->responderID);
+ else
+ signer_trusted = 1;
+ if (!signer) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not find OCSP signer certificate");
+ goto fail;
+ }
+
+ skey = X509_get_pubkey(signer);
+ if (!skey) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not get OCSP signer public key");
+ goto fail;
+ }
+ if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
+ basic->signatureAlgorithm, basic->signature,
+ basic->tbsResponseData, skey) <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: BasicOCSPResponse signature is invalid");
+ goto fail;
+ }
+
+ X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
+ buf);
+
+ if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted))
+ goto fail;
+ X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
+ ret = X509_verify_cert(&ctx);
+ chain = X509_STORE_CTX_get1_chain(&ctx);
+ X509_STORE_CTX_cleanup(&ctx);
+ if (ret <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not validate OCSP signer certificate");
+ goto fail;
+ }
+
+ if (!chain || sk_X509_num(chain) <= 0) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
+ goto fail;
+ }
+
+ if (!signer_trusted) {
+ X509_check_purpose(signer, -1, 0);
+ if ((signer->ex_flags & EXFLAG_XKUSAGE) &&
+ (signer->ex_xkusage & XKU_OCSP_SIGN)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP signer certificate delegation OK");
+ } else {
+ tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
+ if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
+ X509_TRUST_TRUSTED) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP signer certificate not trusted");
+ result = OCSP_NO_RESPONSE;
+ goto fail;
+ }
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
+ ASN1_INTEGER_get(rd->version));
+
+ txt = responderid_str(rd->responderID);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
+ txt);
+ os_free(txt);
+ }
+
+ txt = generalizedtime_str(rd->producedAt);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
+ txt);
+ os_free(txt);
+ }
+
+ num_resp = sk_SingleResponse_num(rd->responses);
+ if (num_resp == 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
+ result = OCSP_NO_RESPONSE;
+ goto fail;
+ }
+ cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
+ for (j = 0; j < num_resp; j++) {
+ SingleResponse *sresp;
+ CertID *cid1, *cid2;
+
+ sresp = sk_SingleResponse_value(rd->responses, j);
+ wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
+ j + 1, num_resp);
+
+ txt = algor_str(sresp->certID->hashAlgorithm);
+ if (txt) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: certID hashAlgorithm: %s", txt);
+ os_free(txt);
+ }
+
+ txt = octet_string_str(sresp->certID->issuerNameHash);
+ if (txt) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: certID issuerNameHash: %s", txt);
+ os_free(txt);
+ }
+
+ txt = octet_string_str(sresp->certID->issuerKeyHash);
+ if (txt) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: certID issuerKeyHash: %s", txt);
+ os_free(txt);
+ }
+
+ txt = integer_str(sresp->certID->serialNumber);
+ if (txt) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: certID serialNumber: %s", txt);
+ os_free(txt);
+ }
+
+ switch (sresp->certStatus->type) {
+ case 0:
+ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
+ break;
+ case 1:
+ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
+ break;
+ }
+
+ txt = generalizedtime_str(sresp->thisUpdate);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
+ os_free(txt);
+ }
+
+ if (sresp->nextUpdate) {
+ txt = generalizedtime_str(sresp->nextUpdate);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
+ txt);
+ os_free(txt);
+ }
+ }
+
+ txt = extensions_str("singleExtensions",
+ sresp->singleExtensions);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+ os_free(txt);
+ }
+
+ cid1 = cmp_sresp->certID;
+ cid2 = sresp->certID;
+ if (j > 0 &&
+ (OBJ_cmp(cid1->hashAlgorithm->algorithm,
+ cid2->hashAlgorithm->algorithm) != 0 ||
+ ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
+ cid2->issuerNameHash) != 0 ||
+ ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
+ cid2->issuerKeyHash) != 0)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
+ goto fail;
+ }
+
+ if (!matching_resp && issuer &&
+ ASN1_INTEGER_cmp(sresp->certID->serialNumber,
+ X509_get_serialNumber(cert)) == 0 &&
+ issuer_match(cert, issuer, sresp->certID) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: This response matches peer certificate");
+ matching_resp = sresp;
+ }
+ }
+
+ txt = extensions_str("responseExtensions", rd->responseExtensions);
+ if (txt) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+ os_free(txt);
+ }
+
+ if (!matching_resp) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not find OCSP response that matches the peer certificate");
+ result = OCSP_NO_RESPONSE;
+ goto fail;
+ }
+
+ if (!ocsp_resp_valid(matching_resp->thisUpdate,
+ matching_resp->nextUpdate)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP response not valid at this time");
+ goto fail;
+ }
+
+ if (matching_resp->certStatus->type == 1) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
+ result = OCSP_REVOKED;
+ goto fail;
+ }
+
+ if (matching_resp->certStatus->type != 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: OCSP response did not indicate good status");
+ result = OCSP_NO_RESPONSE;
+ goto fail;
+ }
+
+ /* OCSP response indicated the certificate is good. */
+ result = OCSP_GOOD;
+fail:
+ sk_X509_pop_free(chain, X509_free);
+ sk_X509_free(untrusted);
+ sk_X509_pop_free(certs, X509_free);
+ BasicOCSPResponse_free(basic);
+ OCSPResponse_free(resp);
+
+ return result;
+}
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7fe736b..6fd72c5 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -45,6 +45,15 @@
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+#define HOSTAPD_CHAN_VHT_10_150 0x00100000
+#define HOSTAPD_CHAN_VHT_30_130 0x00200000
+#define HOSTAPD_CHAN_VHT_50_110 0x00400000
+#define HOSTAPD_CHAN_VHT_70_90 0x00800000
+#define HOSTAPD_CHAN_VHT_90_70 0x01000000
+#define HOSTAPD_CHAN_VHT_110_50 0x02000000
+#define HOSTAPD_CHAN_VHT_130_30 0x04000000
+#define HOSTAPD_CHAN_VHT_150_10 0x08000000
+
/**
* enum reg_change_initiator - Regulatory change initiator
*/
@@ -407,6 +416,28 @@
*/
const u8 *mac_addr_mask;
+ /**
+ * sched_scan_plans - Scan plans for scheduled scan
+ *
+ * Each scan plan consists of the number of iterations to scan and the
+ * interval between scans. When a scan plan finishes (i.e., it was run
+ * for the specified number of iterations), the next scan plan is
+ * executed. The scan plans are executed in the order they appear in
+ * the array (lower index first). The last scan plan will run infinitely
+ * (until requested to stop), thus must not specify the number of
+ * iterations. All other scan plans must specify the number of
+ * iterations.
+ */
+ struct sched_scan_plan {
+ u32 interval; /* In seconds */
+ u32 iterations; /* Zero to run infinitely */
+ } *sched_scan_plans;
+
+ /**
+ * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+ */
+ unsigned int sched_scan_plans_num;
+
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -1214,6 +1245,8 @@
#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
/** Driver supports automatic band selection */
#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
u64 flags;
#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
@@ -1231,6 +1264,15 @@
/** Maximum number of supported active probe SSIDs for sched_scan */
int max_sched_scan_ssids;
+ /** Maximum number of supported scan plans for scheduled scan */
+ unsigned int max_sched_scan_plans;
+
+ /** Maximum interval in a scan plan. In seconds */
+ u32 max_sched_scan_plan_interval;
+
+ /** Maximum number of iterations in a single scan plan */
+ u32 max_sched_scan_plan_iterations;
+
/** Whether sched_scan (offloaded scanning) is supported */
int sched_scan_supported;
@@ -1297,6 +1339,16 @@
*/
#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
u32 rrm_flags;
+
+ /* Driver concurrency capabilities */
+ unsigned int conc_capab;
+ /* Maximum number of concurrent channels on 2.4 GHz */
+ unsigned int max_conc_chan_2_4;
+ /* Maximum number of concurrent channels on 5 GHz */
+ unsigned int max_conc_chan_5_0;
+
+ /* Maximum number of supported CSA counters */
+ u16 max_csa_counters;
};
@@ -1397,6 +1449,16 @@
* WPA_IF_MESH - Mesh interface
*/
WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
};
struct wpa_init_params {
@@ -1537,8 +1599,8 @@
struct beacon_data beacon_csa;
struct beacon_data beacon_after;
- u16 counter_offset_beacon;
- u16 counter_offset_presp;
+ u16 counter_offset_beacon[2];
+ u16 counter_offset_presp[2];
};
/* TDLS peer capabilities for send_tdls_mgmt() */
@@ -1602,6 +1664,7 @@
/* ACS channel list info */
unsigned int ch_list_len;
const u8 *ch_list;
+ const int *freq_list;
};
@@ -1942,10 +2005,13 @@
* @noack: Do not wait for this frame to be acked (disable retries)
* @freq: Frequency (in MHz) to send the frame on, or 0 to let the
* driver decide
+ * @csa_offs: Array of CSA offsets or %NULL
+ * @csa_offs_len: Number of elements in csa_offs
* Returns: 0 on success, -1 on failure
*/
int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
- int noack, unsigned int freq);
+ int noack, unsigned int freq, const u16 *csa_offs,
+ size_t csa_offs_len);
/**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -2349,7 +2415,8 @@
* Returns: 0 on success, -1 on failure
*/
int (*sta_set_flags)(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and);
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and);
/**
* set_tx_queue_params - Set TX queue parameters
@@ -2380,12 +2447,13 @@
* change interface address)
* @bridge: Bridge interface to use or %NULL if no bridge configured
* @use_existing: Whether to allow existing interface to be used
+ * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
* Returns: 0 on success, -1 on failure
*/
int (*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);
+ const char *bridge, int use_existing, int setup_ap);
/**
* if_remove - Remove a virtual interface
@@ -2967,7 +3035,6 @@
* sched_scan - Request the driver to initiate scheduled scan
* @priv: Private driver interface data
* @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
* Returns: 0 on success, -1 on failure
*
* This operation should be used for scheduled scan offload to
@@ -2978,8 +3045,7 @@
* and if not provided or if it returns -1, we fall back to
* normal host-scheduled scans.
*/
- int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
- u32 interval);
+ int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
/**
* stop_sched_scan - Request the driver to stop a scheduled scan
@@ -3394,6 +3460,39 @@
* Returns 0 on success, -1 on failure
*/
int (*set_band)(void *priv, enum set_band band);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @priv: Private driver interface data
+ * @if_type: Interface type
+ * @num: Number of channels
+ * @freq_list: Preferred channel frequency list encoded in MHz values
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type.
+ */
+ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+ unsigned int *num, unsigned int *freq_list);
+
+ /**
+ * set_prob_oper_freq - Indicate probable P2P operating channel
+ * @priv: Private driver interface data
+ * @freq: Channel frequency in MHz
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to inform the driver of the operating
+ * frequency that an ongoing P2P group formation is likely to come up
+ * on. Local device is assuming P2P Client role.
+ */
+ int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+
+ /**
+ * abort_scan - Request the driver to abort an ongoing scan
+ * @priv: Private driver interface data
+ * Returns 0 on success, -1 on failure
+ */
+ int (*abort_scan)(void *priv);
};
@@ -4053,6 +4152,12 @@
* ptk_kek_len - The length of ptk_kek
*/
size_t ptk_kek_len;
+
+ /**
+ * subnet_status - The subnet status:
+ * 0 = unknown, 1 = unchanged, 2 = changed
+ */
+ u8 subnet_status;
} assoc_info;
/**
@@ -4336,6 +4441,9 @@
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID)
* @num_ssids: Number of entries in ssids array
+ * @external_scan: Whether the scan info is for an external scan
+ * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+ * 0 if the source of the scan event is a vendor scan
*/
struct scan_info {
int aborted;
@@ -4343,6 +4451,8 @@
size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids;
+ int external_scan;
+ int nl_scan_event;
} scan_info;
/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b721c32..1afeeb2 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -55,6 +55,10 @@
#include "netlink.h"
#include "linux_ioctl.h"
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#define ATHEROS_USE_RAW_RECEIVE
+#endif
+
struct atheros_driver_data {
struct hostapd_data *hapd; /* back pointer */
@@ -430,7 +434,8 @@
static int
atheros_sta_set_flags(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
{
/* For now, only support setting Authorized flag */
if (flags_or & WPA_STA_AUTHORIZED)
@@ -823,7 +828,7 @@
return 0;
}
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+#ifdef ATHEROS_USE_RAW_RECEIVE
static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
@@ -911,7 +916,7 @@
break;
}
}
-#endif
+#endif /* ATHEROS_USE_RAW_RECEIVE */
static int atheros_receive_pkt(struct atheros_driver_data *drv)
{
@@ -923,11 +928,11 @@
#ifdef CONFIG_WPS
filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
IEEE80211_FILTER_TYPE_AUTH |
IEEE80211_FILTER_TYPE_ACTION);
-#endif
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
#ifdef CONFIG_WNM
filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
#endif /* CONFIG_WNM */
@@ -1026,7 +1031,7 @@
#define atheros_set_ap_wps_ie NULL
#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
static int
atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
u16 status_code, const u8 *ie, size_t len)
@@ -1102,7 +1107,7 @@
}
return ret;
}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
static void
atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1177,6 +1182,7 @@
atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
char *custom, char *end)
{
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
@@ -1232,9 +1238,6 @@
* so all are enabled for WPS... ugh.
*/
wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
-#endif /* CONFIG_WPS */
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20)
-#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
/*
* Atheros driver uses a hack to pass Probe Request frames as a
@@ -1243,47 +1246,53 @@
* Format: "Manage.prob_req <frame len>" | zero padding | frame
*/
int len = atoi(custom + 16);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
"length %d", len);
return;
}
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
/* Format: "Manage.assoc_req <frame len>" | zero padding |
* frame */
int len = atoi(custom + 17);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.assoc_req event length %d",
+ len);
return;
}
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (strncmp(custom, "Manage.action ", 14) == 0) {
- /* Format: "Manage.assoc_req <frame len>" | zero padding |
- * frame */
- int len = atoi(custom + 14);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
- return;
- }
- atheros_raw_receive(drv, NULL,
- (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
- } else if (strncmp(custom, "Manage.auth ", 12) == 0) {
- /* Format: "Manage.auth <frame len>" | zero padding | frame
- */
+ } else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+ /* Format: "Manage.auth <frame len>" | zero padding | frame */
int len = atoi(custom + 12);
- if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
- wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
- "assoc_req/auth event length %d", len);
+ if (len < 0 ||
+ MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.auth event length %d", len);
return;
}
atheros_raw_receive(drv, NULL,
(u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_WPS or CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#ifdef ATHEROS_USE_RAW_RECEIVE
+ } else if (strncmp(custom, "Manage.action ", 14) == 0) {
+ /* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+ */
+ int len = atoi(custom + 14);
+ if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+ wpa_printf(MSG_DEBUG,
+ "Invalid Manage.action event length %d",
+ len);
+ return;
+ }
+ atheros_raw_receive(drv, NULL,
+ (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* ATHEROS_USE_RAW_RECEIVE */
}
}
@@ -1375,7 +1384,7 @@
static void
atheros_wireless_event_wireless(struct atheros_driver_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -1383,13 +1392,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
/* 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)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -1421,7 +1430,7 @@
* just like IWEVCUSTOM.
*/
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end)
+ if (iwe->u.data.length > end - custom)
return;
buf = malloc(iwe->u.data.length + 1);
if (buf == NULL)
@@ -1695,10 +1704,10 @@
atheros_reset_appfilter(drv);
if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+ atheros_set_opt_ie(priv, NULL, 0);
wpabuf_free(drv->wpa_ie);
wpabuf_free(drv->wps_beacon_ie);
wpabuf_free(drv->wps_probe_resp_ie);
- atheros_set_opt_ie(priv, NULL, 0);
}
netlink_deinit(drv->netlink);
(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
@@ -1836,10 +1845,11 @@
}
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
- int noack, unsigned int freq)
+ int noack, unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct atheros_driver_data *drv = priv;
u8 buf[1510];
@@ -1861,8 +1871,11 @@
return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
sizeof(struct ieee80211req_mgmtbuf) + data_len);
}
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+
static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
size_t tspec_ielen)
{
@@ -2142,10 +2155,12 @@
.set_ap_wps_ie = atheros_set_ap_wps_ie,
.set_authmode = atheros_set_authmode,
.set_ap = atheros_set_ap,
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
.sta_assoc = atheros_sta_assoc,
.sta_auth = atheros_sta_auth,
.send_mlme = atheros_send_mgmt,
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
.add_tspec = atheros_add_tspec,
.add_sta_node = atheros_add_sta_node,
#endif /* CONFIG_IEEE80211R */
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 9ac87b4..bab1f03 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -894,7 +894,8 @@
static int
bsd_set_sta_authorized(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
{
int authorized = -1;
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 8835005..517a3bb 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -140,7 +140,7 @@
static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
{
struct ieee80211_hdr *hdr;
- u16 fc, extra_len, type, stype;
+ u16 fc, type, stype;
size_t data_len = len;
int ver;
union wpa_event_data event;
@@ -165,19 +165,10 @@
ver = fc & WLAN_FC_PVER;
- /* protocol version 3 is reserved for indicating extra data after the
- * payload, version 2 for indicating ACKed frame (TX callbacks), and
- * version 1 for indicating failed frame (no ACK, TX callbacks) */
- if (ver == 3) {
- u8 *pos = buf + len - 2;
- extra_len = WPA_GET_LE16(pos);
- printf("extra data in frame (elen=%d)\n", extra_len);
- if ((size_t) extra_len + 2 > len) {
- printf(" extra data overflow\n");
- return;
- }
- len -= extra_len + 2;
- } else if (ver == 1 || ver == 2) {
+ /* protocol version 2 is reserved for indicating ACKed frame (TX
+ * callbacks), and version 1 for indicating failed frame (no ACK, TX
+ * callbacks) */
+ if (ver == 1 || ver == 2) {
handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
return;
} else if (ver != 0) {
@@ -267,7 +258,8 @@
static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
- unsigned int freq)
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct hostap_driver_data *drv = priv;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -316,7 +308,7 @@
pos += 2;
memcpy(pos, data, data_len);
- res = hostap_send_mlme(drv, (u8 *) hdr, len, 0);
+ res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
"failed: %d (%s)",
@@ -329,7 +321,8 @@
static int hostap_sta_set_flags(void *priv, const u8 *addr,
- int total_flags, int flags_or, int flags_and)
+ unsigned int total_flags, unsigned int flags_or,
+ unsigned int flags_and)
{
struct hostap_driver_data *drv = priv;
struct prism2_hostapd_param param;
@@ -821,7 +814,7 @@
static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -829,13 +822,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
iwe->cmd, iwe->len);
- if (iwe->len <= IW_EV_LCP_LEN)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -854,7 +847,7 @@
switch (iwe->cmd) {
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end)
+ if (iwe->u.data.length > end - custom)
return;
buf = malloc(iwe->u.data.length + 1);
if (buf == NULL)
@@ -1053,7 +1046,7 @@
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.deauth.reason_code = host_to_le16(reason);
return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.deauth), 0);
+ sizeof(mgmt.u.deauth), 0, 0, NULL, 0);
}
@@ -1091,7 +1084,7 @@
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.disassoc.reason_code = host_to_le16(reason);
return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
- sizeof(mgmt.u.disassoc), 0);
+ sizeof(mgmt.u.disassoc), 0, 0, NULL, 0);
}
@@ -1169,7 +1162,7 @@
os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
- hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0);
+ hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0);
}
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 669f1b8..fd32134 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -785,8 +785,8 @@
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2a05a3b..0fd836b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -176,7 +176,8 @@
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);
+ int no_cck, int no_ack, int offchanok,
+ const u16 *csa_offs, size_t csa_offs_len);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
@@ -1536,6 +1537,10 @@
"after rfkill unblock");
return;
}
+
+ if (is_p2p_net_interface(drv->nlmode))
+ nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
/* rtnetlink ifup handler will report interface as enabled */
}
@@ -1878,6 +1883,11 @@
ret = -1;
}
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+ /* FST Action frames */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+ ret = -1;
+#endif /* CONFIG_FST */
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
@@ -2221,6 +2231,11 @@
"interface '%s' UP", bss->ifname);
return ret;
}
+
+ if (is_p2p_net_interface(nlmode))
+ nl80211_disable_11b_rates(bss->drv,
+ bss->drv->ifindex, 1);
+
if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
return ret;
} else {
@@ -2463,6 +2478,7 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
const u8 *key, size_t key_len)
{
@@ -2490,6 +2506,7 @@
return ret;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
@@ -2520,6 +2537,7 @@
}
#endif /* CONFIG_TDLS */
+#ifdef CONFIG_DRIVER_NL80211_QCA
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",
@@ -2527,6 +2545,7 @@
ret = issue_key_mgmt_set_key(drv, key, key_len);
return ret;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
if (alg == WPA_ALG_NONE) {
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
@@ -3084,7 +3103,9 @@
const void *data, size_t len,
int encrypt, int noack,
unsigned int freq, int no_cck,
- int offchanok, unsigned int wait_time)
+ int offchanok, unsigned int wait_time,
+ const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
u64 cookie;
@@ -3110,7 +3131,8 @@
wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
- &cookie, no_cck, noack, offchanok);
+ &cookie, no_cck, noack, offchanok,
+ csa_offs, csa_offs_len);
if (res == 0 && !noack) {
const struct ieee80211_mgmt *mgmt;
u16 fc;
@@ -3136,7 +3158,9 @@
size_t data_len, int noack,
unsigned int freq, int no_cck,
int offchanok,
- unsigned int wait_time)
+ unsigned int wait_time,
+ const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
@@ -3166,7 +3190,7 @@
}
return nl80211_send_frame_cmd(bss, freq, 0,
data, data_len, NULL, 1, noack,
- 1);
+ 1, csa_offs, csa_offs_len);
}
if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
@@ -3180,7 +3204,8 @@
wait_time,
data, data_len,
&drv->send_action_cookie,
- no_cck, noack, offchanok);
+ no_cck, noack, offchanok,
+ csa_offs, csa_offs_len);
}
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
@@ -3200,7 +3225,8 @@
wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame");
return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
noack, freq, no_cck, offchanok,
- wait_time);
+ wait_time, csa_offs,
+ csa_offs_len);
}
@@ -3253,7 +3279,7 @@
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- struct nlattr *acl;
+ struct nl_msg *acl;
unsigned int i;
int ret;
@@ -3266,23 +3292,26 @@
wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
params->acl_policy ? "Accept" : "Deny", params->num_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);
+ acl = nlmsg_alloc();
+ if (!acl)
return -ENOMEM;
- }
-
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);
+ if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+ nlmsg_free(acl);
return -ENOMEM;
}
}
- nla_nest_end(msg, 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) ||
+ nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
+ nlmsg_free(msg);
+ nlmsg_free(acl);
+ return -ENOMEM;
+ }
+ nlmsg_free(acl);
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
@@ -4236,7 +4265,7 @@
memcpy(pos, data, data_len);
res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, NULL, 0);
if (res < 0) {
wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
"failed: %d (%s)",
@@ -4249,8 +4278,9 @@
static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
- int total_flags,
- int flags_or, int flags_and)
+ unsigned int total_flags,
+ unsigned int flags_or,
+ unsigned int flags_and)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
@@ -4671,6 +4701,7 @@
int ret;
int algs;
+#ifdef CONFIG_DRIVER_NL80211_QCA
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 ||
@@ -4680,6 +4711,7 @@
if (ret)
return ret;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
@@ -5424,7 +5456,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
- 0);
+ 0, NULL, 0);
}
@@ -5451,7 +5483,7 @@
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
- 0);
+ 0, NULL, 0);
}
@@ -5674,8 +5706,8 @@
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
size_t i;
- char brname[IFNAMSIZ];
- int ifindex, br_ifindex;
+ char master_ifname[IFNAMSIZ];
+ int ifindex, br_ifindex = 0;
int br_added = 0;
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
@@ -5686,15 +5718,21 @@
drv = bss->drv;
- if (linux_br_get(brname, params->ifname) == 0) {
+ if (linux_br_get(master_ifname, params->ifname) == 0) {
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);
+ params->ifname, master_ifname);
+ br_ifindex = if_nametoindex(master_ifname);
+ os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+ } else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+ linux_master_get(master_ifname, params->ifname) == 0) {
+ wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+ params->ifname, master_ifname);
+ /* start listening for EAPOL on the master interface */
+ add_ifidx(drv, if_nametoindex(master_ifname));
} else {
- brname[0] = '\0';
- br_ifindex = 0;
+ master_ifname[0] = '\0';
}
+
bss->br_ifindex = br_ifindex;
for (i = 0; i < params->num_bridge; i++) {
@@ -5714,7 +5752,7 @@
if (i802_check_bridge(drv, bss, params->bridge[0],
params->ifname) < 0)
goto failed;
- if (os_strcmp(params->bridge[0], brname) != 0)
+ if (os_strcmp(params->bridge[0], master_ifname) != 0)
br_added = 1;
}
@@ -5792,8 +5830,9 @@
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
}
- return -1;
}
@@ -5864,7 +5903,8 @@
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
char *force_ifname, u8 *if_addr,
- const char *bridge, int use_existing)
+ const char *bridge, int use_existing,
+ int setup_ap)
{
enum nl80211_iftype nlmode;
struct i802_bss *bss = priv;
@@ -5948,7 +5988,7 @@
os_memcpy(if_addr, new_addr, ETH_ALEN);
}
- if (type == WPA_IF_AP_BSS) {
+ if (type == WPA_IF_AP_BSS && setup_ap) {
struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
if (new_bss == NULL) {
if (added)
@@ -6103,7 +6143,8 @@
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
u64 *cookie_out, int no_cck, int no_ack,
- int offchanok)
+ int offchanok, const u16 *csa_offs,
+ size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
@@ -6123,6 +6164,8 @@
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)) ||
+ (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+ csa_offs_len * sizeof(u16), csa_offs)) ||
nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
goto fail;
@@ -6140,6 +6183,20 @@
if (cookie_out)
*cookie_out = no_ack ? (u64) -1 : cookie;
+
+ if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Drop oldest pending send action cookie 0x%llx",
+ (long long unsigned int)
+ drv->send_action_cookies[0]);
+ os_memmove(&drv->send_action_cookies[0],
+ &drv->send_action_cookies[1],
+ (MAX_SEND_ACTION_COOKIES - 1) *
+ sizeof(u64));
+ drv->num_send_action_cookies--;
+ }
+ drv->send_action_cookies[drv->num_send_action_cookies] = cookie;
+ drv->num_send_action_cookies++;
}
fail:
@@ -6182,29 +6239,28 @@
!drv->use_monitor))
ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
0, freq, no_cck, 1,
- wait_time);
+ wait_time, NULL, 0);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
&drv->send_action_cookie,
- no_cck, 0, 1);
+ no_cck, 0, 1, NULL, 0);
os_free(buf);
return ret;
}
-static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
{
- struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
- (long long unsigned int) drv->send_action_cookie);
+ (long long unsigned int) cookie);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
- nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
nlmsg_free(msg);
return;
}
@@ -6216,6 +6272,30 @@
}
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ unsigned int i;
+ u64 cookie;
+
+ /* Cancel the last pending TX cookie */
+ nl80211_frame_wait_cancel(bss, drv->send_action_cookie);
+
+ /*
+ * Cancel the other pending TX cookies, if any. This is needed since
+ * the driver may keep a list of all pending offchannel TX operations
+ * and free up the radio only once they have expired or cancelled.
+ */
+ for (i = drv->num_send_action_cookies; i > 0; i--) {
+ cookie = drv->send_action_cookies[i - 1];
+ if (cookie != drv->send_action_cookie)
+ nl80211_frame_wait_cancel(bss, cookie);
+ }
+ drv->num_send_action_cookies = 0;
+}
+
+
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
unsigned int duration)
{
@@ -6438,9 +6518,13 @@
static void wpa_driver_nl80211_resume(void *priv)
{
struct i802_bss *bss = priv;
+ enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
if (i802_set_iface_flags(bss, 1))
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+ if (is_p2p_net_interface(nlmode))
+ nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
}
@@ -6513,8 +6597,12 @@
os_memset(si, 0, sizeof(*si));
res = nl80211_get_link_signal(drv, si);
- if (res != 0)
- return res;
+ if (res) {
+ if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+ drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+ return res;
+ si->current_signal = 0;
+ }
res = nl80211_get_channel_width(drv, si);
if (res != 0)
@@ -6529,7 +6617,7 @@
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
- 0, 0, 0, 0);
+ 0, 0, 0, 0, NULL, 0);
}
@@ -6938,7 +7026,7 @@
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
- 0, 0) < 0)
+ 0, 0, NULL, 0) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
@@ -7208,6 +7296,19 @@
struct wpa_driver_scan_params *params)
{
struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /*
+ * Do a vendor specific scan if possible. If only_new_results is
+ * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+ * cannot be achieved through a vendor scan. The below condition may
+ * need to be modified if new scan flags are added in the future whose
+ * functionality can only be achieved through a normal scan.
+ */
+ if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+ return wpa_driver_nl80211_vendor_scan(bss, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
return wpa_driver_nl80211_scan(bss, params);
}
@@ -7245,11 +7346,13 @@
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
- unsigned int freq)
+ unsigned int freq,
+ const u16 *csa_offs, size_t csa_offs_len)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
- freq, 0, 0, 0);
+ freq, 0, 0, 0, csa_offs,
+ csa_offs_len);
}
@@ -7476,7 +7579,13 @@
"capa.max_acl_mac_addrs=%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",
+ "capa.mac_addr_rand_scan_supported=%d\n"
+ "capa.conc_capab=%u\n"
+ "capa.max_conc_chan_2_4=%u\n"
+ "capa.max_conc_chan_5_0=%u\n"
+ "capa.max_sched_scan_plans=%u\n"
+ "capa.max_sched_scan_plan_interval=%u\n"
+ "capa.max_sched_scan_plan_iterations=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -7492,7 +7601,13 @@
drv->capa.max_acl_mac_addrs,
drv->capa.num_multichan_concurrent,
drv->capa.mac_addr_rand_sched_scan_supported,
- drv->capa.mac_addr_rand_scan_supported);
+ drv->capa.mac_addr_rand_scan_supported,
+ drv->capa.conc_capab,
+ drv->capa.max_conc_chan_2_4,
+ drv->capa.max_conc_chan_5_0,
+ drv->capa.max_sched_scan_plans,
+ drv->capa.max_sched_scan_plan_interval,
+ drv->capa.max_sched_scan_plan_iterations);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -7535,6 +7650,8 @@
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nlattr *beacon_csa;
int ret = -ENOBUFS;
+ int csa_off_len = 0;
+ int i;
wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
settings->cs_count, settings->block_tx,
@@ -7551,20 +7668,56 @@
(drv->nlmode != NL80211_IFTYPE_P2P_GO))
return -EOPNOTSUPP;
- /* check settings validity */
- if (!settings->beacon_csa.tail ||
- ((settings->beacon_csa.tail_len <=
- settings->counter_offset_beacon) ||
- (settings->beacon_csa.tail[settings->counter_offset_beacon] !=
- settings->cs_count)))
+ /*
+ * Remove empty counters, assuming Probe Response and Beacon frame
+ * counters match. This implementation assumes that there are only two
+ * counters.
+ */
+ if (settings->counter_offset_beacon[0] &&
+ !settings->counter_offset_beacon[1]) {
+ csa_off_len = 1;
+ } else if (settings->counter_offset_beacon[1] &&
+ !settings->counter_offset_beacon[0]) {
+ csa_off_len = 1;
+ settings->counter_offset_beacon[0] =
+ settings->counter_offset_beacon[1];
+ settings->counter_offset_presp[0] =
+ settings->counter_offset_presp[1];
+ } else if (settings->counter_offset_beacon[1] &&
+ settings->counter_offset_beacon[0]) {
+ csa_off_len = 2;
+ } else {
+ wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+ return -EINVAL;
+ }
+
+ /* Check CSA counters validity */
+ if (drv->capa.max_csa_counters &&
+ csa_off_len > drv->capa.max_csa_counters) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Too many CSA counters provided");
+ return -EINVAL;
+ }
+
+ if (!settings->beacon_csa.tail)
return -EINVAL;
- if (settings->beacon_csa.probe_resp &&
- ((settings->beacon_csa.probe_resp_len <=
- settings->counter_offset_presp) ||
- (settings->beacon_csa.probe_resp[settings->counter_offset_presp] !=
- settings->cs_count)))
- return -EINVAL;
+ for (i = 0; i < csa_off_len; i++) {
+ u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+ u16 csa_c_off_presp = settings->counter_offset_presp[i];
+
+ if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+ (settings->beacon_csa.tail[csa_c_off_bcn] !=
+ settings->cs_count))
+ return -EINVAL;
+
+ if (settings->beacon_csa.probe_resp &&
+ ((settings->beacon_csa.probe_resp_len <=
+ csa_c_off_presp) ||
+ (settings->beacon_csa.probe_resp[csa_c_off_presp] !=
+ settings->cs_count)))
+ return -EINVAL;
+ }
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
@@ -7588,11 +7741,13 @@
if (ret)
goto error;
- if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
- settings->counter_offset_beacon) ||
+ if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_beacon) ||
(settings->beacon_csa.probe_resp &&
- nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
- settings->counter_offset_presp)))
+ nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+ csa_off_len * sizeof(u16),
+ settings->counter_offset_presp)))
goto fail;
nla_nest_end(msg, beacon_csa);
@@ -7838,6 +7993,7 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
{
struct i802_bss *bss = priv;
@@ -7870,6 +8026,7 @@
return send_and_recv_msgs(drv, msg, NULL, NULL);
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
static int nl80211_set_mac_addr(void *priv, const u8 *addr)
@@ -8354,6 +8511,8 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
{
switch (hw_mode) {
@@ -8373,6 +8532,26 @@
}
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+ int i, len, ret;
+ u32 *freqs;
+
+ if (!freq_list)
+ return 0;
+ len = int_array_len(freq_list);
+ freqs = os_malloc(sizeof(u32) * len);
+ if (!freqs)
+ return -1;
+ for (i = 0; i < len; i++)
+ freqs[i] = freq_list[i];
+ ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(u32) * len, freqs);
+ os_free(freqs);
+ return ret;
+}
+
+
static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
{
struct i802_bss *bss = priv;
@@ -8402,7 +8581,8 @@
params->ch_width) ||
(params->ch_list_len &&
nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
- params->ch_list))) {
+ params->ch_list)) ||
+ add_acs_freq_list(msg, params->freq_list)) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -8470,6 +8650,195 @@
}
+struct nl80211_pcl {
+ unsigned int num;
+ unsigned int *freq_list;
+};
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nl80211_pcl *param = arg;
+ struct nlattr *nl_vend, *attr;
+ enum qca_iface_type iface_type;
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ unsigned int num, max_num;
+ u32 *freqs;
+
+ 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)
+ return NL_SKIP;
+
+ 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_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+ if (!attr) {
+ wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+
+ iface_type = (enum qca_iface_type) nla_get_u32(attr);
+ wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+ iface_type);
+
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+ if (!attr) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: preferred_freq_list couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+
+ /*
+ * param->num has the maximum number of entries for which there
+ * is room in the freq_list provided by the caller.
+ */
+ freqs = nla_data(attr);
+ max_num = nla_len(attr) / sizeof(u32);
+ if (max_num > param->num)
+ max_num = param->num;
+ for (num = 0; num < max_num; num++)
+ param->freq_list[num] = freqs[num];
+ param->num = num;
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ unsigned int i;
+ struct nlattr *params;
+ struct nl80211_pcl param;
+ enum qca_iface_type iface_type;
+
+ if (!drv->get_pref_freq_list)
+ return -1;
+
+ switch (if_type) {
+ case WPA_IF_STATION:
+ iface_type = QCA_IFACE_TYPE_STA;
+ break;
+ case WPA_IF_AP_BSS:
+ iface_type = QCA_IFACE_TYPE_AP;
+ break;
+ case WPA_IF_P2P_GO:
+ iface_type = QCA_IFACE_TYPE_P2P_GO;
+ break;
+ case WPA_IF_P2P_CLIENT:
+ iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+ break;
+ case WPA_IF_IBSS:
+ iface_type = QCA_IFACE_TYPE_IBSS;
+ break;
+ case WPA_IF_TDLS:
+ iface_type = QCA_IFACE_TYPE_TDLS;
+ break;
+ default:
+ return -1;
+ }
+
+ param.num = *num;
+ param.freq_list = freq_list;
+
+ if (!(msg = nl80211_drv_msg(drv, 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_GET_PREFERRED_FREQ_LIST) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+ iface_type)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+ ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, ¶m);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in send_and_recv_msgs", __func__);
+ return ret;
+ }
+
+ *num = param.num;
+
+ for (i = 0; i < *num; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
+ i, freq_list[i]);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ struct nlattr *params;
+
+ if (!drv->set_prob_oper_freq)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set P2P probable operating freq %u for ifindex %d",
+ freq, bss->ifindex);
+
+ 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_SET_PROBABLE_OPER_CHANNEL) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+ QCA_IFACE_TYPE_P2P_CLIENT) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+ freq)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ __func__);
+ return ret;
+ }
+ nlmsg_free(msg);
+ return 0;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -8480,6 +8849,7 @@
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+ .abort_scan = wpa_driver_nl80211_abort_scan,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
.associate = wpa_driver_nl80211_associate,
@@ -8563,7 +8933,9 @@
.vendor_cmd = nl80211_vendor_cmd,
.set_qos_map = nl80211_set_qos_map,
.set_wowlan = nl80211_set_wowlan,
+#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
.set_mac_addr = nl80211_set_mac_addr,
#ifdef CONFIG_MESH
.init_mesh = wpa_driver_nl80211_init_mesh,
@@ -8576,6 +8948,10 @@
.br_set_net_param = wpa_driver_br_set_net_param,
.add_tx_ts = nl80211_add_ts,
.del_tx_ts = nl80211_del_ts,
+#ifdef CONFIG_DRIVER_NL80211_QCA
.do_acs = wpa_driver_do_acs,
.set_band = nl80211_set_band,
+ .get_pref_freq_list = nl80211_get_pref_freq_list,
+ .set_prob_oper_freq = nl80211_set_prob_oper_freq,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 1536d2f..21c0b6d 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -146,9 +146,16 @@
unsigned int set_rekey_offload:1;
unsigned int p2p_go_ctwindow_supported:1;
unsigned int setband_vendor_cmd_avail:1;
+ unsigned int get_pref_freq_list:1;
+ unsigned int set_prob_oper_freq:1;
+ unsigned int scan_vendor_cmd_avail:1;
+ u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
+#define MAX_SEND_ACTION_COOKIES 20
+ u64 send_action_cookies[MAX_SEND_ACTION_COOKIES];
+ unsigned int num_send_action_cookies;
unsigned int last_mgmt_freq;
@@ -180,6 +187,13 @@
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+
+ /*
+ * Tells whether the last scan issued from wpa_supplicant was a normal
+ * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+ * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+ */
+ int last_scan_cmd;
};
struct nl_msg;
@@ -265,11 +279,13 @@
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);
+ struct wpa_driver_scan_params *params);
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);
+int wpa_driver_nl80211_abort_scan(void *priv);
const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
#endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 4929cea..c74ed5f 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -499,6 +499,19 @@
capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+ if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+ tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+ capa->max_sched_scan_plans =
+ nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+ capa->max_sched_scan_plan_interval =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+ capa->max_sched_scan_plan_iterations =
+ nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+ }
+
if (tb[NL80211_ATTR_MAX_MATCH_SETS])
capa->max_match_sets =
nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
@@ -580,6 +593,7 @@
case QCA_NL80211_VENDOR_SUBCMD_TEST:
drv->vendor_cmd_test_avail = 1;
break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
drv->roaming_vendor_cmd_avail = 1;
break;
@@ -589,6 +603,12 @@
case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
drv->get_features_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+ drv->get_pref_freq_list = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+ drv->set_prob_oper_freq = 1;
+ break;
case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
drv->capa.flags |=
WPA_DRIVER_FLAGS_ACS_OFFLOAD;
@@ -596,6 +616,10 @@
case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
drv->setband_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ drv->scan_vendor_cmd_avail = 1;
+ break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
@@ -627,6 +651,10 @@
capa->max_stations =
nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+ if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
+ capa->max_csa_counters =
+ nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+
return NL_SKIP;
}
@@ -683,8 +711,6 @@
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 =
@@ -692,10 +718,24 @@
drv->capa.mac_addr_rand_scan_supported =
info->mac_addr_rand_scan_supported;
+ if (info->channel_switch_supported) {
+ drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+ if (!drv->capa.max_csa_counters)
+ drv->capa.max_csa_counters = 1;
+ }
+
+ if (!drv->capa.max_sched_scan_plans) {
+ drv->capa.max_sched_scan_plans = 1;
+ drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+ drv->capa.max_sched_scan_plan_iterations = 0;
+ }
+
return 0;
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
static int dfs_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -751,6 +791,7 @@
struct features_info {
u8 *flags;
size_t flags_len;
+ struct wpa_driver_capa *capa;
};
@@ -776,6 +817,19 @@
info->flags = nla_data(attr);
info->flags_len = nla_len(attr);
}
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+ if (attr)
+ info->capa->conc_capab = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+ if (attr)
+ info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+ if (attr)
+ info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
}
return NL_SKIP;
@@ -810,6 +864,7 @@
}
os_memset(&info, 0, sizeof(info));
+ info.capa = &drv->capa;
ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
if (ret || !info.flags)
return;
@@ -819,8 +874,14 @@
if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+ &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
@@ -901,9 +962,21 @@
if (!drv->use_monitor && !info.data_tx_status)
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+#ifdef CONFIG_DRIVER_NL80211_QCA
qca_nl80211_check_dfs_capa(drv);
qca_nl80211_get_features(drv);
+ /*
+ * To enable offchannel simultaneous support in wpa_supplicant, the
+ * underlying driver needs to support the same along with offchannel TX.
+ * Offchannel TX support is needed since remain_on_channel and
+ * action_tx use some common data structures and hence cannot be
+ * scheduled simultaneously.
+ */
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
return 0;
}
@@ -912,6 +985,7 @@
u16 *num_modes;
struct hostapd_hw_modes *modes;
int last_mode, last_chan_idx;
+ int failed;
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1029,7 +1103,7 @@
mode->num_channels + new_channels,
sizeof(struct hostapd_channel_data));
if (!channel)
- return NL_SKIP;
+ return NL_STOP;
mode->channels = channel;
mode->num_channels += new_channels;
@@ -1075,7 +1149,7 @@
mode->rates = os_calloc(mode->num_rates, sizeof(int));
if (!mode->rates)
- return NL_SKIP;
+ return NL_STOP;
idx = 0;
@@ -1104,8 +1178,10 @@
mode = os_realloc_array(phy_info->modes,
*phy_info->num_modes + 1,
sizeof(*mode));
- if (!mode)
- return NL_SKIP;
+ if (!mode) {
+ phy_info->failed = 1;
+ return NL_STOP;
+ }
phy_info->modes = mode;
mode = &phy_info->modes[*(phy_info->num_modes)];
@@ -1141,11 +1217,12 @@
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)
+ if (ret == NL_OK)
+ ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+ if (ret != NL_OK) {
+ phy_info->failed = 1;
return ret;
- ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
- if (ret != NL_OK)
- return ret;
+ }
return NL_OK;
}
@@ -1360,7 +1437,7 @@
static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
- int end)
+ int end, int max_bw)
{
int c;
@@ -1377,6 +1454,32 @@
if (chan->freq - 70 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+
+ if (max_bw >= 160) {
+ if (chan->freq - 10 >= start && chan->freq + 150 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_10_150;
+
+ if (chan->freq - 30 >= start && chan->freq + 130 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_30_130;
+
+ if (chan->freq - 50 >= start && chan->freq + 110 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_50_110;
+
+ if (chan->freq - 70 >= start && chan->freq + 90 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_70_90;
+
+ if (chan->freq - 90 >= start && chan->freq + 70 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_90_70;
+
+ if (chan->freq - 110 >= start && chan->freq + 50 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_110_50;
+
+ if (chan->freq - 130 >= start && chan->freq + 30 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_130_30;
+
+ if (chan->freq - 150 >= start && chan->freq + 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_150_10;
+ }
}
}
@@ -1407,7 +1510,7 @@
if (!results->modes[m].vht_capab)
continue;
- nl80211_set_vht_mode(&results->modes[m], start, end);
+ nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
}
}
@@ -1545,6 +1648,7 @@
.num_modes = num_modes,
.modes = NULL,
.last_mode = -1,
+ .failed = 0,
};
*num_modes = 0;
@@ -1561,6 +1665,16 @@
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
nl80211_set_regulatory_flags(drv, &result);
+ if (result.failed) {
+ int i;
+
+ for (i = 0; result.modes && i < *num_modes; i++) {
+ os_free(result.modes[i].channels);
+ os_free(result.modes[i].rates);
+ }
+ os_free(result.modes);
+ return NULL;
+ }
return wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7b0f721..4d7ac47 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -268,7 +268,8 @@
struct nlattr *authorized,
struct nlattr *key_replay_ctr,
struct nlattr *ptk_kck,
- struct nlattr *ptk_kek)
+ struct nlattr *ptk_kek,
+ struct nlattr *subnet_status)
{
union wpa_event_data event;
const u8 *ssid;
@@ -367,6 +368,17 @@
event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
}
+ if (subnet_status) {
+ /*
+ * At least for now, this is only available from
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+ * attribute has the same values 0, 1, 2 as are used in the
+ * variable here, so no mapping between different values are
+ * needed.
+ */
+ event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
@@ -639,10 +651,21 @@
* 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.
+ * In case of locally generated event clear
+ * ignore_next_local_deauth as well, to avoid next local
+ * deauth event be wrongly ignored.
*/
- 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));
+ if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
+ ETH_ALEN)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+ drv->ignore_next_local_deauth = 0;
+ } else {
+ 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;
}
@@ -679,13 +702,15 @@
mgmt->u.disassoc.variable;
}
} else {
+ event.deauth_info.locally_generated =
+ !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
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;
+ if (event.deauth_info.locally_generated)
+ drv->ignore_next_local_deauth = 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) {
@@ -968,7 +993,7 @@
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
- struct nlattr *tb[])
+ struct nlattr *tb[], int external_scan)
{
union wpa_event_data event;
struct nlattr *nl;
@@ -978,7 +1003,7 @@
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
- if (drv->scan_for_auth) {
+ if (!external_scan && drv->scan_for_auth) {
drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry");
@@ -989,6 +1014,8 @@
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = aborted;
+ info->external_scan = external_scan;
+ info->nl_scan_event = 1;
if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
@@ -1004,7 +1031,7 @@
}
}
if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
- char msg[200], *pos, *end;
+ char msg[300], *pos, *end;
int res;
pos = msg;
@@ -1444,6 +1471,8 @@
}
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
const u8 *data, size_t len)
{
@@ -1596,7 +1625,8 @@
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]);
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
}
@@ -1686,6 +1716,140 @@
}
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ union wpa_event_data event;
+ struct scan_info *info;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* External scan trigger event, ignore */
+ return;
+ }
+
+ /* Cookie match, own scan */
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->external_scan = 0;
+ info->nl_scan_event = 0;
+
+ drv->scan_state = SCAN_STARTED;
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+ int aborted, struct nlattr *tb[],
+ int external_scan)
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+ info->external_scan = external_scan;
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_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[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+ char msg[300], *pos, *end;
+ int res;
+
+ pos = msg;
+ end = pos + sizeof(msg);
+ *pos = '\0';
+
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_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 qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ enum scan_status status;
+ int external_scan;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+ if (status >= VENDOR_SCAN_STATUS_MAX)
+ return; /* invalid status */
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* Event from an external scan, get scan results */
+ external_scan = 1;
+ } else {
+ external_scan = 0;
+ if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+ drv->scan_state = SCAN_COMPLETED;
+ else
+ drv->scan_state = SCAN_ABORTED;
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ drv->vendor_scan_cookie = 0;
+ drv->last_scan_cmd = 0;
+ }
+
+ send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+ external_scan);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
{
@@ -1693,6 +1857,7 @@
case QCA_NL80211_VENDOR_SUBCMD_TEST:
wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
qca_nl80211_avoid_freq(drv, data, len);
break;
@@ -1709,6 +1874,13 @@
case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ qca_nl80211_scan_trigger_event(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+ qca_nl80211_scan_done_event(drv, data, len);
+ break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
default:
wpa_printf(MSG_DEBUG,
"nl80211: Ignore unsupported QCA vendor event %u",
@@ -1831,6 +2003,7 @@
{
struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data data;
+ int external_scan_event = 0;
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1883,28 +2056,38 @@
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);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ drv->scan_state = SCAN_COMPLETED;
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 0, tb, external_scan_event);
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);
+ send_scan_event(drv, 0, tb, 0);
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);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ 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);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 1, tb, external_scan_event);
break;
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
@@ -1927,7 +2110,7 @@
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
tb[NL80211_ATTR_RESP_IE],
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index f3d45e5..2ff254e 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -1,5 +1,6 @@
/*
* Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications
@@ -14,6 +15,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
#include "driver_nl80211.h"
@@ -131,6 +133,8 @@
goto fail;
}
nla_nest_end(msg, ssids);
+ } else {
+ wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
}
if (params->extra_ies) {
@@ -221,6 +225,9 @@
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
drv->scan_for_auth = 0;
+ if (TEST_FAIL())
+ return -1;
+
msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
if (!msg)
return -1;
@@ -294,6 +301,7 @@
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
fail:
nlmsg_free(msg);
@@ -301,16 +309,82 @@
}
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+ struct nl_msg *msg,
+ struct wpa_driver_scan_params *params)
+{
+ struct nlattr *plans;
+ struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+ unsigned int i;
+
+ plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+ if (!plans)
+ return -1;
+
+ for (i = 0; i < params->sched_scan_plans_num; i++) {
+ struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+ if (!plan)
+ return -1;
+
+ if (!scan_plans[i].interval ||
+ scan_plans[i].interval >
+ drv->capa.max_sched_scan_plan_interval) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: sched scan plan no. %u: Invalid interval: %u",
+ i, scan_plans[i].interval);
+ return -1;
+ }
+
+ if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ scan_plans[i].interval))
+ return -1;
+
+ if (scan_plans[i].iterations >
+ drv->capa.max_sched_scan_plan_iterations) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+ i, scan_plans[i].iterations);
+ return -1;
+ }
+
+ if (scan_plans[i].iterations &&
+ nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+ scan_plans[i].iterations))
+ return -1;
+
+ nla_nest_end(msg, plan);
+
+ /*
+ * All the scan plans must specify the number of iterations
+ * except the last plan, which will run infinitely. So if the
+ * number of iterations is not specified, this ought to be the
+ * last scan plan.
+ */
+ if (!scan_plans[i].iterations)
+ break;
+ }
+
+ if (i != params->sched_scan_plans_num - 1) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: All sched scan plans but the last must specify number of iterations");
+ return -1;
+ }
+
+ nla_nest_end(msg, plans);
+ return 0;
+}
+
+
/**
* 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 wpa_driver_scan_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -325,11 +399,27 @@
return android_pno_start(bss, params);
#endif /* ANDROID */
+ if (!params->sched_scan_plans_num ||
+ params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Invalid number of sched scan plans: %u",
+ params->sched_scan_plans_num);
+ return -1;
+ }
+
msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
- if (!msg ||
- nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+ if (!msg)
goto fail;
+ if (drv->capa.max_sched_scan_plan_iterations) {
+ if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+ goto fail;
+ } else {
+ if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+ params->sched_scan_plans[0].interval * 1000))
+ goto fail;
+ }
+
if ((drv->num_filter_ssids &&
(int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
params->filter_rssi) {
@@ -392,8 +482,7 @@
goto fail;
}
- wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
- "scan interval %d msec", ret, interval);
+ wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
fail:
nlmsg_free(msg);
@@ -443,8 +532,8 @@
pos = ies;
end = ies + ies_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
@@ -778,3 +867,213 @@
wpa_scan_results_free(res);
}
+
+
+int wpa_driver_nl80211_abort_scan(void *priv)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int ret;
+ struct nl_msg *msg;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+ msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ }
+
+ return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int scan_cookie_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u64 *cookie = 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_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ *cookie = nla_get_u64(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ }
+
+ return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor 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_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *attr;
+ size_t i;
+ u32 scan_flags = 0;
+ int ret = -1;
+ u64 cookie = 0;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+ drv->scan_for_auth = 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_TRIGGER_SCAN) )
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (attr == NULL)
+ goto fail;
+
+ if (params->num_ssids) {
+ struct nlattr *ssids;
+
+ ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_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, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+ params->extra_ies_len, params->extra_ies))
+ goto fail;
+ }
+
+ if (params->freqs) {
+ struct nlattr *freqs;
+
+ freqs = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_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->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, QCA_WLAN_VENDOR_ATTR_SCAN_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, QCA_WLAN_VENDOR_ATTR_SCAN_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;
+
+ if (params->p2p_probe) {
+ struct nlattr *rates;
+
+ wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+ rates = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_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, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+ goto fail;
+ }
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+ drv->vendor_scan_cookie = cookie;
+ drv->scan_state = SCAN_REQUESTED;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+ ret, (long long unsigned int) cookie);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 1cfc15d..762c12f 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -161,11 +161,11 @@
return NULL;
}
- while (results->num < (size_t) num && pos + sizeof(int) < end) {
+ while (results->num < (size_t) num && end - pos > sizeof(int)) {
int len;
os_memcpy(&len, pos, sizeof(int));
pos += sizeof(int);
- if (len < 0 || len > 10000 || pos + len > end)
+ if (len < 0 || len > 10000 || len > end - pos)
break;
r = os_malloc(len);
@@ -220,6 +220,56 @@
}
+static int wpa_driver_privsep_authenticate(
+ void *priv, struct wpa_driver_auth_params *params)
+{
+ struct wpa_driver_privsep_data *drv = priv;
+ struct privsep_cmd_authenticate *data;
+ int i, res;
+ size_t buflen;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
+ " auth_alg=%d local_state_change=%d p2p=%d",
+ __func__, priv, params->freq, MAC2STR(params->bssid),
+ params->auth_alg, params->local_state_change, params->p2p);
+
+ buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+ data = os_zalloc(buflen);
+ if (data == NULL)
+ return -1;
+
+ data->freq = params->freq;
+ os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+ os_memcpy(data->ssid, params->ssid, params->ssid_len);
+ data->ssid_len = params->ssid_len;
+ data->auth_alg = params->auth_alg;
+ data->ie_len = params->ie_len;
+ for (i = 0; i < 4; i++) {
+ if (params->wep_key[i])
+ os_memcpy(data->wep_key[i], params->wep_key[i],
+ params->wep_key_len[i]);
+ data->wep_key_len[i] = params->wep_key_len[i];
+ }
+ data->wep_tx_keyidx = params->wep_tx_keyidx;
+ data->local_state_change = params->local_state_change;
+ data->p2p = params->p2p;
+ pos = (u8 *) (data + 1);
+ if (params->ie_len) {
+ os_memcpy(pos, params->ie, params->ie_len);
+ pos += params->ie_len;
+ }
+ if (params->sae_data_len)
+ os_memcpy(pos, params->sae_data, params->sae_data_len);
+
+ res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
+ NULL, NULL);
+ os_free(data);
+
+ return res;
+}
+
+
static int wpa_driver_privsep_associate(
void *priv, struct wpa_driver_associate_params *params)
{
@@ -309,6 +359,32 @@
}
+static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len)
+{
+ union wpa_event_data data;
+ struct privsep_event_auth *auth;
+
+ os_memset(&data, 0, sizeof(data));
+ if (len < sizeof(*auth))
+ return;
+ auth = (struct privsep_event_auth *) buf;
+ if (len < sizeof(*auth) + auth->ies_len)
+ return;
+
+ os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
+ os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
+ data.auth.auth_type = auth->auth_type;
+ data.auth.auth_transaction = auth->auth_transaction;
+ data.auth.status_code = auth->status_code;
+ if (auth->ies_len) {
+ data.auth.ies = (u8 *) (auth + 1);
+ data.auth.ies_len = auth->ies_len;
+ }
+
+ wpa_supplicant_event(ctx, EVENT_AUTH, &data);
+}
+
+
static void wpa_driver_privsep_event_assoc(void *ctx,
enum wpa_event_type event,
u8 *buf, size_t len)
@@ -468,6 +544,9 @@
case PRIVSEP_EVENT_SCAN_RESULTS:
wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
break;
+ case PRIVSEP_EVENT_SCAN_STARTED:
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+ break;
case PRIVSEP_EVENT_ASSOC:
wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
event_buf, event_len);
@@ -503,6 +582,9 @@
wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
event_len);
break;
+ case PRIVSEP_EVENT_AUTH:
+ wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len);
+ break;
}
os_free(buf);
@@ -703,6 +785,10 @@
res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
if (res < 0 || len != sizeof(*capa))
return -1;
+ /* For now, no support for passing extended_capa pointers */
+ capa->extended_capa = NULL;
+ capa->extended_capa_mask = NULL;
+ capa->extended_capa_len = 0;
return 0;
}
@@ -735,6 +821,7 @@
.set_param = wpa_driver_privsep_set_param,
.scan2 = wpa_driver_privsep_scan,
.deauthenticate = wpa_driver_privsep_deauthenticate,
+ .authenticate = wpa_driver_privsep_authenticate,
.associate = wpa_driver_privsep_associate,
.get_capa = wpa_driver_privsep_get_capa,
.get_mac_addr = wpa_driver_privsep_get_mac_addr,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 01defdf..791cd5d 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -422,7 +422,7 @@
static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
- char *data, int len)
+ char *data, unsigned int len)
{
struct iw_event iwe_buf, *iwe = &iwe_buf;
char *pos, *end, *custom, *buf;
@@ -430,13 +430,13 @@
pos = data;
end = data + len;
- while (pos + IW_EV_LCP_LEN <= end) {
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
iwe->cmd, iwe->len);
- if (iwe->len <= IW_EV_LCP_LEN)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
return;
custom = pos + IW_EV_POINT_LEN;
@@ -480,7 +480,7 @@
}
break;
case IWEVMICHAELMICFAILURE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVMICHAELMICFAILURE length");
return;
@@ -489,7 +489,7 @@
drv->ctx, custom, iwe->u.data.length);
break;
case IWEVCUSTOM:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVCUSTOM length");
return;
@@ -508,7 +508,7 @@
NULL);
break;
case IWEVASSOCREQIE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVASSOCREQIE length");
return;
@@ -517,7 +517,7 @@
drv, custom, iwe->u.data.length);
break;
case IWEVASSOCRESPIE:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVASSOCRESPIE length");
return;
@@ -526,7 +526,7 @@
drv, custom, iwe->u.data.length);
break;
case IWEVPMKIDCAND:
- if (custom + iwe->u.data.length > end) {
+ if (iwe->u.data.length > end - custom) {
wpa_printf(MSG_DEBUG, "WEXT: Invalid "
"IWEVPMKIDCAND length");
return;
@@ -1220,7 +1220,7 @@
char *end)
{
int ssid_len = iwe->u.essid.length;
- if (custom + ssid_len > end)
+ if (ssid_len > end - custom)
return;
if (iwe->u.essid.flags &&
ssid_len > 0 &&
@@ -1316,7 +1316,7 @@
size_t clen;
clen = iwe->len;
- if (custom + clen > end)
+ if (clen > (size_t) (end - custom))
return;
maxrate = 0;
while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
@@ -1369,7 +1369,7 @@
u8 *tmp;
clen = iwe->u.data.length;
- if (custom + clen > end)
+ if (clen > (size_t) (end - custom))
return;
if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
@@ -1441,8 +1441,8 @@
/* Figure out whether we need to fake any IEs */
pos = data->ie;
end = pos + data->ie_len;
- while (pos && pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (pos && end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_SSID)
ssid_ie = pos;
@@ -1530,11 +1530,11 @@
end = (char *) res_buf + len;
os_memset(&data, 0, sizeof(data));
- while (pos + IW_EV_LCP_LEN <= end) {
+ while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
/* Event data may be unaligned, so make a local, aligned copy
* before processing. */
os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
- if (iwe->len <= IW_EV_LCP_LEN)
+ if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
break;
custom = pos + IW_EV_POINT_LEN;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 9434078..c4f5f97 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,6 +30,9 @@
DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
DRV_OBJS += ../src/utils/radiotap.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
NEED_SME=y
NEED_AP_MLME=y
NEED_NETLINK=y
@@ -54,7 +57,9 @@
ifdef CONFIG_LIBNL_TINY
DRV_LIBS += -lnl-tiny
else
- DRV_LIBS += -lnl
+ ifndef CONFIG_OSX
+ DRV_LIBS += -lnl
+ endif
endif
ifdef CONFIG_LIBNL20
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 8da4c53..0a05a24 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -26,6 +26,9 @@
DRV_OBJS += src/drivers/driver_nl80211_monitor.c
DRV_OBJS += src/drivers/driver_nl80211_scan.c
DRV_OBJS += src/utils/radiotap.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
NEED_SME=y
NEED_AP_MLME=y
NEED_NETLINK=y
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 837971d..e21147a 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -219,3 +219,26 @@
os_strlcpy(brname, pos, IFNAMSIZ);
return 0;
}
+
+
+int linux_master_get(char *master_ifname, const char *ifname)
+{
+ char buf[128], masterlink[128], *pos;
+ ssize_t res;
+
+ /* check whether there is a master */
+ os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
+
+ res = readlink(buf, masterlink, sizeof(masterlink));
+ if (res < 0 || (size_t) res >= sizeof(masterlink))
+ return -1;
+
+ masterlink[res] = '\0';
+
+ pos = os_strrchr(masterlink, '/');
+ if (pos == NULL)
+ return -1;
+ pos++;
+ os_strlcpy(master_ifname, pos, IFNAMSIZ);
+ return 0;
+}
diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
index c03fe6e..6de4d9b 100644
--- a/src/drivers/linux_ioctl.h
+++ b/src/drivers/linux_ioctl.h
@@ -18,5 +18,6 @@
int linux_br_add_if(int sock, const char *brname, const char *ifname);
int linux_br_del_if(int sock, const char *brname, const char *ifname);
int linux_br_get(char *brname, const char *ifname);
+int linux_master_get(char *master_ifname, const char *ifname);
#endif /* LINUX_IOCTL_H */
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ae16ba9..5b7b5eb 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,6 +10,7 @@
* Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
* Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015 Intel Deutschland GmbH
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -328,7 +329,15 @@
* partial scan results may be available
*
* @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
- * intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ * intervals and certain number of cycles, as specified by
+ * %NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ * not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ * scheduled scan will run in an infinite loop with the specified interval.
+ * These attributes are mutually exculsive,
+ * i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ * NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ * If for some reason scheduled scan is aborted by the driver, all scan
+ * plans are canceled (including scan plans that did not start yet).
* Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
* are passed, they are used in the probe requests. For
* broadcast, a broadcast SSID must be passed (ie. an empty
@@ -811,6 +820,10 @@
* as an event to indicate changes for devices with wiphy-specific regdom
* management.
*
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ * not running. The driver indicates the status of the scan through
+ * cfg80211_scan_done().
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -997,6 +1010,8 @@
NL80211_CMD_WIPHY_REG_CHANGE,
+ NL80211_CMD_ABORT_SCAN,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1754,12 +1769,27 @@
* should be contained in the result as the sum of the respective counters
* over all channels.
*
- * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
- * WoWLAN net-detect scan) is started, u32 in seconds.
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ * scheduled scan is started. Or the delay before a WoWLAN
+ * net-detect scan is started, counting from the moment the
+ * system is suspended. This value is a u32, in seconds.
* @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
* is operating in an indoor environment.
*
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ * scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ * a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ * Each scan plan defines the number of scan iterations and the interval
+ * between scans. The last scan plan will always run infinitely,
+ * thus it must not specify the number of iterations, only the interval
+ * between scans. The scan plans are executed sequentially.
+ * Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2129,6 +2159,11 @@
NL80211_ATTR_REG_INDOOR,
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+ NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+ NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+ NL80211_ATTR_SCHED_SCAN_PLANS,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2619,16 +2654,17 @@
* an indoor surroundings, i.e., it is connected to AC power (and not
* through portable DC inverters) or is under the control of a master
* that is acting as an AP and is connected to AC power.
- * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this
* channel if it's connected concurrently to a BSS on the same channel on
* the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
- * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
- * channel that has the GO_CONCURRENT attribute set can be done when there
- * is a clear assessment that the device is operating under the guidance of
- * an authorized master, i.e., setting up a GO while the device is also
- * connected to an AP with DFS and radar detection on the UNII band (it is
- * up to user-space, i.e., wpa_supplicant to perform the required
- * verifications)
+ * band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ * off-channel on a channel that has the IR_CONCURRENT attribute set can be
+ * done when there is a clear assessment that the device is operating under
+ * the guidance of an authorized master, i.e., setting up a GO or TDLS
+ * off-channel while the device is also connected to an AP with DFS and
+ * radar detection on the UNII band (it is up to user-space, i.e.,
+ * wpa_supplicant to perform the required verifications). Using this
+ * attribute for IR is disallowed for master interfaces (IBSS, AP).
* @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
* on this channel in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
@@ -2640,7 +2676,7 @@
* See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
* for more information on the FCC description of the relaxations allowed
* by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
- * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
*/
enum nl80211_frequency_attr {
__NL80211_FREQUENCY_ATTR_INVALID,
@@ -2658,7 +2694,7 @@
NL80211_FREQUENCY_ATTR_NO_160MHZ,
NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
- NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
NL80211_FREQUENCY_ATTR_NO_20MHZ,
NL80211_FREQUENCY_ATTR_NO_10MHZ,
@@ -2671,6 +2707,8 @@
#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN NL80211_FREQUENCY_ATTR_NO_IR
#define NL80211_FREQUENCY_ATTR_NO_IBSS NL80211_FREQUENCY_ATTR_NO_IR
#define NL80211_FREQUENCY_ATTR_NO_IR NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+ NL80211_FREQUENCY_ATTR_IR_CONCURRENT
/**
* enum nl80211_bitrate_attr - bitrate attributes
@@ -2829,7 +2867,7 @@
* @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_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_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
@@ -2846,7 +2884,7 @@
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_IR_CONCURRENT = 1<<12,
NL80211_RRF_NO_HT40MINUS = 1<<13,
NL80211_RRF_NO_HT40PLUS = 1<<14,
NL80211_RRF_NO_80MHZ = 1<<15,
@@ -2858,6 +2896,7 @@
#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR
#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT NL80211_RRF_IR_CONCURRENT
/* For backport compatibility with older userspace */
#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -3359,6 +3398,9 @@
* (not present if no beacon frame has been received yet)
* @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
* @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ * was last updated by a received frame. The value is expected to be
+ * accurate to about 10ms. (u64, nanoseconds)
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3378,6 +3420,7 @@
NL80211_BSS_CHAN_WIDTH,
NL80211_BSS_BEACON_TSF,
NL80211_BSS_PRESP_DATA,
+ NL80211_BSS_LAST_SEEN_BOOTTIME,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4584,4 +4627,28 @@
NL80211_TDLS_PEER_WMM = 1<<2,
};
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ * seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ * scan plan (u32). The last scan plan must not specify this attribute
+ * because it will run infinitely. A value of zero is invalid as it will
+ * make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ * currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+ __NL80211_SCHED_SCAN_PLAN_INVALID,
+ NL80211_SCHED_SCAN_PLAN_INTERVAL,
+ NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+ /* keep last */
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+ NL80211_SCHED_SCAN_PLAN_MAX =
+ __NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
index 8c7ae27..b081879 100644
--- a/src/eap_common/eap_gpsk_common.c
+++ b/src/eap_common/eap_gpsk_common.c
@@ -92,7 +92,8 @@
n = (len + hashlen - 1) / hashlen;
for (i = 1; i <= n; i++) {
WPA_PUT_BE16(ibuf, i);
- hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+ if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
+ return -1;
clen = left > hashlen ? hashlen : left;
os_memcpy(opos, hash, clen);
opos += clen;
@@ -534,8 +535,7 @@
break;
#ifdef EAP_GPSK_SHA256
case EAP_GPSK_CIPHER_SHA256:
- hmac_sha256(sk, sk_len, data, len, mic);
- ret = 0;
+ ret = hmac_sha256(sk, sk_len, data, len, mic);
break;
#endif /* EAP_GPSK_SHA256 */
default:
@@ -545,5 +545,8 @@
break;
}
+ if (ret)
+ wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
+
return ret;
}
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 4d27623..67f8f70 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -115,6 +115,26 @@
case 26:
nid = NID_secp224r1;
break;
+#ifdef NID_brainpoolP224r1
+ case 27:
+ nid = NID_brainpoolP224r1;
+ break;
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+ case 28:
+ nid = NID_brainpoolP256r1;
+ break;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+ case 29:
+ nid = NID_brainpoolP384r1;
+ break;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+ case 30:
+ nid = NID_brainpoolP512r1;
+ break;
+#endif /* NID_brainpoolP512r1 */
default:
wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
return -1;
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index c22e43e..8819541 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -121,7 +121,7 @@
attr->next_tmpid_len = len;
break;
case EAP_SAKE_AT_MSK_LIFE:
- wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+ wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MSK_LIFE");
if (len != 4) {
wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
"AT_MSK_LIFE payload length %d", len);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 1dbe003..28d5116 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -188,6 +188,14 @@
*/
eapol_set_bool(sm, EAPOL_eapResp, FALSE);
eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+ /*
+ * RFC 4137 does not reset ignore here, but since it is possible for
+ * some method code paths to end up not setting ignore=FALSE, clear the
+ * value here to avoid issues if a previous authentication attempt
+ * failed with ignore=TRUE being left behind in the last
+ * m.check(eapReqData) operation.
+ */
+ sm->ignore = 0;
sm->num_rounds = 0;
sm->prev_failure = 0;
sm->expected_failure = 0;
@@ -584,7 +592,7 @@
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,
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
EAP_CODE_INITIATE, hdr->identifier);
if (msg == NULL)
@@ -708,7 +716,7 @@
wpabuf_free(sm->lastRespData);
if (sm->eapRespData) {
if (sm->workaround)
- os_memcpy(sm->last_md5, sm->req_md5, 16);
+ os_memcpy(sm->last_sha1, sm->req_sha1, 20);
sm->lastId = sm->reqId;
sm->lastRespData = wpabuf_dup(sm->eapRespData);
eapol_set_bool(sm, EAPOL_eapResp, TRUE);
@@ -914,12 +922,12 @@
duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
if (sm->workaround && duplicate &&
- os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+ os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) {
/*
* RFC 4137 uses (reqId == lastId) as the only verification for
* duplicate EAP requests. However, this misses cases where the
* AS is incorrectly using the same id again; and
- * unfortunately, such implementations exist. Use MD5 hash as
+ * unfortunately, such implementations exist. Use SHA1 hash as
* an extra verification for the packets being duplicate to
* workaround these issues.
*/
@@ -1765,7 +1773,7 @@
if (sm->workaround) {
const u8 *addr[1];
addr[0] = wpabuf_head(req);
- md5_vector(1, addr, &plen, sm->req_md5);
+ sha1_vector(1, addr, &plen, sm->req_sha1);
}
switch (hdr->code) {
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index dfbda56..1fd4970 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -452,6 +452,7 @@
/* DHComponent_P = Encr(key, y_p) */
rpos = wpabuf_put(resp, data->sess.dhcomp_len);
if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
+ wpabuf_free(resp);
wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
os_memset(key, 0, sizeof(key));
return eap_eke_build_fail(data, ret, id,
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index f636e74..833dcb6 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1096,7 +1096,7 @@
/* Parse TLVs from the decrypted Phase 2 data */
pos = wpabuf_mhead(decrypted);
end = pos + wpabuf_len(decrypted);
- while (pos + 4 < end) {
+ while (end - pos > 4) {
mandatory = pos[0] & 0x80;
tlv_type = WPA_GET_BE16(pos) & 0x3fff;
pos += 2;
@@ -1572,6 +1572,13 @@
EAP_TYPE_FAST,
data->fast_version, id, &msg,
&resp);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return resp;
+ }
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
char cipher[80];
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 89e604e..c0986b3 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -709,7 +709,7 @@
pos = pac->pac_info;
end = pos + pac->pac_info_len;
- while (pos + 4 < end) {
+ while (end - pos > 4) {
type = WPA_GET_BE16(pos);
pos += 2;
len = WPA_GET_BE16(pos);
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 5f8b5fa..99b44da 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -338,9 +338,9 @@
Boolean rxResp /* LEAP only */;
Boolean leap_done;
Boolean peap_done;
- u8 req_md5[16]; /* MD5() of the current EAP packet */
- u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
- * in duplicate request detection. */
+ u8 req_sha1[20]; /* SHA1() of the current EAP packet */
+ u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
+ * in duplicate request detection. */
void *msg_ctx;
void *scard_ctx;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 9e486e7..6acf1e8 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -511,6 +511,11 @@
struct eap_sm *sm, struct eap_mschapv2_data *data,
struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
{
+#ifdef CONFIG_NO_RC4
+ wpa_printf(MSG_ERROR,
+ "EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
+ return NULL;
+#else /* CONFIG_NO_RC4 */
struct wpabuf *resp;
int ms_len;
const u8 *username, *password, *new_password;
@@ -628,6 +633,7 @@
fail:
wpabuf_free(resp);
return NULL;
+#endif /* CONFIG_NO_RC4 */
}
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 4f68fce..98a48a6 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1011,6 +1011,13 @@
data->peap_version, id, &msg,
&resp);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return resp;
+ }
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
char *label;
wpa_printf(MSG_DEBUG,
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index cd7a2ab..892b590 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -288,6 +288,12 @@
}
if (id->prep == EAP_PWD_PREP_MS) {
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR,
+ "EAP-PWD (peer): MS password hash not supported in FIPS mode");
+ eap_pwd_state(data, FAILURE);
+ return;
+#else /* CONFIG_FIPS */
if (data->password_hash) {
res = hash_nt_password_hash(data->password, pwhashhash);
} else {
@@ -307,6 +313,7 @@
password = pwhashhash;
password_len = sizeof(pwhashhash);
+#endif /* CONFIG_FIPS */
} else {
password = data->password;
password_len = data->password_len;
@@ -767,7 +774,8 @@
wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
fin:
- bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+ if (data->grp)
+ bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
BN_clear_free(x);
BN_clear_free(y);
if (data->outbuf == NULL) {
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 99a2816..cbf7461 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1135,7 +1135,7 @@
if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
"for NONCE_MT");
- os_free(data);
+ eap_sim_deinit(sm, data);
return NULL;
}
data->num_id_req = 0;
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index d81b1cf..66a027a 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -156,20 +156,6 @@
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
- if (res == -1) {
- struct eap_peer_config *config = eap_get_config(sm);
- if (config) {
- /*
- * The TLS handshake failed. So better forget the old
- * PIN. It may be wrong, we cannot be sure but trying
- * the wrong one again might block it on the card--so
- * better ask the user again.
- */
- os_free(config->pin);
- config->pin = NULL;
- }
- }
-
if (resp) {
/*
* This is likely an alert message, so send it instead of just
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index fef7fdb..af2b754 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -68,6 +68,10 @@
params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
if (os_strstr(txt, "tls_disable_session_ticket=0"))
params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+ if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
+ params->flags |= TLS_CONN_DISABLE_TLSv1_0;
+ if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
+ params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
params->flags |= TLS_CONN_DISABLE_TLSv1_1;
if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
@@ -343,10 +347,10 @@
struct eap_ssl_data *data, u8 eap_type,
size_t *len)
{
- struct tls_keys keys;
+ struct tls_random keys;
u8 *out;
- if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
return NULL;
if (keys.client_random == NULL || keys.server_random == NULL)
@@ -678,12 +682,18 @@
if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
/* TLS processing has failed - return error */
wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
- "report error");
+ "report error (len=%u)",
+ (unsigned int) wpabuf_len(data->tls_out));
ret = -1;
/* TODO: clean pin if engine used? */
+ if (wpabuf_len(data->tls_out) == 0) {
+ wpabuf_free(data->tls_out);
+ data->tls_out = NULL;
+ return -1;
+ }
}
- if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+ if (wpabuf_len(data->tls_out) == 0) {
/*
* TLS negotiation should now be complete since all other cases
* needing more data should have been caught above based on
@@ -749,20 +759,24 @@
int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
char *buf, size_t buflen, int verbose)
{
- char name[128];
+ char version[20], name[128];
int len = 0, ret;
- 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"
- "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;
- }
+ if (tls_get_version(data->ssl_ctx, data->conn, version,
+ sizeof(version)) < 0)
+ version[0] = '\0';
+ if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
+ name[0] = '\0';
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "eap_tls_version=%s\n"
+ "EAP TLS cipher=%s\n"
+ "tls_session_reused=%d\n",
+ version, name,
+ tls_connection_resumed(data->ssl_ctx, data->conn));
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
return len;
}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 25e3cba..b186c91 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -175,7 +175,8 @@
}
avp->avp_code = host_to_be32(avp_code);
- avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
+ avp->avp_length = host_to_be32(((u32) flags << 24) |
+ (u32) (hdrlen + len));
return avphdr + hdrlen;
}
@@ -253,11 +254,13 @@
}
+#ifndef CONFIG_FIPS
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
}
+#endif /* CONFIG_FIPS */
static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
@@ -428,6 +431,10 @@
struct eap_method_ret *ret,
struct wpabuf **resp)
{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
#ifdef EAP_MSCHAPv2
struct wpabuf *msg;
u8 *buf, *pos, *challenge, *peer_challenge;
@@ -510,6 +517,7 @@
wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
return -1;
#endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
}
@@ -518,6 +526,10 @@
struct eap_method_ret *ret,
struct wpabuf **resp)
{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
struct wpabuf *msg;
u8 *buf, *pos, *challenge;
const u8 *identity, *password;
@@ -592,6 +604,7 @@
ret->decision = DECISION_COND_SUCC;
return 0;
+#endif /* CONFIG_FIPS */
}
@@ -654,6 +667,10 @@
struct eap_method_ret *ret,
struct wpabuf **resp)
{
+#ifdef CONFIG_FIPS
+ wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
+ return -1;
+#else /* CONFIG_FIPS */
struct wpabuf *msg;
u8 *buf, *pos, *challenge;
const u8 *identity, *password;
@@ -722,6 +739,7 @@
ret->decision = DECISION_COND_SUCC;
return 0;
+#endif /* CONFIG_FIPS */
}
@@ -1393,6 +1411,12 @@
res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
data->ttls_version, identifier,
in_data, out_data);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return -1;
+ }
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 7ce0a53..7ac99c7 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -557,6 +557,9 @@
if (data->out_buf == NULL) {
wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
"message from WPS");
+ eap_wsc_state(data, FAIL);
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
return NULL;
}
data->out_used = 0;
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index 55ab72a..ca6502e 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -128,7 +128,7 @@
t = (const struct ikev2_transform *) pos;
transform_len = WPA_GET_BE16(t->transform_length);
- if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+ if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
transform_len);
return -1;
@@ -248,7 +248,7 @@
ppos = (const u8 *) (p + 1);
pend = pos + proposal_len;
- if (ppos + p->spi_size > pend) {
+ if (p->spi_size > pend - ppos) {
wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
"in proposal");
return -1;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 09be581..69eaab8 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -131,6 +131,7 @@
const u8 *server_id;
size_t server_id_len;
int erp;
+ unsigned int tls_session_lifetime;
#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 978c879..c90443d 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -210,6 +210,7 @@
Boolean initiate_reauth_start_sent;
Boolean try_initiate_reauth;
int erp;
+ unsigned int tls_session_lifetime;
#ifdef CONFIG_TESTING_OPTIONS
u32 tls_test_flags;
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 6651229..84ecafc 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -96,7 +96,8 @@
plen += 2 + domain_len;
}
- msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
+ msg = eap_msg_alloc(EAP_VENDOR_IETF,
+ (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
EAP_CODE_INITIATE, id);
if (msg == NULL)
return NULL;
@@ -714,8 +715,8 @@
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);
+ msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+ plen, EAP_CODE_FINISH, id);
if (msg == NULL)
return;
wpabuf_put_u8(msg, flags);
@@ -799,7 +800,7 @@
sm->rxInitiate = FALSE;
- pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+ pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
sm->eap_if.eapRespData, &len);
if (pos == NULL) {
wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
@@ -1246,6 +1247,17 @@
break;
}
SM_ENTER(EAP, SEND_REQUEST);
+ if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
+ /*
+ * This transition is not mentioned in RFC 4137, but it
+ * is needed to handle cleanly a case where EAP method
+ * buildReq fails.
+ */
+ wpa_printf(MSG_DEBUG,
+ "EAP: Method did not return a request");
+ SM_ENTER(EAP, FAILURE);
+ break;
+ }
break;
case EAP_METHOD_RESPONSE:
/*
@@ -1853,6 +1865,7 @@
sm->server_id = conf->server_id;
sm->server_id_len = conf->server_id_len;
sm->erp = conf->erp;
+ sm->tls_session_lifetime = conf->tls_session_lifetime;
#ifdef CONFIG_TESTING_OPTIONS
sm->tls_test_flags = conf->tls_test_flags;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 6745100..47210e1 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -180,42 +180,47 @@
buf, end - buf);
pos = buf;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ u8 id, elen;
+
+ id = *pos++;
+ elen = *pos++;
+ if (elen > end - pos)
break;
- switch (*pos) {
+ switch (id) {
case PAC_OPAQUE_TYPE_PAD:
goto done;
case PAC_OPAQUE_TYPE_KEY:
- if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
- wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
- "PAC-Key length %d", pos[1]);
+ if (elen != EAP_FAST_PAC_KEY_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-FAST: Invalid PAC-Key length %d",
+ elen);
os_free(buf);
return -1;
}
- pac_key = pos + 2;
+ pac_key = pos;
wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from "
"decrypted PAC-Opaque",
pac_key, EAP_FAST_PAC_KEY_LEN);
break;
case PAC_OPAQUE_TYPE_LIFETIME:
- if (pos[1] != 4) {
+ if (elen != 4) {
wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
"PAC-Key lifetime length %d",
- pos[1]);
+ elen);
os_free(buf);
return -1;
}
- lifetime = WPA_GET_BE32(pos + 2);
+ lifetime = WPA_GET_BE32(pos);
break;
case PAC_OPAQUE_TYPE_IDENTITY:
- identity = pos + 2;
- identity_len = pos[1];
+ identity = pos;
+ identity_len = elen;
break;
}
- pos += 2 + pos[1];
+ pos += elen;
}
done:
@@ -428,7 +433,7 @@
}
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) {
wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
eap_fast_reset(sm, data);
return NULL;
@@ -1134,7 +1139,7 @@
pos = wpabuf_mhead(data);
end = pos + wpabuf_len(data);
- while (pos + 4 < end) {
+ while (end - pos > 4) {
mandatory = pos[0] & 0x80;
tlv_type = WPA_GET_BE16(pos) & 0x3fff;
pos += 2;
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 3848f30..51062b0 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -95,6 +95,37 @@
eap_peap_state_txt(data->state),
eap_peap_state_txt(state));
data->state = state;
+ if (state == FAILURE || state == FAILURE_REQ)
+ tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_peap_valid_session(struct eap_sm *sm,
+ struct eap_peap_data *data)
+{
+ struct wpabuf *buf;
+
+ if (!sm->tls_session_lifetime ||
+ tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+ return;
+
+ buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+ if (!buf)
+ return;
+ wpabuf_put_u8(buf, EAP_TYPE_PEAP);
+ if (sm->identity) {
+ u8 id_len;
+
+ if (sm->identity_len <= 255)
+ id_len = sm->identity_len;
+ else
+ id_len = 255;
+ wpabuf_put_u8(buf, id_len);
+ wpabuf_put_data(buf, sm->identity, id_len);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ }
+ tls_connection_set_success_data(data->ssl.conn, buf);
}
@@ -151,7 +182,7 @@
data->state = START;
data->crypto_binding = OPTIONAL_BINDING;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) {
wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
eap_peap_reset(sm, data);
return NULL;
@@ -708,10 +739,12 @@
if (status == EAP_TLV_RESULT_SUCCESS) {
wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
"- requested %s", requested);
- if (data->tlv_request == TLV_REQ_SUCCESS)
+ if (data->tlv_request == TLV_REQ_SUCCESS) {
eap_peap_state(data, SUCCESS);
- else
+ eap_peap_valid_session(sm, data);
+ } else {
eap_peap_state(data, FAILURE);
+ }
} else if (status == EAP_TLV_RESULT_FAILURE) {
wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
@@ -1094,6 +1127,7 @@
wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
if (data->state == SUCCESS_REQ) {
eap_peap_state(data, SUCCESS);
+ eap_peap_valid_session(sm, data);
}
break;
case EAP_CODE_FAILURE:
@@ -1159,6 +1193,7 @@
break;
case SUCCESS_REQ:
eap_peap_state(data, SUCCESS);
+ eap_peap_valid_session(sm, data);
break;
case FAILURE_REQ:
eap_peap_state(data, FAILURE);
@@ -1175,10 +1210,65 @@
struct wpabuf *respData)
{
struct eap_peap_data *data = priv;
+ const struct wpabuf *buf;
+ const u8 *pos;
+ u8 id_len;
+
if (eap_server_tls_process(sm, &data->ssl, respData, data,
EAP_TYPE_PEAP, eap_peap_process_version,
- eap_peap_process_msg) < 0)
+ eap_peap_process_msg) < 0) {
eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ if (data->state == SUCCESS ||
+ !tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+ !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+ return;
+
+ buf = tls_connection_get_success_data(data->ssl.conn);
+ if (!buf || wpabuf_len(buf) < 2) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: No success data in resumed session - reject attempt");
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ pos = wpabuf_head(buf);
+ if (*pos != EAP_TYPE_PEAP) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt",
+ *pos);
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ pos++;
+ id_len = *pos++;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session",
+ pos, id_len);
+ os_free(sm->identity);
+ sm->identity = os_malloc(id_len ? id_len : 1);
+ if (!sm->identity) {
+ sm->identity_len = 0;
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ os_memcpy(sm->identity, pos, id_len);
+ sm->identity_len = id_len;
+
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database",
+ sm->identity, sm->identity_len);
+ eap_peap_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-PEAP: Resuming previous session - skip Phase2");
+ eap_peap_state(data, SUCCESS_REQ);
+ tls_connection_set_success_data_resumed(data->ssl.conn);
}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 58cfe8a..bd18a4b 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -48,6 +48,23 @@
eap_tls_state_txt(data->state),
eap_tls_state_txt(state));
data->state = state;
+ if (state == FAILURE)
+ tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
+{
+ struct wpabuf *buf;
+
+ if (!sm->tls_session_lifetime)
+ return;
+
+ buf = wpabuf_alloc(1);
+ if (!buf)
+ return;
+ wpabuf_put_u8(buf, data->eap_type);
+ tls_connection_set_success_data(data->ssl.conn, buf);
}
@@ -60,7 +77,7 @@
return NULL;
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
@@ -82,7 +99,7 @@
return NULL;
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
@@ -104,7 +121,8 @@
return NULL;
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
+ EAP_WFA_UNAUTH_TLS_TYPE)) {
wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
eap_tls_reset(sm, data);
return NULL;
@@ -183,6 +201,7 @@
* fragments waiting to be sent out. */
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
eap_tls_state(data, SUCCESS);
+ eap_tls_valid_session(sm, data);
}
return res;
@@ -234,10 +253,41 @@
struct wpabuf *respData)
{
struct eap_tls_data *data = priv;
+ const struct wpabuf *buf;
+ const u8 *pos;
+
if (eap_server_tls_process(sm, &data->ssl, respData, data,
data->eap_type, NULL, eap_tls_process_msg) <
- 0)
+ 0) {
eap_tls_state(data, FAILURE);
+ return;
+ }
+
+ if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+ !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+ return;
+
+ buf = tls_connection_get_success_data(data->ssl.conn);
+ if (!buf || wpabuf_len(buf) < 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TLS: No success data in resumed session - reject attempt");
+ eap_tls_state(data, FAILURE);
+ return;
+ }
+
+ pos = wpabuf_head(buf);
+ if (*pos != data->eap_type) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
+ *pos);
+ eap_tls_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TLS: Resuming previous session");
+ eap_tls_state(data, SUCCESS);
+ tls_connection_set_success_data_resumed(data->ssl.conn);
}
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 23498c9..05677b7 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -44,8 +44,11 @@
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- int verify_peer)
+ int verify_peer, int eap_type)
{
+ u8 session_ctx[8];
+ unsigned int flags = 0;
+
if (sm->ssl_ctx == NULL) {
wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
return -1;
@@ -68,7 +71,13 @@
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_TLS_INTERNAL */
- if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
+ if (eap_type != EAP_TYPE_FAST)
+ flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+ os_memcpy(session_ctx, "hostapd", 7);
+ session_ctx[7] = (u8) eap_type;
+ if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
+ flags, session_ctx,
+ sizeof(session_ctx))) {
wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
"of TLS peer certificate");
tls_connection_deinit(sm->ssl_ctx, data->conn);
@@ -133,10 +142,10 @@
struct eap_ssl_data *data, u8 eap_type,
size_t *len)
{
- struct tls_keys keys;
+ struct tls_random keys;
u8 *out;
- if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+ if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
return NULL;
if (keys.client_random == NULL || keys.server_random == NULL)
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 31c67e8..53ffa1e 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -71,6 +71,36 @@
eap_ttls_state_txt(data->state),
eap_ttls_state_txt(state));
data->state = state;
+ if (state == FAILURE)
+ tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_ttls_valid_session(struct eap_sm *sm,
+ struct eap_ttls_data *data)
+{
+ struct wpabuf *buf;
+
+ if (!sm->tls_session_lifetime)
+ return;
+
+ buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+ if (!buf)
+ return;
+ wpabuf_put_u8(buf, EAP_TYPE_TTLS);
+ if (sm->identity) {
+ u8 id_len;
+
+ if (sm->identity_len <= 255)
+ id_len = sm->identity_len;
+ else
+ id_len = 255;
+ wpabuf_put_u8(buf, id_len);
+ wpabuf_put_data(buf, sm->identity, id_len);
+ } else {
+ wpabuf_put_u8(buf, 0);
+ }
+ tls_connection_set_success_data(data->ssl.conn, buf);
}
@@ -317,7 +347,7 @@
data->ttls_version = EAP_TTLS_VERSION;
data->state = START;
- if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+ if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
eap_ttls_reset(sm, data);
return NULL;
@@ -518,6 +548,7 @@
wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
eap_ttls_state(data, SUCCESS);
+ eap_ttls_valid_session(sm, data);
}
@@ -576,6 +607,7 @@
0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
eap_ttls_state(data, SUCCESS);
+ eap_ttls_valid_session(sm, data);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
eap_ttls_state(data, FAILURE);
@@ -643,6 +675,7 @@
if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
eap_ttls_state(data, SUCCESS);
+ eap_ttls_valid_session(sm, data);
} else {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
@@ -906,6 +939,7 @@
break;
case PHASE2_METHOD:
eap_ttls_state(data, SUCCESS);
+ eap_ttls_valid_session(sm, data);
break;
case FAILURE:
break;
@@ -1129,6 +1163,7 @@
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged response");
eap_ttls_state(data, SUCCESS);
+ eap_ttls_valid_session(sm, data);
} else if (!data->mschapv2_resp_ok) {
wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
"acknowledged error");
@@ -1155,10 +1190,64 @@
struct wpabuf *respData)
{
struct eap_ttls_data *data = priv;
+ const struct wpabuf *buf;
+ const u8 *pos;
+ u8 id_len;
+
if (eap_server_tls_process(sm, &data->ssl, respData, data,
EAP_TYPE_TTLS, eap_ttls_process_version,
- eap_ttls_process_msg) < 0)
+ eap_ttls_process_msg) < 0) {
eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+ !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+ return;
+
+ buf = tls_connection_get_success_data(data->ssl.conn);
+ if (!buf || wpabuf_len(buf) < 1) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TTLS: No success data in resumed session - reject attempt");
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ pos = wpabuf_head(buf);
+ if (*pos != EAP_TYPE_TTLS) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt",
+ *pos);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ pos++;
+ id_len = *pos++;
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session",
+ pos, id_len);
+ os_free(sm->identity);
+ sm->identity = os_malloc(id_len ? id_len : 1);
+ if (!sm->identity) {
+ sm->identity_len = 0;
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ os_memcpy(sm->identity, pos, id_len);
+ sm->identity_len = id_len;
+
+ if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+ wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database",
+ sm->identity, sm->identity_len);
+ eap_ttls_state(data, FAILURE);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "EAP-TTLS: Resuming previous session - skip Phase2");
+ eap_ttls_state(data, SUCCESS);
+ tls_connection_set_success_data_resumed(data->ssl.conn);
}
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index acf5435..d84c3d2 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -66,6 +66,7 @@
struct eap_sim_pseudonym *pseudonyms;
struct eap_sim_reauth *reauths;
struct eap_sim_db_pending *pending;
+ unsigned int eap_sim_db_timeout;
#ifdef CONFIG_SQLITE
sqlite3 *sqlite_db;
char db_tmp_identity[100];
@@ -76,6 +77,10 @@
};
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx);
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx);
+
+
#ifdef CONFIG_SQLITE
static int db_table_exists(sqlite3 *db, const char *name)
@@ -397,6 +402,57 @@
}
+static void eap_sim_db_free_pending(struct eap_sim_db_data *data,
+ struct eap_sim_db_pending *entry)
+{
+ eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry);
+ eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry);
+ os_free(entry);
+}
+
+
+static void eap_sim_db_del_pending(struct eap_sim_db_data *data,
+ struct eap_sim_db_pending *entry)
+{
+ struct eap_sim_db_pending **pp = &data->pending;
+
+ while (*pp != NULL) {
+ if (*pp == entry) {
+ *pp = entry->next;
+ eap_sim_db_free_pending(data, entry);
+ return;
+ }
+ pp = &(*pp)->next;
+ }
+}
+
+
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx)
+{
+ struct eap_sim_db_data *data = eloop_ctx;
+ struct eap_sim_db_pending *entry = user_ctx;
+
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry);
+ eap_sim_db_del_pending(data, entry);
+}
+
+
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx)
+{
+ struct eap_sim_db_data *data = eloop_ctx;
+ struct eap_sim_db_pending *entry = user_ctx;
+
+ /*
+ * Report failure and allow some time for EAP server to process it
+ * before deleting the query.
+ */
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry);
+ entry->state = FAILURE;
+ data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+ eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry);
+}
+
+
static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
const char *imsi, char *buf)
{
@@ -472,7 +528,7 @@
parse_fail:
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
}
@@ -563,7 +619,7 @@
parse_fail:
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
}
@@ -690,12 +746,13 @@
/**
* eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
* @config: Configuration data (e.g., file name)
+ * @db_timeout: Database lookup timeout
* @get_complete_cb: Callback function for reporting availability of triplets
* @ctx: Context pointer for get_complete_cb
* Returns: Pointer to a private data structure or %NULL on failure
*/
struct eap_sim_db_data *
-eap_sim_db_init(const char *config,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
void (*get_complete_cb)(void *ctx, void *session_ctx),
void *ctx)
{
@@ -709,6 +766,7 @@
data->sock = -1;
data->get_complete_cb = get_complete_cb;
data->ctx = ctx;
+ data->eap_sim_db_timeout = db_timeout;
data->fname = os_strdup(config);
if (data->fname == NULL)
goto fail;
@@ -796,7 +854,7 @@
while (pending) {
prev_pending = pending;
pending = pending->next;
- os_free(prev_pending);
+ eap_sim_db_free_pending(data, prev_pending);
}
os_free(data);
@@ -833,11 +891,11 @@
}
-static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data,
+ struct eap_sim_db_pending *entry)
{
- /* TODO: add limit for maximum length for pending list; remove latest
- * (i.e., last) entry from the list if the limit is reached; could also
- * use timeout to expire pending entries */
+ eloop_register_timeout(data->eap_sim_db_timeout, 0,
+ eap_sim_db_query_timeout, data, entry);
}
@@ -891,7 +949,7 @@
if (entry->state == FAILURE) {
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
"failure");
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
return EAP_SIM_DB_FAILURE;
}
@@ -911,7 +969,7 @@
os_memcpy(sres, entry->u.sim.sres,
num_chal * EAP_SIM_SRES_LEN);
os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
return num_chal;
}
@@ -945,7 +1003,8 @@
entry->cb_session_ctx = cb_session_ctx;
entry->state = PENDING;
eap_sim_db_add_pending(data, entry);
- eap_sim_db_expire_pending(data);
+ eap_sim_db_expire_pending(data, entry);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
return EAP_SIM_DB_PENDING;
}
@@ -1356,7 +1415,7 @@
entry = eap_sim_db_get_pending(data, imsi, 1);
if (entry) {
if (entry->state == FAILURE) {
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
return EAP_SIM_DB_FAILURE;
}
@@ -1375,7 +1434,7 @@
os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
*res_len = entry->u.aka.res_len;
- os_free(entry);
+ eap_sim_db_free_pending(data, entry);
return 0;
}
@@ -1406,7 +1465,8 @@
entry->cb_session_ctx = cb_session_ctx;
entry->state = PENDING;
eap_sim_db_add_pending(data, entry);
- eap_sim_db_expire_pending(data);
+ eap_sim_db_expire_pending(data, entry);
+ wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
return EAP_SIM_DB_PENDING;
}
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index 53a1a7c..ca900b9 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -31,7 +31,7 @@
struct eap_sim_db_data;
struct eap_sim_db_data *
-eap_sim_db_init(const char *config,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
void (*get_complete_cb)(void *ctx, void *session_ctx),
void *ctx);
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index ddf90b8..dc943eb 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -70,7 +70,7 @@
struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
u8 code, u8 identifier);
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
- int verify_peer);
+ int verify_peer, int eap_type);
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);
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 632598f..5385cd8 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -133,7 +133,7 @@
t = (const struct ikev2_transform *) pos;
transform_len = WPA_GET_BE16(t->transform_length);
- if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+ if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
transform_len);
return -1;
@@ -221,7 +221,7 @@
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;
@@ -256,7 +256,7 @@
ppos = (const u8 *) (p + 1);
pend = pos + proposal_len;
- if (ppos + p->spi_size > pend) {
+ if (p->spi_size > pend - ppos) {
wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
"in proposal");
return -1;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 3b0c2e4..ff33d28 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-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -198,6 +198,18 @@
{
SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
sm->portMode = Auto;
+
+ /*
+ * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but
+ * it looks like this would be logical thing to do here since the
+ * EAPOL-Key exchange is not possible in this state. It is possible to
+ * get here on disconnection event without advancing to the
+ * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN
+ * authenticator state machine runs and that may advance from
+ * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the
+ * last association. This can be avoided by clearing keyRun here.
+ */
+ sm->keyRun = FALSE;
}
@@ -835,6 +847,7 @@
eap_conf.server_id = eapol->conf.server_id;
eap_conf.server_id_len = eapol->conf.server_id_len;
eap_conf.erp = eapol->conf.erp;
+ eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
if (sm->eap == NULL) {
eapol_auth_free(sm);
@@ -1080,6 +1093,87 @@
}
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for "
+ MACSTR, MAC2STR(sm->addr));
+ sm->reAuthenticate = TRUE;
+ eapol_auth_step(sm);
+}
+
+
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+ const char *value)
+{
+ wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for "
+ MACSTR " - param=%s value=%s",
+ MAC2STR(sm->addr), param, value);
+
+ if (os_strcasecmp(param, "AdminControlledDirections") == 0) {
+ if (os_strcmp(value, "Both") == 0)
+ sm->adminControlledDirections = Both;
+ else if (os_strcmp(value, "In") == 0)
+ sm->adminControlledDirections = In;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "AdminControlledPortControl") == 0) {
+ if (os_strcmp(value, "ForceAuthorized") == 0)
+ sm->portControl = ForceAuthorized;
+ else if (os_strcmp(value, "ForceUnauthorized") == 0)
+ sm->portControl = ForceUnauthorized;
+ else if (os_strcmp(value, "Auto") == 0)
+ sm->portControl = Auto;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "quietPeriod") == 0) {
+ sm->quietPeriod = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "serverTimeout") == 0) {
+ sm->serverTimeout = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "reAuthPeriod") == 0) {
+ sm->reAuthPeriod = atoi(value);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "reAuthEnabled") == 0) {
+ if (os_strcmp(value, "TRUE") == 0)
+ sm->reAuthEnabled = TRUE;
+ else if (os_strcmp(value, "FALSE") == 0)
+ sm->reAuthEnabled = FALSE;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) {
+ if (os_strcmp(value, "TRUE") == 0)
+ sm->keyTxEnabled = TRUE;
+ else if (os_strcmp(value, "FALSE") == 0)
+ sm->keyTxEnabled = FALSE;
+ else
+ return -1;
+ eapol_auth_step(sm);
+ return 0;
+ }
+
+ return -1;
+}
+
+
static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
struct eapol_auth_config *src)
{
@@ -1148,6 +1242,7 @@
}
dst->erp_send_reauth_start = src->erp_send_reauth_start;
dst->erp = src->erp;
+ dst->tls_session_lifetime = src->tls_session_lifetime;
return 0;
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index ebed19a..e1974e4 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -1,6 +1,6 @@
/*
* IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -27,6 +27,7 @@
int erp_send_reauth_start;
char *erp_domain; /* a copy of this will be allocated */
int erp; /* Whether ERP is enabled on authentication server */
+ unsigned int tls_session_lifetime;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@@ -94,5 +95,8 @@
int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
size_t buflen);
int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm);
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+ const char *value);
#endif /* EAPOL_AUTH_SM_H */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index a29b49c..aa3e117 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -162,12 +162,6 @@
struct radius_class_data radius_class;
struct wpabuf *radius_cui; /* Chargeable-User-Identity */
- /* Keys for encrypting and signing EAPOL-Key frames */
- u8 *eapol_key_sign;
- size_t eapol_key_sign_len;
- u8 *eapol_key_crypt;
- size_t eapol_key_crypt_len;
-
struct eap_sm *eap;
Boolean initializing; /* in process of initializing state machines */
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index eb8c5bb..65460fc 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -244,7 +244,8 @@
SM_STATE(SUPP_PAE, CONNECTING)
{
- int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
+ int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
+ sm->SUPP_PAE_state == SUPP_PAE_HELD;
SM_ENTRY(SUPP_PAE, CONNECTING);
if (sm->eapTriggerStart)
@@ -313,6 +314,16 @@
{
SM_ENTRY(SUPP_PAE, RESTART);
sm->eapRestart = TRUE;
+ if (sm->altAccept) {
+ /*
+ * Prevent EAP peer state machine from failing due to prior
+ * external EAP success notification (altSuccess=TRUE in the
+ * IDLE state could result in a transition to the FAILURE state.
+ */
+ wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE");
+ sm->eapSuccess = FALSE;
+ sm->altAccept = FALSE;
+ }
}
@@ -653,7 +664,9 @@
struct ieee802_1x_eapol_key *key;
struct eap_key_data keydata;
u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+#ifndef CONFIG_NO_RC4
u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+#endif /* CONFIG_NO_RC4 */
int key_len, res, sign_key_len, encr_key_len;
u16 rx_key_length;
size_t plen;
@@ -747,6 +760,13 @@
return;
}
if (key_len == rx_key_length) {
+#ifdef CONFIG_NO_RC4
+ if (encr_key_len) {
+ /* otherwise unused */
+ }
+ wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
+ return;
+#else /* CONFIG_NO_RC4 */
os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
encr_key_len);
@@ -755,6 +775,7 @@
datakey, key_len);
wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
datakey, key_len);
+#endif /* CONFIG_NO_RC4 */
} else if (key_len == 0) {
/*
* IEEE 802.1X-2004 specifies that least significant Key Length
diff --git a/src/fst/Makefile b/src/fst/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/fst/Makefile
@@ -0,0 +1,8 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ rm -f *~ *.o *.d
+
+install:
+ @echo Nothing to be made.
diff --git a/src/fst/fst.c b/src/fst/fst.c
new file mode 100644
index 0000000..40430e2
--- /dev/null
+++ b/src/fst/fst.c
@@ -0,0 +1,225 @@
+/*
+ * FST module implementation
+ * 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 "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+struct dl_list fst_global_ctrls_list;
+
+
+static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
+ Boolean connected,
+ const u8 *peer_addr)
+{
+ union fst_event_extra extra;
+
+ extra.peer_state.connected = connected;
+ os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
+ sizeof(extra.peer_state.ifname));
+ os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
+
+ foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
+ iface, NULL, &extra);
+}
+
+
+struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
+ const struct fst_wpa_obj *iface_obj,
+ const struct fst_iface_cfg *cfg)
+{
+ struct fst_group *g;
+ struct fst_group *group = NULL;
+ struct fst_iface *iface = NULL;
+ Boolean new_group = FALSE;
+
+ WPA_ASSERT(ifname != NULL);
+ WPA_ASSERT(iface_obj != NULL);
+ WPA_ASSERT(cfg != NULL);
+
+ foreach_fst_group(g) {
+ if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
+ group = g;
+ break;
+ }
+ }
+
+ if (!group) {
+ group = fst_group_create(cfg->group_id);
+ if (!group) {
+ fst_printf(MSG_ERROR, "%s: FST group cannot be created",
+ cfg->group_id);
+ return NULL;
+ }
+ new_group = TRUE;
+ }
+
+ iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
+ if (!iface) {
+ fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
+ ifname);
+ if (new_group)
+ fst_group_delete(group);
+ return NULL;
+ }
+
+ fst_group_attach_iface(group, iface);
+ fst_group_update_ie(group);
+
+ foreach_fst_ctrl_call(on_iface_added, iface);
+
+ fst_printf_iface(iface, MSG_DEBUG,
+ "iface attached to group %s (prio=%d, llt=%d)",
+ cfg->group_id, cfg->priority, cfg->llt);
+
+ return iface;
+}
+
+
+void fst_detach(struct fst_iface *iface)
+{
+ struct fst_group *group = fst_iface_get_group(iface);
+
+ fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
+ fst_group_get_id(group));
+ fst_session_global_on_iface_detached(iface);
+ foreach_fst_ctrl_call(on_iface_removed, iface);
+ fst_group_detach_iface(group, iface);
+ fst_iface_delete(iface);
+ fst_group_update_ie(group);
+ fst_group_delete_if_empty(group);
+}
+
+
+int fst_global_init(void)
+{
+ dl_list_init(&fst_global_groups_list);
+ dl_list_init(&fst_global_ctrls_list);
+ fst_session_global_init();
+ return 0;
+}
+
+
+void fst_global_deinit(void)
+{
+ struct fst_group *group;
+ struct fst_ctrl_handle *h;
+
+ fst_session_global_deinit();
+ while ((group = fst_first_group()) != NULL)
+ fst_group_delete(group);
+ while ((h = dl_list_first(&fst_global_ctrls_list,
+ struct fst_ctrl_handle,
+ global_ctrls_lentry)))
+ fst_global_del_ctrl(h);
+}
+
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
+{
+ struct fst_ctrl_handle *h;
+
+ if (!ctrl)
+ return NULL;
+
+ h = os_zalloc(sizeof(*h));
+ if (!h)
+ return NULL;
+
+ if (ctrl->init && ctrl->init()) {
+ os_free(h);
+ return NULL;
+ }
+
+ h->ctrl = *ctrl;
+ dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
+
+ return h;
+}
+
+
+void fst_global_del_ctrl(struct fst_ctrl_handle *h)
+{
+ dl_list_del(&h->global_ctrls_lentry);
+ if (h->ctrl.deinit)
+ h->ctrl.deinit();
+ os_free(h);
+}
+
+
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ if (fst_iface_is_connected(iface, mgmt->sa, FALSE))
+ fst_session_on_action_rx(iface, mgmt, len);
+ else
+ wpa_printf(MSG_DEBUG,
+ "FST: Ignore FST Action frame - no FST connection with "
+ MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
+{
+ if (is_zero_ether_addr(addr))
+ return;
+
+#ifndef HOSTAPD
+ fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+ fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
+ MAC2STR(addr));
+
+ fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr);
+}
+
+
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
+{
+ if (is_zero_ether_addr(addr))
+ return;
+
+#ifndef HOSTAPD
+ fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+ fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
+ MAC2STR(addr));
+
+ fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr);
+}
+
+
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+ struct fst_iface *iface2)
+{
+ return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
+}
+
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ case HOSTAPD_MODE_IEEE80211G:
+ return MB_BAND_ID_WIFI_2_4GHZ;
+ case HOSTAPD_MODE_IEEE80211A:
+ return MB_BAND_ID_WIFI_5GHZ;
+ case HOSTAPD_MODE_IEEE80211AD:
+ return MB_BAND_ID_WIFI_60GHZ;
+ default:
+ WPA_ASSERT(0);
+ return MB_BAND_ID_WIFI_2_4GHZ;
+ }
+}
diff --git a/src/fst/fst.h b/src/fst/fst.h
new file mode 100644
index 0000000..0c0e435
--- /dev/null
+++ b/src/fst/fst.h
@@ -0,0 +1,296 @@
+/*
+ * FST module - interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_H
+#define FST_H
+
+#ifdef CONFIG_FST
+
+#include "common/defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+/* FST module hostap integration API */
+
+#define US_IN_MS 1000
+#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */
+
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+
+#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
+#define FST_MAX_PRIO_VALUE ((u8) -1)
+#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
+
+#define FST_DEFAULT_LLT_CFG_VALUE 50
+
+struct hostapd_hw_modes;
+struct ieee80211_mgmt;
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+struct fst_get_peer_ctx;
+struct fst_ctrl_handle;
+
+struct fst_wpa_obj {
+ void *ctx;
+
+ /**
+ * get_bssid - Get BSSID of the interface
+ * @ctx: User context %ctx
+ * Returns: BSSID for success, %NULL for failure.
+ *
+ * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
+ * the associated AP.
+ */
+ const u8 * (*get_bssid)(void *ctx);
+
+ /**
+ * get_channel_info - Get current channel info
+ * @ctx: User context %ctx
+ * @hw_mode: OUT, current HW mode
+ * @channel: OUT, current channel
+ */
+ void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
+ u8 *channel);
+
+ /**
+ * get_hw_modes - Get hardware modes
+ * @ctx: User context %ctx
+ * @modes: OUT, pointer on array of hw modes
+ *
+ * Returns: Number of hw modes available.
+ */
+ int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
+
+ /**
+ * set_ies - Set interface's MB IE
+ * @ctx: User context %ctx
+ * @fst_ies: MB IE buffer (owned by FST module)
+ */
+ void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
+
+ /**
+ * send_action - Send FST Action frame via the interface
+ * @ctx: User context %ctx
+ * @addr: Address of the destination STA
+ * @data: Action frame buffer
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
+
+ /**
+ * get_mb_ie - Get last MB IE received from STA
+ * @ctx: User context %ctx
+ * @addr: Address of the STA
+ * Returns: MB IE buffer, %NULL if no MB IE received from the STA
+ */
+ const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
+
+ /**
+ * update_mb_ie - Update last MB IE received from STA
+ * @ctx: User context %ctx
+ * @addr: Address of the STA
+ * @buf: Buffer that contains the MB IEs data
+ * @size: Size of data in %buf
+ */
+ void (*update_mb_ie)(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size);
+
+ /**
+ * get_peer_first - Get MAC address of the 1st connected STA
+ * @ctx: User context %ctx
+ * @get_ctx: Context to be used for %get_peer_next call
+ * @mb_only: %TRUE if only multi-band capable peer should be reported
+ * Returns: Address of the 1st connected STA, %NULL if no STAs connected
+ */
+ const u8 * (*get_peer_first)(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only);
+ /**
+ * get_peer_next - Get MAC address of the next connected STA
+ * @ctx: User context %ctx
+ * @get_ctx: Context received from %get_peer_first or previous
+ * %get_peer_next call
+ * @mb_only: %TRUE if only multi-band capable peer should be reported
+ * Returns: Address of the next connected STA, %NULL if no more STAs
+ * connected
+ */
+ const u8 * (*get_peer_next)(void *ctx,
+ struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only);
+};
+
+/**
+ * fst_global_init - Global FST module initiator
+ * Returns: 0 for success, negative error code for failure.
+ * Note: The purpose of this function is to allocate and initiate global
+ * FST module data structures (linked lists, static data etc.)
+ * This function should be called prior to the 1st %fst_attach call.
+ */
+int fst_global_init(void);
+
+/**
+ * fst_global_deinit - Global FST module de-initiator
+ * Note: The purpose of this function is to deallocate and de-initiate global
+ * FST module data structures (linked lists, static data etc.)
+ */
+void fst_global_deinit(void);
+
+/**
+ * struct fst_ctrl - Notification interface for FST module
+ */
+struct fst_ctrl {
+ /**
+ * init - Initialize the notification interface
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*init)(void);
+
+ /**
+ * deinit - Deinitialize the notification interface
+ */
+ void (*deinit)(void);
+
+ /**
+ * on_group_created - Notify about FST group creation
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_group_created)(struct fst_group *g);
+
+ /**
+ * on_group_deleted - Notify about FST group deletion
+ */
+ void (*on_group_deleted)(struct fst_group *g);
+
+ /**
+ * on_iface_added - Notify about interface addition
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_iface_added)(struct fst_iface *i);
+
+ /**
+ * on_iface_removed - Notify about interface removal
+ */
+ void (*on_iface_removed)(struct fst_iface *i);
+
+ /**
+ * on_session_added - Notify about FST session addition
+ * Returns: 0 for success, negative error code for failure.
+ */
+ int (*on_session_added)(struct fst_session *s);
+
+ /**
+ * on_session_removed - Notify about FST session removal
+ */
+ void (*on_session_removed)(struct fst_session *s);
+
+ /**
+ * on_event - Notify about FST event
+ * @event_type: Event type
+ * @i: Interface object that relates to the event or NULL
+ * @g: Group object that relates to the event or NULL
+ * @extra - Event specific data (see fst_ctrl_iface.h for more info)
+ */
+ void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
+ struct fst_session *s,
+ const union fst_event_extra *extra);
+};
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
+void fst_global_del_ctrl(struct fst_ctrl_handle *h);
+
+/**
+ * NOTE: These values have to be read from configuration file
+ */
+struct fst_iface_cfg {
+ char group_id[FST_MAX_GROUP_ID_LEN + 1];
+ u8 priority;
+ u32 llt;
+};
+
+/**
+ * fst_attach - Attach interface to an FST group according to configuration read
+ * @ifname: Interface name
+ * @own_addr: Own interface MAC address
+ * @iface_obj: Callbacks to be used by FST module to communicate with
+ * hostapd/wpa_supplicant
+ * @cfg: FST-related interface configuration read from the configuration file
+ * Returns: FST interface object for success, %NULL for failure.
+ */
+struct fst_iface * fst_attach(const char *ifname,
+ const u8 *own_addr,
+ const struct fst_wpa_obj *iface_obj,
+ const struct fst_iface_cfg *cfg);
+
+/**
+ * fst_detach - Detach an interface
+ * @iface: FST interface object
+ */
+void fst_detach(struct fst_iface *iface);
+
+/* FST module inputs */
+/**
+ * fst_rx_action - FST Action frames handler
+ * @iface: FST interface object
+ * @mgmt: Action frame arrived
+ * @len: Action frame length
+ */
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+ size_t len);
+
+/**
+ * fst_notify_peer_connected - FST STA connect handler
+ * @iface: FST interface object
+ * @addr: Address of the connected STA
+ */
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
+
+/**
+ * fst_notify_peer_disconnected - FST STA disconnect handler
+ * @iface: FST interface object
+ * @addr: Address of the disconnected STA
+ */
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
+
+/* FST module auxiliary routines */
+
+/**
+ * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
+ * same FST group
+ * @iface1: 1st FST interface object
+ * @iface1: 2nd FST interface object
+ *
+ * Returns: %TRUE if the interfaces belong to the same FST group,
+ * %FALSE otherwise
+ */
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+ struct fst_iface *iface2);
+
+#else /* CONFIG_FST */
+
+static inline int fst_global_init(void)
+{
+ return 0;
+}
+
+static inline int fst_global_start(void)
+{
+ return 0;
+}
+
+static inline void fst_global_stop(void)
+{
+}
+
+static inline void fst_global_deinit(void)
+{
+}
+
+#endif /* CONFIG_FST */
+
+#endif /* FST_H */
diff --git a/src/fst/fst_ctrl_aux.c b/src/fst/fst_ctrl_aux.c
new file mode 100644
index 0000000..dc7b2a7
--- /dev/null
+++ b/src/fst/fst_ctrl_aux.c
@@ -0,0 +1,69 @@
+/*
+ * FST module implementation
+ * 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 "common/defs.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_aux.h"
+
+
+static const char *session_event_names[] = {
+ [EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED,
+ [EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP,
+ [EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE,
+};
+
+static const char *reason_names[] = {
+ [REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN,
+ [REASON_SETUP] FST_CS_PVAL_REASON_SETUP,
+ [REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH,
+ [REASON_STT] FST_CS_PVAL_REASON_STT,
+ [REASON_REJECT] FST_CS_PVAL_REASON_REJECT,
+ [REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS,
+ [REASON_RESET] FST_CS_PVAL_REASON_RESET,
+ [REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE,
+};
+
+static const char *session_state_names[] = {
+ [FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL,
+ [FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION,
+ [FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE,
+ [FST_SESSION_STATE_TRANSITION_CONFIRMED]
+ FST_CS_PVAL_STATE_TRANSITION_CONFIRMED,
+};
+
+
+/* helpers */
+const char * fst_get_str_name(unsigned index, const char *names[],
+ size_t names_size)
+{
+ if (index >= names_size || !names[index])
+ return FST_NAME_UNKNOWN;
+ return names[index];
+}
+
+
+const char * fst_session_event_type_name(enum fst_event_type event)
+{
+ return fst_get_str_name(event, session_event_names,
+ ARRAY_SIZE(session_event_names));
+}
+
+
+const char * fst_reason_name(enum fst_reason reason)
+{
+ return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names));
+}
+
+
+const char * fst_session_state_name(enum fst_session_state state)
+{
+ return fst_get_str_name(state, session_state_names,
+ ARRAY_SIZE(session_state_names));
+}
diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h
new file mode 100644
index 0000000..e2133f5
--- /dev/null
+++ b/src/fst/fst_ctrl_aux.h
@@ -0,0 +1,91 @@
+/*
+ * FST module - miscellaneous definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_AUX_H
+#define FST_CTRL_AUX_H
+
+#include "common/defs.h"
+
+/* FST module control interface API */
+#define FST_INVALID_SESSION_ID ((u32) -1)
+#define FST_MAX_GROUP_ID_SIZE 32
+#define FST_MAX_INTERFACE_SIZE 32
+
+enum fst_session_state {
+ FST_SESSION_STATE_INITIAL,
+ FST_SESSION_STATE_SETUP_COMPLETION,
+ FST_SESSION_STATE_TRANSITION_DONE,
+ FST_SESSION_STATE_TRANSITION_CONFIRMED,
+ FST_SESSION_STATE_LAST
+};
+
+enum fst_event_type {
+ EVENT_FST_IFACE_STATE_CHANGED, /* An interface has been either attached
+ * to or detached from an FST group */
+ EVENT_FST_ESTABLISHED, /* FST Session has been established */
+ EVENT_FST_SETUP, /* FST Session request received */
+ EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
+ EVENT_PEER_STATE_CHANGED /* FST related generic event occurred,
+ * see struct fst_hostap_event_data for
+ * more info */
+};
+
+enum fst_initiator {
+ FST_INITIATOR_UNDEFINED,
+ FST_INITIATOR_LOCAL,
+ FST_INITIATOR_REMOTE,
+};
+
+union fst_event_extra {
+ struct fst_event_extra_iface_state {
+ Boolean attached;
+ char ifname[FST_MAX_INTERFACE_SIZE];
+ char group_id[FST_MAX_GROUP_ID_SIZE];
+ } iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
+ struct fst_event_extra_peer_state {
+ Boolean connected;
+ char ifname[FST_MAX_INTERFACE_SIZE];
+ u8 addr[ETH_ALEN];
+ } peer_state; /* for EVENT_PEER_STATE_CHANGED */
+ struct fst_event_extra_session_state {
+ enum fst_session_state old_state;
+ enum fst_session_state new_state;
+ union fst_session_state_switch_extra {
+ struct {
+ enum fst_reason {
+ REASON_TEARDOWN,
+ REASON_SETUP,
+ REASON_SWITCH,
+ REASON_STT,
+ REASON_REJECT,
+ REASON_ERROR_PARAMS,
+ REASON_RESET,
+ REASON_DETACH_IFACE,
+ } reason;
+ u8 reject_code; /* REASON_REJECT */
+ /* REASON_SWITCH,
+ * REASON_TEARDOWN,
+ * REASON_REJECT
+ */
+ enum fst_initiator initiator;
+ } to_initial;
+ } extra;
+ } session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
+};
+
+/* helpers - prints enum in string form */
+#define FST_NAME_UNKNOWN "UNKNOWN"
+
+const char * fst_get_str_name(unsigned index, const char *names[],
+ size_t names_size);
+
+const char * fst_session_event_type_name(enum fst_event_type);
+const char * fst_reason_name(enum fst_reason reason);
+const char * fst_session_state_name(enum fst_session_state state);
+
+#endif /* FST_CTRL_AUX_H */
diff --git a/src/fst/fst_ctrl_defs.h b/src/fst/fst_ctrl_defs.h
new file mode 100644
index 0000000..6735389
--- /dev/null
+++ b/src/fst/fst_ctrl_defs.h
@@ -0,0 +1,109 @@
+/*
+ * FST module - shared Control interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_DEFS_H
+#define FST_CTRL_DEFS_H
+
+/* Undefined value */
+#define FST_CTRL_PVAL_NONE "NONE"
+
+/* FST-ATTACH parameters */
+#define FST_ATTACH_CMD_PNAME_LLT "llt" /* pval = desired LLT */
+#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */
+
+/* FST-MANAGER parameters */
+/* FST Session states */
+#define FST_CS_PVAL_STATE_INITIAL "INITIAL"
+#define FST_CS_PVAL_STATE_SETUP_COMPLETION "SETUP_COMPLETION"
+#define FST_CS_PVAL_STATE_TRANSITION_DONE "TRANSITION_DONE"
+#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED"
+
+/* FST Session reset reasons */
+#define FST_CS_PVAL_REASON_TEARDOWN "REASON_TEARDOWN"
+#define FST_CS_PVAL_REASON_SETUP "REASON_SETUP"
+#define FST_CS_PVAL_REASON_SWITCH "REASON_SWITCH"
+#define FST_CS_PVAL_REASON_STT "REASON_STT"
+#define FST_CS_PVAL_REASON_REJECT "REASON_REJECT"
+#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS"
+#define FST_CS_PVAL_REASON_RESET "REASON_RESET"
+#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE"
+
+/* FST Session responses */
+#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT"
+#define FST_CS_PVAL_RESPONSE_REJECT "REJECT"
+
+/* FST Session action initiator */
+#define FST_CS_PVAL_INITIATOR_LOCAL "LOCAL"
+#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE"
+
+/* FST-CLI subcommands and parameter names */
+#define FST_CMD_LIST_GROUPS "list_groups"
+#define FST_CMD_LIST_IFACES "list_ifaces"
+#define FST_CMD_IFACE_PEERS "iface_peers"
+#define FST_CMD_GET_PEER_MBIES "get_peer_mbies"
+#define FST_CMD_LIST_SESSIONS "list_sessions"
+#define FST_CMD_SESSION_ADD "session_add"
+#define FST_CMD_SESSION_REMOVE "session_remove"
+#define FST_CMD_SESSION_GET "session_get"
+#define FST_CSG_PNAME_OLD_PEER_ADDR "old_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_NEW_PEER_ADDR "new_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_LLT "llt" /* pval = numeric llt value */
+#define FST_CSG_PNAME_STATE "state" /* pval = FST_CS_PVAL_STATE_... */
+#define FST_CMD_SESSION_SET "session_set"
+#define FST_CSS_PNAME_OLD_PEER_ADDR FST_CSG_PNAME_OLD_PEER_ADDR
+#define FST_CSS_PNAME_NEW_PEER_ADDR FST_CSG_PNAME_NEW_PEER_ADDR
+#define FST_CSS_PNAME_OLD_IFNAME FST_CSG_PNAME_OLD_IFNAME
+#define FST_CSS_PNAME_NEW_IFNAME FST_CSG_PNAME_NEW_IFNAME
+#define FST_CSS_PNAME_LLT FST_CSG_PNAME_LLT
+#define FST_CMD_SESSION_INITIATE "session_initiate"
+#define FST_CMD_SESSION_RESPOND "session_respond"
+#define FST_CMD_SESSION_TRANSFER "session_transfer"
+#define FST_CMD_SESSION_TEARDOWN "session_teardown"
+
+#ifdef CONFIG_FST_TEST
+#define FST_CTR_PVAL_BAD_NEW_BAND "bad_new_band"
+
+#define FST_CMD_TEST_REQUEST "test_request"
+#define FST_CTR_IS_SUPPORTED "is_supported"
+#define FST_CTR_SEND_SETUP_REQUEST "send_setup_request"
+#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response"
+#define FST_CTR_SEND_ACK_REQUEST "send_ack_request"
+#define FST_CTR_SEND_ACK_RESPONSE "send_ack_response"
+#define FST_CTR_SEND_TEAR_DOWN "send_tear_down"
+#define FST_CTR_GET_FSTS_ID "get_fsts_id"
+#define FST_CTR_GET_LOCAL_MBIES "get_local_mbies"
+#endif /* CONFIG_FST_TEST */
+
+/* Events */
+#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE"
+#define FST_CEI_PNAME_IFNAME "ifname"
+#define FST_CEI_PNAME_GROUP "group"
+#define FST_CEI_PNAME_ATTACHED "attached"
+#define FST_CEI_PNAME_DETACHED "detached"
+#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER"
+#define FST_CEP_PNAME_IFNAME "ifname"
+#define FST_CEP_PNAME_ADDR "peer_addr"
+#define FST_CEP_PNAME_CONNECTED "connected"
+#define FST_CEP_PNAME_DISCONNECTED "disconnected"
+#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION"
+#define FST_CES_PNAME_SESSION_ID "session_id"
+#define FST_CES_PNAME_EVT_TYPE "event_type"
+#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE"
+/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */
+#define FST_CES_PNAME_OLD_STATE "old_state"
+#define FST_CES_PNAME_NEW_STATE "new_state"
+#define FST_CES_PNAME_REASON "reason" /* pval = FST_CS_PVAL_REASON_... */
+#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */
+/* pval = FST_CS_PVAL_INITIATOR_... */
+#define FST_CES_PNAME_INITIATOR "initiator"
+#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED"
+#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP"
+
+#endif /* FST_CTRL_DEFS_H */
diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c
new file mode 100644
index 0000000..98ece9f
--- /dev/null
+++ b/src/fst/fst_ctrl_iface.c
@@ -0,0 +1,948 @@
+/*
+ * FST module - Control Interface implementation
+ * 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 "common/defs.h"
+#include "list.h"
+#include "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_iface.h"
+
+
+static struct fst_group * get_fst_group_by_id(const char *id)
+{
+ struct fst_group *g;
+
+ foreach_fst_group(g) {
+ const char *group_id = fst_group_get_id(g);
+
+ if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
+ return g;
+ }
+
+ return NULL;
+}
+
+
+/* notifications */
+static Boolean format_session_state_extra(const union fst_event_extra *extra,
+ char *buffer, size_t size)
+{
+ int len;
+ char reject_str[32] = FST_CTRL_PVAL_NONE;
+ const char *initiator = FST_CTRL_PVAL_NONE;
+ const struct fst_event_extra_session_state *ss;
+
+ ss = &extra->session_state;
+ if (ss->new_state != FST_SESSION_STATE_INITIAL)
+ return TRUE;
+
+ switch (ss->extra.to_initial.reason) {
+ case REASON_REJECT:
+ if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
+ os_snprintf(reject_str, sizeof(reject_str), "%u",
+ ss->extra.to_initial.reject_code);
+ /* no break */
+ case REASON_TEARDOWN:
+ case REASON_SWITCH:
+ switch (ss->extra.to_initial.initiator) {
+ case FST_INITIATOR_LOCAL:
+ initiator = FST_CS_PVAL_INITIATOR_LOCAL;
+ break;
+ case FST_INITIATOR_REMOTE:
+ initiator = FST_CS_PVAL_INITIATOR_REMOTE;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ len = os_snprintf(buffer, size,
+ FST_CES_PNAME_REASON "=%s "
+ FST_CES_PNAME_REJECT_CODE "=%s "
+ FST_CES_PNAME_INITIATOR "=%s",
+ fst_reason_name(ss->extra.to_initial.reason),
+ reject_str, initiator);
+
+ return !os_snprintf_error(size, len);
+}
+
+
+static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
+ enum fst_event_type event_type,
+ const union fst_event_extra *extra)
+{
+ struct fst_group *g;
+ char extra_str[128] = "";
+ const struct fst_event_extra_session_state *ss;
+ const struct fst_event_extra_iface_state *is;
+ const struct fst_event_extra_peer_state *ps;
+
+ /*
+ * FST can use any of interface objects as it only sends messages
+ * on global Control Interface, so we just pick the 1st one.
+ */
+
+ if (!f) {
+ foreach_fst_group(g) {
+ f = fst_group_first_iface(g);
+ if (f)
+ break;
+ }
+ if (!f)
+ return;
+ }
+
+ WPA_ASSERT(f->iface_obj.ctx);
+
+ switch (event_type) {
+ case EVENT_FST_IFACE_STATE_CHANGED:
+ if (!extra)
+ return;
+ is = &extra->iface_state;
+ wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
+ FST_CTRL_EVENT_IFACE " %s "
+ FST_CEI_PNAME_IFNAME "=%s "
+ FST_CEI_PNAME_GROUP "=%s",
+ is->attached ? FST_CEI_PNAME_ATTACHED :
+ FST_CEI_PNAME_DETACHED,
+ is->ifname, is->group_id);
+ break;
+ case EVENT_PEER_STATE_CHANGED:
+ if (!extra)
+ return;
+ ps = &extra->peer_state;
+ wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+ FST_CTRL_EVENT_PEER " %s "
+ FST_CEP_PNAME_IFNAME "=%s "
+ FST_CEP_PNAME_ADDR "=" MACSTR,
+ ps->connected ? FST_CEP_PNAME_CONNECTED :
+ FST_CEP_PNAME_DISCONNECTED,
+ ps->ifname, MAC2STR(ps->addr));
+ break;
+ case EVENT_FST_SESSION_STATE_CHANGED:
+ if (!extra)
+ return;
+ if (!format_session_state_extra(extra, extra_str,
+ sizeof(extra_str))) {
+ fst_printf(MSG_ERROR,
+ "CTRL: Cannot format STATE_CHANGE extra");
+ extra_str[0] = 0;
+ }
+ ss = &extra->session_state;
+ wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+ FST_CTRL_EVENT_SESSION " "
+ FST_CES_PNAME_SESSION_ID "=%u "
+ FST_CES_PNAME_EVT_TYPE "=%s "
+ FST_CES_PNAME_OLD_STATE "=%s "
+ FST_CES_PNAME_NEW_STATE "=%s %s",
+ session_id,
+ fst_session_event_type_name(event_type),
+ fst_session_state_name(ss->old_state),
+ fst_session_state_name(ss->new_state),
+ extra_str);
+ break;
+ case EVENT_FST_ESTABLISHED:
+ case EVENT_FST_SETUP:
+ wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+ FST_CTRL_EVENT_SESSION " "
+ FST_CES_PNAME_SESSION_ID "=%u "
+ FST_CES_PNAME_EVT_TYPE "=%s",
+ session_id,
+ fst_session_event_type_name(event_type));
+ break;
+ }
+}
+
+
+/* command processors */
+
+/* fst session_get */
+static int session_get(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ struct fst_iface *new_iface, *old_iface;
+ const u8 *old_peer_addr, *new_peer_addr;
+ u32 id;
+
+ id = strtoul(session_id, NULL, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ old_peer_addr = fst_session_get_peer_addr(s, TRUE);
+ new_peer_addr = fst_session_get_peer_addr(s, FALSE);
+ new_iface = fst_session_get_iface(s, FALSE);
+ old_iface = fst_session_get_iface(s, TRUE);
+
+ return os_snprintf(buf, buflen,
+ FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
+ FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
+ FST_CSG_PNAME_NEW_IFNAME "=%s\n"
+ FST_CSG_PNAME_OLD_IFNAME "=%s\n"
+ FST_CSG_PNAME_LLT "=%u\n"
+ FST_CSG_PNAME_STATE "=%s\n",
+ MAC2STR(old_peer_addr),
+ MAC2STR(new_peer_addr),
+ new_iface ? fst_iface_get_name(new_iface) :
+ FST_CTRL_PVAL_NONE,
+ old_iface ? fst_iface_get_name(old_iface) :
+ FST_CTRL_PVAL_NONE,
+ fst_session_get_llt(s),
+ fst_session_state_name(fst_session_get_state(s)));
+}
+
+
+/* fst session_set */
+static int session_set(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ char *p, *q;
+ u32 id;
+ int ret;
+
+ id = strtoul(session_id, &p, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
+ return os_snprintf(buf, buflen, "FAIL\n");
+ p++;
+
+ if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
+ ret = fst_session_set_str_ifname(s, q + 1, TRUE);
+ } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
+ ret = fst_session_set_str_ifname(s, q + 1, FALSE);
+ } else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
+ ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
+ } else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
+ ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
+ } else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
+ ret = fst_session_set_str_llt(s, q + 1);
+ } else {
+ fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+
+
+/* fst session_add/remove */
+static int session_add(const char *group_id, char *buf, size_t buflen)
+{
+ struct fst_group *g;
+ struct fst_session *s;
+
+ g = get_fst_group_by_id(group_id);
+ if (!g) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+ group_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ s = fst_session_create(g);
+ if (!s) {
+ fst_printf(MSG_ERROR,
+ "CTRL: Cannot create session for group '%s'",
+ group_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
+}
+
+
+static int session_remove(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ struct fst_group *g;
+ u32 id;
+
+ id = strtoul(session_id, NULL, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ g = fst_session_get_group(s);
+ fst_session_reset(s);
+ fst_session_delete(s);
+ fst_group_delete_if_empty(g);
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_initiate */
+static int session_initiate(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ u32 id;
+
+ id = strtoul(session_id, NULL, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (fst_session_initiate_setup(s)) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_respond */
+static int session_respond(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ char *p;
+ u32 id;
+ u8 status_code;
+
+ id = strtoul(session_id, &p, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (*p != ' ')
+ return os_snprintf(buf, buflen, "FAIL\n");
+ p++;
+
+ if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
+ status_code = WLAN_STATUS_SUCCESS;
+ } else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
+ status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+ } else {
+ fst_printf(MSG_WARNING,
+ "CTRL: session %u: unknown response status: %s",
+ id, p);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (fst_session_respond(s, status_code)) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
+ id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ fst_printf(MSG_INFO, "CTRL: session %u responded", id);
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_transfer */
+static int session_transfer(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ u32 id;
+
+ id = strtoul(session_id, NULL, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (fst_session_initiate_switch(s)) {
+ fst_printf(MSG_WARNING,
+ "CTRL: Cannot initiate ST for session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_teardown */
+static int session_teardown(const char *session_id, char *buf, size_t buflen)
+{
+ struct fst_session *s;
+ u32 id;
+
+ id = strtoul(session_id, NULL, 0);
+
+ s = fst_session_get_by_id(id);
+ if (!s) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ if (fst_session_tear_down_setup(s)) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
+ id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+#ifdef CONFIG_FST_TEST
+/* fst test_request */
+static int test_request(const char *request, char *buf, size_t buflen)
+{
+ const char *p = request;
+ int ret;
+
+ if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
+ os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
+ ret = fst_test_req_send_fst_request(
+ p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
+ } else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
+ os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
+ ret = fst_test_req_send_fst_response(
+ p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
+ } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
+ os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
+ ret = fst_test_req_send_ack_request(
+ p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
+ } else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
+ os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
+ ret = fst_test_req_send_ack_response(
+ p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
+ } else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
+ os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
+ ret = fst_test_req_send_tear_down(
+ p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
+ } else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
+ os_strlen(FST_CTR_GET_FSTS_ID))) {
+ u32 fsts_id = fst_test_req_get_fsts_id(
+ p + os_strlen(FST_CTR_GET_FSTS_ID));
+ if (fsts_id != FST_FSTS_ID_NOT_FOUND)
+ return os_snprintf(buf, buflen, "%u\n", fsts_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ } else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
+ os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
+ return fst_test_req_get_local_mbies(
+ p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
+ } else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
+ os_strlen(FST_CTR_IS_SUPPORTED))) {
+ ret = 0;
+ } else {
+ fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+#endif /* CONFIG_FST_TEST */
+
+
+/* fst list_sessions */
+struct list_sessions_cb_ctx {
+ char *buf;
+ size_t buflen;
+ size_t reply_len;
+};
+
+
+static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
+ void *ctx)
+{
+ struct list_sessions_cb_ctx *c = ctx;
+ int ret;
+
+ ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
+
+ c->buf += ret;
+ c->buflen -= ret;
+ c->reply_len += ret;
+}
+
+
+static int list_sessions(const char *group_id, char *buf, size_t buflen)
+{
+ struct list_sessions_cb_ctx ctx;
+ struct fst_group *g;
+
+ g = get_fst_group_by_id(group_id);
+ if (!g) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+ group_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ ctx.buf = buf;
+ ctx.buflen = buflen;
+ ctx.reply_len = 0;
+
+ fst_session_enum(g, list_session_enum_cb, &ctx);
+
+ ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
+
+ return ctx.reply_len;
+}
+
+
+/* fst iface_peers */
+static int iface_peers(const char *group_id, char *buf, size_t buflen)
+{
+ const char *ifname;
+ struct fst_group *g;
+ struct fst_iface *f;
+ struct fst_get_peer_ctx *ctx;
+ const u8 *addr;
+ unsigned found = 0;
+ int ret = 0;
+
+ g = get_fst_group_by_id(group_id);
+ if (!g) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+ group_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ ifname = os_strchr(group_id, ' ');
+ if (!ifname)
+ return os_snprintf(buf, buflen, "FAIL\n");
+ ifname++;
+
+ foreach_fst_group_iface(g, f) {
+ const char *in = fst_iface_get_name(f);
+
+ if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return os_snprintf(buf, buflen, "FAIL\n");
+
+ addr = fst_iface_get_peer_first(f, &ctx, FALSE);
+ for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
+ int res;
+
+ res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
+ MAC2STR(addr));
+ if (os_snprintf_error(buflen - ret, res))
+ break;
+ ret += res;
+ }
+
+ return ret;
+}
+
+
+static int get_peer_mbies(const char *params, char *buf, size_t buflen)
+{
+ char *endp;
+ char ifname[FST_MAX_INTERFACE_SIZE];
+ u8 peer_addr[ETH_ALEN];
+ struct fst_group *g;
+ struct fst_iface *iface = NULL;
+ const struct wpabuf *mbies;
+
+ if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
+ !*ifname)
+ goto problem;
+
+ while (isspace(*endp))
+ endp++;
+ if (fst_read_peer_addr(endp, peer_addr))
+ goto problem;
+
+ foreach_fst_group(g) {
+ iface = fst_group_get_iface_by_name(g, ifname);
+ if (iface)
+ break;
+ }
+ if (!iface)
+ goto problem;
+
+ mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
+ if (!mbies)
+ goto problem;
+
+ return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
+ wpabuf_len(mbies));
+
+problem:
+ return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+
+/* fst list_ifaces */
+static int list_ifaces(const char *group_id, char *buf, size_t buflen)
+{
+ struct fst_group *g;
+ struct fst_iface *f;
+ int ret = 0;
+
+ g = get_fst_group_by_id(group_id);
+ if (!g) {
+ fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+ group_id);
+ return os_snprintf(buf, buflen, "FAIL\n");
+ }
+
+ foreach_fst_group_iface(g, f) {
+ int res;
+ const u8 *iface_addr = fst_iface_get_addr(f);
+
+ res = os_snprintf(buf + ret, buflen - ret,
+ "%s|" MACSTR "|%u|%u\n",
+ fst_iface_get_name(f),
+ MAC2STR(iface_addr),
+ fst_iface_get_priority(f),
+ fst_iface_get_llt(f));
+ if (os_snprintf_error(buflen - ret, res))
+ break;
+ ret += res;
+ }
+
+ return ret;
+}
+
+
+/* fst list_groups */
+static int list_groups(const char *cmd, char *buf, size_t buflen)
+{
+ struct fst_group *g;
+ int ret = 0;
+
+ foreach_fst_group(g) {
+ int res;
+
+ res = os_snprintf(buf + ret, buflen - ret, "%s\n",
+ fst_group_get_id(g));
+ if (os_snprintf_error(buflen - ret, res))
+ break;
+ ret += res;
+ }
+
+ return ret;
+}
+
+
+static const char * band_freq(enum mb_band_id band)
+{
+ static const char *band_names[] = {
+ [MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ",
+ [MB_BAND_ID_WIFI_5GHZ] "5GHZ",
+ [MB_BAND_ID_WIFI_60GHZ] "60GHZ",
+ };
+
+ return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
+}
+
+
+static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ const struct wpabuf *wpabuf;
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+ int ret = 0;
+
+ fst_iface_get_channel_info(iface, &hw_mode, &channel);
+
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
+ num, band_freq(fst_hw_mode_to_band(hw_mode)));
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
+ num, fst_iface_get_name(iface));
+ wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
+ if (wpabuf) {
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
+ num);
+ ret += wpa_snprintf_hex(buf + ret, buflen - ret,
+ wpabuf_head(wpabuf),
+ wpabuf_len(wpabuf));
+ ret += os_snprintf(buf + ret, buflen - ret, "\n");
+ }
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
+ num, fst_iface_get_group_id(iface));
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
+ num, fst_iface_get_priority(iface));
+ ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
+ num, fst_iface_get_llt(iface));
+
+ return ret;
+}
+
+
+static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
+ Boolean attached)
+{
+ union fst_event_extra extra;
+
+ os_memset(&extra, 0, sizeof(extra));
+ extra.iface_state.attached = attached;
+ os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
+ sizeof(extra.iface_state.ifname));
+ os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
+ sizeof(extra.iface_state.group_id));
+
+ fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
+ EVENT_FST_IFACE_STATE_CHANGED, &extra);
+}
+
+
+static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
+{
+ fst_ctrl_iface_on_iface_state_changed(i, TRUE);
+ return 0;
+}
+
+
+static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
+{
+ fst_ctrl_iface_on_iface_state_changed(i, FALSE);
+}
+
+
+static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
+ struct fst_iface *i, struct fst_session *s,
+ const union fst_event_extra *extra)
+{
+ u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
+
+ fst_ctrl_iface_notify(i, session_id, event_type, extra);
+}
+
+
+static const struct fst_ctrl ctrl_cli = {
+ .on_iface_added = fst_ctrl_iface_on_iface_added,
+ .on_iface_removed = fst_ctrl_iface_on_iface_removed,
+ .on_event = fst_ctrl_iface_on_event,
+};
+
+const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
+
+
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+ struct fst_group *g;
+ struct fst_iface *f;
+ unsigned num = 0;
+ int ret = 0;
+
+ foreach_fst_group(g) {
+ foreach_fst_group_iface(g, f) {
+ if (fst_iface_is_connected(f, addr, TRUE)) {
+ ret += print_band(num++, f, addr,
+ buf + ret, buflen - ret);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/* fst ctrl processor */
+int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
+{
+ static const struct fst_command {
+ const char *name;
+ unsigned has_param;
+ int (*process)(const char *group_id, char *buf, size_t buflen);
+ } commands[] = {
+ { FST_CMD_LIST_GROUPS, 0, list_groups},
+ { FST_CMD_LIST_IFACES, 1, list_ifaces},
+ { FST_CMD_IFACE_PEERS, 1, iface_peers},
+ { FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
+ { FST_CMD_LIST_SESSIONS, 1, list_sessions},
+ { FST_CMD_SESSION_ADD, 1, session_add},
+ { FST_CMD_SESSION_REMOVE, 1, session_remove},
+ { FST_CMD_SESSION_GET, 1, session_get},
+ { FST_CMD_SESSION_SET, 1, session_set},
+ { FST_CMD_SESSION_INITIATE, 1, session_initiate},
+ { FST_CMD_SESSION_RESPOND, 1, session_respond},
+ { FST_CMD_SESSION_TRANSFER, 1, session_transfer},
+ { FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
+#ifdef CONFIG_FST_TEST
+ { FST_CMD_TEST_REQUEST, 1, test_request },
+#endif /* CONFIG_FST_TEST */
+ { NULL, 0, NULL }
+ };
+ const struct fst_command *c;
+ const char *p;
+ const char *temp;
+ Boolean non_spaces_found;
+
+ for (c = commands; c->name; c++) {
+ if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
+ continue;
+ p = cmd + os_strlen(c->name);
+ if (c->has_param) {
+ if (!isspace(p[0]))
+ return os_snprintf(reply, reply_size, "FAIL\n");
+ p++;
+ temp = p;
+ non_spaces_found = FALSE;
+ while (*temp) {
+ if (!isspace(*temp)) {
+ non_spaces_found = TRUE;
+ break;
+ }
+ temp++;
+ }
+ if (!non_spaces_found)
+ return os_snprintf(reply, reply_size, "FAIL\n");
+ }
+ return c->process(p, reply, reply_size);
+ }
+
+ return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
+}
+
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
+{
+ int ret = -1;
+ const char *curp;
+
+ *valid = FALSE;
+ *endp = (char *) params;
+ curp = params;
+ if (*curp) {
+ ret = (int) strtol(curp, endp, 0);
+ if (!**endp || isspace(**endp))
+ *valid = TRUE;
+ }
+
+ return ret;
+}
+
+
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+ char **endp)
+{
+ size_t max_chars_to_copy;
+ char *cur_dest;
+
+ *endp = (char *) params;
+ while (isspace(**endp))
+ (*endp)++;
+ if (!**endp || buflen <= 1)
+ return -EINVAL;
+
+ max_chars_to_copy = buflen - 1;
+ /* We need 1 byte for the terminating zero */
+ cur_dest = buf;
+ while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
+ *cur_dest = **endp;
+ (*endp)++;
+ cur_dest++;
+ max_chars_to_copy--;
+ }
+ *cur_dest = 0;
+
+ return 0;
+}
+
+
+int fst_read_peer_addr(const char *mac, u8 *peer_addr)
+{
+ if (hwaddr_aton(mac, peer_addr)) {
+ fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
+ mac);
+ return -1;
+ }
+
+ if (is_zero_ether_addr(peer_addr) ||
+ is_multicast_ether_addr(peer_addr)) {
+ fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
+ mac);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+ struct fst_iface_cfg *cfg)
+{
+ char *pos;
+ char *endp;
+ Boolean is_valid;
+ int val;
+
+ if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
+ fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
+ &endp))
+ return -EINVAL;
+
+ cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
+ cfg->priority = 0;
+ pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
+ if (pos) {
+ pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
+ if (*pos == '=') {
+ val = fst_read_next_int_param(pos + 1, &is_valid,
+ &endp);
+ if (is_valid)
+ cfg->llt = val;
+ }
+ }
+ pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
+ if (pos) {
+ pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
+ if (*pos == '=') {
+ val = fst_read_next_int_param(pos + 1, &is_valid,
+ &endp);
+ if (is_valid)
+ cfg->priority = (u8) val;
+ }
+ }
+
+ return 0;
+}
+
+
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
+{
+ char *endp;
+
+ return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
+}
+
+
+int fst_iface_detach(const char *ifname)
+{
+ struct fst_group *g;
+
+ foreach_fst_group(g) {
+ struct fst_iface *f;
+
+ f = fst_group_get_iface_by_name(g, ifname);
+ if (f) {
+ fst_detach(f);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
diff --git a/src/fst/fst_ctrl_iface.h b/src/fst/fst_ctrl_iface.h
new file mode 100644
index 0000000..4d0cd9f
--- /dev/null
+++ b/src/fst/fst_ctrl_iface.h
@@ -0,0 +1,45 @@
+/*
+ * FST module - internal Control interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_IFACE_H
+#define FST_CTRL_IFACE_H
+
+#include "fst/fst_ctrl_aux.h"
+
+#ifdef CONFIG_FST
+
+/* receiver */
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
+
+int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
+
+extern const struct fst_ctrl *fst_ctrl_cli;
+
+#else /* CONFIG_FST */
+
+static inline int
+fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+#endif /* CONFIG_FST */
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp);
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+ char **endp);
+int fst_read_peer_addr(const char *mac, u8 *peer_addr);
+
+struct fst_iface_cfg;
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+ struct fst_iface_cfg *cfg);
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
+int fst_iface_detach(const char *ifname);
+
+#endif /* CTRL_IFACE_FST_H */
diff --git a/src/fst/fst_defs.h b/src/fst/fst_defs.h
new file mode 100644
index 0000000..8ddcc61
--- /dev/null
+++ b/src/fst/fst_defs.h
@@ -0,0 +1,87 @@
+/*
+ * FST module - FST related definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE_80211_FST_DEFS_H
+#define IEEE_80211_FST_DEFS_H
+
+/* IEEE Std 802.11ad */
+
+#define MB_STA_CHANNEL_ALL 0
+
+enum session_type {
+ SESSION_TYPE_BSS = 0, /* Infrastructure BSS */
+ SESSION_TYPE_IBSS = 1,
+ SESSION_TYPE_DLS = 2,
+ SESSION_TYPE_TDLS = 3,
+ SESSION_TYPE_PBSS = 4
+};
+
+#define SESSION_CONTROL(session_type, switch_intent) \
+ (((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00))
+
+#define GET_SESSION_CONTROL_TYPE(session_control) \
+ ((u8) ((session_control) & 0x7))
+
+#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \
+ (((session_control) & 0x10) >> 4)
+
+/* 8.4.2.147 Session Transition element */
+struct session_transition_ie {
+ u8 element_id;
+ u8 length;
+ u32 fsts_id;
+ u8 session_control;
+ u8 new_band_id;
+ u8 new_band_setup;
+ u8 new_band_op;
+ u8 old_band_id;
+ u8 old_band_setup;
+ u8 old_band_op;
+} STRUCT_PACKED;
+
+struct fst_setup_req {
+ u8 action;
+ u8 dialog_token;
+ u32 llt;
+ struct session_transition_ie stie;
+ /* Multi-band (optional) */
+ /* Wakeup Schedule (optional) */
+ /* Awake Window (optional) */
+ /* Switching Stream (optional) */
+} STRUCT_PACKED;
+
+struct fst_setup_res {
+ u8 action;
+ u8 dialog_token;
+ u8 status_code;
+ struct session_transition_ie stie;
+ /* Multi-band (optional) */
+ /* Wakeup Schedule (optional) */
+ /* Awake Window (optional) */
+ /* Switching Stream (optional) */
+ /* Timeout Interval (optional) */
+} STRUCT_PACKED;
+
+struct fst_ack_req {
+ u8 action;
+ u8 dialog_token;
+ u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_ack_res {
+ u8 action;
+ u8 dialog_token;
+ u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_tear_down {
+ u8 action;
+ u32 fsts_id;
+} STRUCT_PACKED;
+
+#endif /* IEEE_80211_FST_DEFS_H */
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
new file mode 100644
index 0000000..e0c055f
--- /dev/null
+++ b/src/fst/fst_group.c
@@ -0,0 +1,437 @@
+/*
+ * FST module - FST group object implementation
+ * 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 "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct dl_list fst_global_groups_list;
+
+
+static void fst_dump_mb_ies(const char *group_id, const char *ifname,
+ struct wpabuf *mbies)
+{
+ const u8 *p = wpabuf_head(mbies);
+ size_t s = wpabuf_len(mbies);
+
+ while (s >= 2) {
+ const struct multi_band_ie *mbie =
+ (const struct multi_band_ie *) p;
+ WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
+ WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
+
+ fst_printf(MSG_WARNING,
+ "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
+ MACSTR
+ " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
+ group_id, ifname,
+ mbie->mb_ctrl, mbie->band_id, mbie->op_class,
+ mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
+ mbie->tsf_offs[0], mbie->tsf_offs[1],
+ mbie->tsf_offs[2], mbie->tsf_offs[3],
+ mbie->tsf_offs[4], mbie->tsf_offs[5],
+ mbie->tsf_offs[6], mbie->tsf_offs[7],
+ mbie->mb_connection_capability,
+ mbie->fst_session_tmout);
+
+ p += 2 + mbie->len;
+ s -= 2 + mbie->len;
+ }
+}
+
+
+static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
+ const u8 *own_addr, enum mb_band_id band, u8 channel)
+{
+ struct multi_band_ie *mbie;
+ size_t len = sizeof(*mbie);
+
+ if (own_addr)
+ len += ETH_ALEN;
+
+ mbie = wpabuf_put(buf, len);
+
+ os_memset(mbie, 0, len);
+
+ mbie->eid = WLAN_EID_MULTI_BAND;
+ mbie->len = len - 2;
+#ifdef HOSTAPD
+ mbie->mb_ctrl = MB_STA_ROLE_AP;
+ mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
+#else /* HOSTAPD */
+ mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
+ mbie->mb_connection_capability = 0;
+#endif /* HOSTAPD */
+ if (bssid)
+ os_memcpy(mbie->bssid, bssid, ETH_ALEN);
+ mbie->band_id = band;
+ mbie->op_class = 0; /* means all */
+ mbie->chan = channel;
+ mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
+
+ if (own_addr) {
+ mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
+ os_memcpy(&mbie[1], own_addr, ETH_ALEN);
+ }
+}
+
+
+static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
+{
+ const u8 *bssid;
+
+ bssid = fst_iface_get_bssid(f);
+ if (bssid) {
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+
+ if (buf) {
+ fst_iface_get_channel_info(f, &hw_mode, &channel);
+ fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
+ fst_hw_mode_to_band(hw_mode), channel);
+ }
+ return 1;
+ } else {
+ unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
+ struct hostapd_hw_modes *modes;
+ enum mb_band_id b;
+ int num_modes = fst_iface_get_hw_modes(f, &modes);
+ int ret = 0;
+
+ while (num_modes--) {
+ b = fst_hw_mode_to_band(modes->mode);
+ modes++;
+ if (b >= ARRAY_SIZE(bands) || bands[b]++)
+ continue;
+ ret++;
+ if (buf)
+ fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
+ b, MB_STA_CHANNEL_ALL);
+ }
+ return ret;
+ }
+}
+
+
+static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
+ struct fst_iface *i)
+{
+ struct wpabuf *buf;
+ struct fst_iface *f;
+ unsigned int nof_mbies = 0;
+ unsigned int nof_ifaces_added = 0;
+
+ foreach_fst_group_iface(g, f) {
+ if (f == i)
+ continue;
+ nof_mbies += fst_fill_iface_mb_ies(f, NULL);
+ }
+
+ buf = wpabuf_alloc(nof_mbies *
+ (sizeof(struct multi_band_ie) + ETH_ALEN));
+ if (!buf) {
+ fst_printf_iface(i, MSG_ERROR,
+ "cannot allocate mem for %u MB IEs",
+ nof_mbies);
+ return NULL;
+ }
+
+ /* The list is sorted in descending order by priorities, so MB IEs will
+ * be arranged in the same order, as required by spec (see corresponding
+ * comment in.fst_attach().
+ */
+ foreach_fst_group_iface(g, f) {
+ if (f == i)
+ continue;
+
+ fst_fill_iface_mb_ies(f, buf);
+ ++nof_ifaces_added;
+
+ fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
+ }
+
+ if (!nof_ifaces_added) {
+ wpabuf_free(buf);
+ buf = NULL;
+ fst_printf_iface(i, MSG_INFO,
+ "cannot add MB IE: no backup ifaces");
+ } else {
+ fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
+ buf);
+ }
+
+ return buf;
+}
+
+
+static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
+{
+ const u8 *peer_addr = NULL;
+
+ switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
+ case MB_STA_ROLE_AP:
+ peer_addr = mbie->bssid;
+ break;
+ case MB_STA_ROLE_NON_PCP_NON_AP:
+ if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
+ (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
+ peer_addr = (const u8 *) &mbie[1];
+ break;
+ default:
+ break;
+ }
+
+ return peer_addr;
+}
+
+
+static struct fst_iface *
+fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
+ const u8 *mb_ies_buff,
+ size_t mb_ies_size,
+ u8 band_id,
+ u8 *iface_peer_addr)
+{
+ while (mb_ies_size >= 2) {
+ const struct multi_band_ie *mbie =
+ (const struct multi_band_ie *) mb_ies_buff;
+
+ if (mbie->eid != WLAN_EID_MULTI_BAND ||
+ (size_t) 2 + mbie->len < sizeof(*mbie))
+ break;
+
+ if (mbie->band_id == band_id) {
+ struct fst_iface *iface;
+
+ foreach_fst_group_iface(g, iface) {
+ const u8 *peer_addr =
+ fst_mbie_get_peer_addr(mbie);
+
+ if (peer_addr &&
+ fst_iface_is_connected(iface, peer_addr,
+ TRUE) &&
+ band_id == fst_iface_get_band_id(iface)) {
+ os_memcpy(iface_peer_addr, peer_addr,
+ ETH_ALEN);
+ return iface;
+ }
+ }
+ break;
+ }
+
+ mb_ies_buff += 2 + mbie->len;
+ mb_ies_size -= 2 + mbie->len;
+ }
+
+ return NULL;
+}
+
+
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+ const char *ifname)
+{
+ struct fst_iface *f;
+
+ foreach_fst_group_iface(g, f) {
+ const char *in = fst_iface_get_name(f);
+
+ if (os_strncmp(in, ifname, os_strlen(in)) == 0)
+ return f;
+ }
+
+ return NULL;
+}
+
+
+u8 fst_group_assign_dialog_token(struct fst_group *g)
+{
+ g->dialog_token++;
+ if (g->dialog_token == 0)
+ g->dialog_token++;
+ return g->dialog_token;
+}
+
+
+u32 fst_group_assign_fsts_id(struct fst_group *g)
+{
+ g->fsts_id++;
+ return g->fsts_id;
+}
+
+
+static Boolean
+fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
+ struct fst_iface *iface,
+ struct fst_iface *other,
+ u8 *peer_addr)
+{
+ struct fst_get_peer_ctx *ctx;
+ const u8 *addr;
+ const u8 *iface_addr;
+ enum mb_band_id iface_band_id;
+
+ WPA_ASSERT(g == fst_iface_get_group(iface));
+ WPA_ASSERT(g == fst_iface_get_group(other));
+
+ iface_addr = fst_iface_get_addr(iface);
+ iface_band_id = fst_iface_get_band_id(iface);
+
+ addr = fst_iface_get_peer_first(other, &ctx, TRUE);
+ for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
+ const struct wpabuf *mbies;
+ u8 other_iface_peer_addr[ETH_ALEN];
+ struct fst_iface *other_new_iface;
+
+ mbies = fst_iface_get_peer_mb_ie(other, addr);
+ if (!mbies)
+ continue;
+
+ other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
+ g, wpabuf_head(mbies), wpabuf_len(mbies),
+ iface_band_id, other_iface_peer_addr);
+ if (other_new_iface == iface &&
+ os_memcmp(iface_addr, other_iface_peer_addr,
+ ETH_ALEN) != 0) {
+ os_memcpy(peer_addr, addr, ETH_ALEN);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+ struct fst_iface *iface,
+ const u8 *peer_addr,
+ const struct session_transition_ie *stie,
+ u8 *iface_peer_addr)
+{
+ struct fst_iface *i;
+
+ foreach_fst_group_iface(g, i) {
+ if (i == iface ||
+ stie->new_band_id != fst_iface_get_band_id(i))
+ continue;
+ if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
+ iface_peer_addr))
+ return i;
+ break;
+ }
+ return NULL;
+}
+
+
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+ struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+ const struct session_transition_ie *stie, u8 *iface_peer_addr)
+{
+ return fst_group_get_new_iface_by_mbie_and_band_id(
+ g, mb_ies_buff, mb_ies_size, stie->new_band_id,
+ iface_peer_addr);
+}
+
+
+struct fst_group * fst_group_create(const char *group_id)
+{
+ struct fst_group *g;
+
+ g = os_zalloc(sizeof(*g));
+ if (g == NULL) {
+ fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
+ return NULL;
+ }
+
+ dl_list_init(&g->ifaces);
+ os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
+
+ dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
+ fst_printf_group(g, MSG_DEBUG, "instance created");
+
+ foreach_fst_ctrl_call(on_group_created, g);
+
+ return g;
+}
+
+
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
+{
+ struct dl_list *list = &g->ifaces;
+ struct fst_iface *f;
+
+ /*
+ * Add new interface to the list.
+ * The list is sorted in descending order by priority to allow
+ * multiple MB IEs creation according to the spec (see 10.32 Multi-band
+ * operation, 10.32.1 General), as they should be ordered according to
+ * priorities.
+ */
+ foreach_fst_group_iface(g, f) {
+ if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
+ break;
+ list = &f->group_lentry;
+ }
+ dl_list_add(list, &i->group_lentry);
+}
+
+
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
+{
+ dl_list_del(&i->group_lentry);
+}
+
+
+void fst_group_delete(struct fst_group *group)
+{
+ struct fst_session *s;
+
+ dl_list_del(&group->global_groups_lentry);
+ WPA_ASSERT(dl_list_empty(&group->ifaces));
+ foreach_fst_ctrl_call(on_group_deleted, group);
+ fst_printf_group(group, MSG_DEBUG, "instance deleted");
+ while ((s = fst_session_global_get_first_by_group(group)) != NULL)
+ fst_session_delete(s);
+ os_free(group);
+}
+
+
+Boolean fst_group_delete_if_empty(struct fst_group *group)
+{
+ Boolean is_empty = !fst_group_has_ifaces(group) &&
+ !fst_session_global_get_first_by_group(group);
+
+ if (is_empty)
+ fst_group_delete(group);
+
+ return is_empty;
+}
+
+
+void fst_group_update_ie(struct fst_group *g)
+{
+ struct fst_iface *i;
+
+ foreach_fst_group_iface(g, i) {
+ struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
+
+ if (!mbie)
+ fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
+
+ fst_iface_attach_mbie(i, mbie);
+ fst_iface_set_ies(i, mbie);
+ fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
+ }
+}
diff --git a/src/fst/fst_group.h b/src/fst/fst_group.h
new file mode 100644
index 0000000..3a87c0b
--- /dev/null
+++ b/src/fst/fst_group.h
@@ -0,0 +1,75 @@
+/*
+ * FST module - FST group object definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_GROUP_H
+#define FST_GROUP_H
+
+struct fst_group {
+ char group_id[IFNAMSIZ + 1];
+ struct dl_list ifaces;
+ u8 dialog_token;
+ u32 fsts_id;
+ struct dl_list global_groups_lentry;
+};
+
+struct session_transition_ie;
+
+#define foreach_fst_group_iface(g, i) \
+ dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry)
+
+struct fst_group * fst_group_create(const char *group_id);
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_delete(struct fst_group *g);
+
+void fst_group_update_ie(struct fst_group *g);
+
+static inline Boolean fst_group_has_ifaces(struct fst_group *g)
+{
+ return !dl_list_empty(&g->ifaces);
+}
+
+static inline struct fst_iface * fst_group_first_iface(struct fst_group *g)
+{
+ return dl_list_first(&g->ifaces, struct fst_iface, group_lentry);
+}
+
+static inline const char * fst_group_get_id(struct fst_group *g)
+{
+ return g->group_id;
+}
+
+Boolean fst_group_delete_if_empty(struct fst_group *group);
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+ const char *ifname);
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+ struct fst_iface *iface,
+ const u8 *peer_addr,
+ const struct session_transition_ie *stie,
+ u8 *iface_peer_addr);
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+ struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+ const struct session_transition_ie *stie, u8 *iface_peer_addr);
+u8 fst_group_assign_dialog_token(struct fst_group *g);
+u32 fst_group_assign_fsts_id(struct fst_group *g);
+
+extern struct dl_list fst_global_groups_list;
+
+#define foreach_fst_group(g) \
+ dl_list_for_each((g), &fst_global_groups_list, \
+ struct fst_group, global_groups_lentry)
+
+static inline struct fst_group * fst_first_group(void)
+{
+ return dl_list_first(&fst_global_groups_list, struct fst_group,
+ global_groups_lentry);
+}
+
+#endif /* FST_GROUP_H */
diff --git a/src/fst/fst_iface.c b/src/fst/fst_iface.c
new file mode 100644
index 0000000..35e83cb
--- /dev/null
+++ b/src/fst/fst_iface.c
@@ -0,0 +1,80 @@
+/*
+ * FST module - FST interface object implementation
+ * 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 "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+ const u8 *own_addr,
+ const struct fst_wpa_obj *iface_obj,
+ const struct fst_iface_cfg *cfg)
+{
+ struct fst_iface *i;
+
+ i = os_zalloc(sizeof(*i));
+ if (!i) {
+ fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s",
+ ifname);
+ return NULL;
+ }
+
+ i->cfg = *cfg;
+ i->iface_obj = *iface_obj;
+ i->group = g;
+ os_strlcpy(i->ifname, ifname, sizeof(i->ifname));
+ os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr));
+
+ if (!i->cfg.llt) {
+ fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted");
+ i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE;
+ }
+
+ return i;
+}
+
+
+void fst_iface_delete(struct fst_iface *i)
+{
+ fst_iface_set_ies(i, NULL);
+ wpabuf_free(i->mb_ie);
+ os_free(i);
+}
+
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
+ Boolean mb_only)
+{
+ struct fst_get_peer_ctx *ctx;
+ const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only);
+
+ for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only))
+ if (os_memcmp(addr, a, ETH_ALEN) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie)
+{
+ wpabuf_free(i->mb_ie);
+ i->mb_ie = mbie;
+}
+
+
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i)
+{
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+
+ fst_iface_get_channel_info(i, &hw_mode, &channel);
+ return fst_hw_mode_to_band(hw_mode);
+}
diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h
new file mode 100644
index 0000000..0eb2732
--- /dev/null
+++ b/src/fst/fst_iface.h
@@ -0,0 +1,136 @@
+/*
+ * FST module - FST interface object definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#ifndef FST_IFACE_H
+#define FST_IFACE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "list.h"
+#include "fst.h"
+
+struct fst_iface {
+ struct fst_group *group;
+ struct fst_wpa_obj iface_obj;
+ u8 own_addr[ETH_ALEN];
+ struct wpabuf *mb_ie;
+ char ifname[IFNAMSIZ + 1];
+ struct fst_iface_cfg cfg;
+ struct dl_list group_lentry;
+};
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+ const u8 *own_addr,
+ const struct fst_wpa_obj *iface_obj,
+ const struct fst_iface_cfg *cfg);
+void fst_iface_delete(struct fst_iface *i);
+
+static inline struct fst_group * fst_iface_get_group(struct fst_iface *i)
+{
+ return i->group;
+}
+
+static inline const char * fst_iface_get_name(struct fst_iface *i)
+{
+ return i->ifname;
+}
+
+static inline const u8 * fst_iface_get_addr(struct fst_iface *i)
+{
+ return i->own_addr;
+}
+
+static inline const char * fst_iface_get_group_id(struct fst_iface *i)
+{
+ return i->cfg.group_id;
+}
+
+static inline u8 fst_iface_get_priority(struct fst_iface *i)
+{
+ return i->cfg.priority;
+}
+
+static inline u32 fst_iface_get_llt(struct fst_iface *i)
+{
+ return i->cfg.llt;
+}
+
+static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i)
+{
+ return i->mb_ie;
+}
+
+static inline const u8 * fst_iface_get_bssid(struct fst_iface *i)
+{
+ return i->iface_obj.get_bssid(i->iface_obj.ctx);
+}
+
+static inline void fst_iface_get_channel_info(struct fst_iface *i,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel);
+}
+
+static inline int fst_iface_get_hw_modes(struct fst_iface *i,
+ struct hostapd_hw_modes **modes)
+{
+ return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes);
+}
+
+static inline void fst_iface_set_ies(struct fst_iface *i,
+ const struct wpabuf *fst_ies)
+{
+ i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies);
+}
+
+static inline int fst_iface_send_action(struct fst_iface *i,
+ const u8 *addr, struct wpabuf *data)
+{
+ return i->iface_obj.send_action(i->iface_obj.ctx, addr, data);
+}
+
+static inline const struct wpabuf *
+fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr)
+{
+ return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr);
+}
+
+static inline void fst_iface_update_mb_ie(struct fst_iface *i,
+ const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
+}
+
+static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
+ struct fst_get_peer_ctx **ctx,
+ Boolean mb_only)
+{
+ return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only);
+}
+
+static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i,
+ struct fst_get_peer_ctx **ctx,
+ Boolean mb_only)
+{
+ return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only);
+}
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
+ Boolean mb_only);
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie);
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i);
+
+static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i)
+{
+ return i->iface_obj.ctx;
+}
+
+#endif /* FST_IFACE_H */
diff --git a/src/fst/fst_internal.h b/src/fst/fst_internal.h
new file mode 100644
index 0000000..9fe32b8
--- /dev/null
+++ b/src/fst/fst_internal.h
@@ -0,0 +1,49 @@
+/*
+ * FST module - auxiliary definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_INTERNAL_H
+#define FST_INTERNAL_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "fst/fst_iface.h"
+#include "fst/fst_group.h"
+#include "fst/fst_session.h"
+
+#define fst_printf(level, format, ...) \
+ wpa_printf((level), "FST: " format, ##__VA_ARGS__)
+
+#define fst_printf_group(group, level, format, ...) \
+ wpa_printf((level), "FST: %s: " format, \
+ fst_group_get_id(group), ##__VA_ARGS__)
+
+#define fst_printf_iface(iface, level, format, ...) \
+ fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \
+ fst_iface_get_name(iface), ##__VA_ARGS__)
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode);
+
+struct fst_ctrl_handle {
+ struct fst_ctrl ctrl;
+ struct dl_list global_ctrls_lentry;
+};
+
+extern struct dl_list fst_global_ctrls_list;
+
+#define foreach_fst_ctrl_call(clb, ...) \
+ do { \
+ struct fst_ctrl_handle *__fst_ctrl_h; \
+ dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \
+ struct fst_ctrl_handle, global_ctrls_lentry) \
+ if (__fst_ctrl_h->ctrl.clb) \
+ __fst_ctrl_h->ctrl.clb(__VA_ARGS__);\
+ } while (0)
+
+#endif /* FST_INTERNAL_H */
diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c
new file mode 100644
index 0000000..f804b12
--- /dev/null
+++ b/src/fst/fst_session.c
@@ -0,0 +1,1623 @@
+/*
+ * FST module - FST Session implementation
+ * 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 "common/defs.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+#ifdef CONFIG_FST_TEST
+#include "fst/fst_ctrl_defs.h"
+#endif /* CONFIG_FST_TEST */
+
+#define US_80211_TU 1024
+
+#define US_TO_TU(m) ((m) * / US_80211_TU)
+#define TU_TO_US(m) ((m) * US_80211_TU)
+
+#define FST_LLT_SWITCH_IMMEDIATELY 0
+
+#define fst_printf_session(s, level, format, ...) \
+ fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \
+ (s)->id, (s)->data.fsts_id, \
+ MAC2STR((s)->data.old_peer_addr), \
+ MAC2STR((s)->data.new_peer_addr), \
+ ##__VA_ARGS__)
+
+#define fst_printf_siface(s, iface, level, format, ...) \
+ fst_printf_session((s), (level), "%s: " format, \
+ fst_iface_get_name(iface), ##__VA_ARGS__)
+
+#define fst_printf_sframe(s, is_old, level, format, ...) \
+ fst_printf_siface((s), \
+ (is_old) ? (s)->data.old_iface : (s)->data.new_iface, \
+ (level), format, ##__VA_ARGS__)
+
+#define FST_LLT_MS_DEFAULT 50
+#define FST_ACTION_MAX_SUPPORTED FST_ACTION_ON_CHANNEL_TUNNEL
+
+const char * const fst_action_names[] = {
+ [FST_ACTION_SETUP_REQUEST] = "Setup Request",
+ [FST_ACTION_SETUP_RESPONSE] = "Setup Response",
+ [FST_ACTION_TEAR_DOWN] = "Tear Down",
+ [FST_ACTION_ACK_REQUEST] = "Ack Request",
+ [FST_ACTION_ACK_RESPONSE] = "Ack Response",
+ [FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel",
+};
+
+struct fst_session {
+ struct {
+ /* Session configuration that can be zeroed on reset */
+ u8 old_peer_addr[ETH_ALEN];
+ u8 new_peer_addr[ETH_ALEN];
+ struct fst_iface *new_iface;
+ struct fst_iface *old_iface;
+ u32 llt_ms;
+ u8 pending_setup_req_dlgt;
+ u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147
+ * Session Transition element */
+ } data;
+ /* Session object internal fields which won't be zeroed on reset */
+ struct dl_list global_sessions_lentry;
+ u32 id; /* Session object ID used to identify
+ * specific session object */
+ struct fst_group *group;
+ enum fst_session_state state;
+ Boolean stt_armed;
+};
+
+static struct dl_list global_sessions_list;
+static u32 global_session_id = 0;
+
+#define foreach_fst_session(s) \
+ dl_list_for_each((s), &global_sessions_list, \
+ struct fst_session, global_sessions_lentry)
+
+#define foreach_fst_session_safe(s, temp) \
+ dl_list_for_each_safe((s), (temp), &global_sessions_list, \
+ struct fst_session, global_sessions_lentry)
+
+
+static void fst_session_global_inc_id(void)
+{
+ global_session_id++;
+ if (global_session_id == FST_INVALID_SESSION_ID)
+ global_session_id++;
+}
+
+
+int fst_session_global_init(void)
+{
+ dl_list_init(&global_sessions_list);
+ return 0;
+}
+
+
+void fst_session_global_deinit(void)
+{
+ WPA_ASSERT(dl_list_empty(&global_sessions_list));
+}
+
+
+static inline void fst_session_notify_ctrl(struct fst_session *s,
+ enum fst_event_type event_type,
+ union fst_event_extra *extra)
+{
+ foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
+}
+
+
+static void fst_session_set_state(struct fst_session *s,
+ enum fst_session_state state,
+ union fst_session_state_switch_extra *extra)
+{
+ if (s->state != state) {
+ union fst_event_extra evext = {
+ .session_state = {
+ .old_state = s->state,
+ .new_state = state,
+ },
+ };
+
+ if (extra)
+ evext.session_state.extra = *extra;
+ fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
+ &evext);
+ fst_printf_session(s, MSG_INFO, "State: %s => %s",
+ fst_session_state_name(s->state),
+ fst_session_state_name(state));
+ s->state = state;
+ }
+}
+
+
+static u32 fst_find_free_session_id(void)
+{
+ u32 i, id = FST_INVALID_SESSION_ID;
+ struct fst_session *s;
+
+ for (i = 0; i < (u32) -1; i++) {
+ Boolean in_use = FALSE;
+
+ foreach_fst_session(s) {
+ if (s->id == global_session_id) {
+ fst_session_global_inc_id();
+ in_use = TRUE;
+ break;
+ }
+ }
+ if (!in_use) {
+ id = global_session_id;
+ fst_session_global_inc_id();
+ break;
+ }
+ }
+
+ return id;
+}
+
+
+static void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct fst_session *s = user_ctx;
+ union fst_session_state_switch_extra extra = {
+ .to_initial = {
+ .reason = REASON_STT,
+ },
+ };
+
+ fst_printf_session(s, MSG_WARNING, "Session State Timeout");
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
+}
+
+
+static void fst_session_stt_arm(struct fst_session *s)
+{
+ eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
+ fst_session_timeout_handler, NULL, s);
+ s->stt_armed = TRUE;
+}
+
+
+static void fst_session_stt_disarm(struct fst_session *s)
+{
+ if (s->stt_armed) {
+ eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
+ s->stt_armed = FALSE;
+ }
+}
+
+
+static Boolean fst_session_is_in_transition(struct fst_session *s)
+{
+ /* See spec, 10.32.2.2 Transitioning between states */
+ return s->stt_armed;
+}
+
+
+static int fst_session_is_in_progress(struct fst_session *s)
+{
+ return s->state != FST_SESSION_STATE_INITIAL;
+}
+
+
+static int fst_session_is_ready_pending(struct fst_session *s)
+{
+ return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+ fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_ready(struct fst_session *s)
+{
+ return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+ !fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_switch_requested(struct fst_session *s)
+{
+ return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
+ fst_session_is_in_transition(s);
+}
+
+
+static struct fst_session *
+fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
+{
+ struct fst_session *s;
+
+ foreach_fst_session(s) {
+ if (s->group == g &&
+ (os_memcmp(s->data.old_peer_addr, peer_addr,
+ ETH_ALEN) == 0 ||
+ os_memcmp(s->data.new_peer_addr, peer_addr,
+ ETH_ALEN) == 0) &&
+ fst_session_is_in_progress(s))
+ return s;
+ }
+
+ return NULL;
+}
+
+
+static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
+{
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = reason,
+ },
+ };
+
+ if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
+ s->state == FST_SESSION_STATE_TRANSITION_DONE)
+ fst_session_tear_down_setup(s);
+ fst_session_stt_disarm(s);
+ os_memset(&s->data, 0, sizeof(s->data));
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static int fst_session_send_action(struct fst_session *s, Boolean old_iface,
+ const void *payload, size_t size,
+ const struct wpabuf *extra_buf)
+{
+ size_t len;
+ int res;
+ struct wpabuf *buf;
+ u8 action;
+ struct fst_iface *iface =
+ old_iface ? s->data.old_iface : s->data.new_iface;
+
+ WPA_ASSERT(payload != NULL);
+ WPA_ASSERT(size != 0);
+
+ action = *(const u8 *) payload;
+
+ WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
+
+ if (!iface) {
+ fst_printf_session(s, MSG_ERROR,
+ "no %s interface for FST Action '%s' sending",
+ old_iface ? "old" : "new",
+ fst_action_names[action]);
+ return -1;
+ }
+
+ len = sizeof(u8) /* category */ + size;
+ if (extra_buf)
+ len += wpabuf_size(extra_buf);
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ fst_printf_session(s, MSG_ERROR,
+ "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
+ len, fst_action_names[action]);
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_FST);
+ wpabuf_put_data(buf, payload, size);
+ if (extra_buf)
+ wpabuf_put_buf(buf, extra_buf);
+
+ res = fst_iface_send_action(iface,
+ old_iface ? s->data.old_peer_addr :
+ s->data.new_peer_addr, buf);
+ if (res < 0)
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "failed to send FST Action '%s'",
+ fst_action_names[action]);
+ else
+ fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
+ fst_action_names[action]);
+ wpabuf_free(buf);
+
+ return res;
+}
+
+
+static int fst_session_send_tear_down(struct fst_session *s)
+{
+ struct fst_tear_down td;
+ int res;
+
+ if (!fst_session_is_in_progress(s)) {
+ fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
+ return -1;
+ }
+
+ WPA_ASSERT(s->data.old_iface != NULL);
+ WPA_ASSERT(s->data.new_iface != NULL);
+
+ os_memset(&td, 0, sizeof(td));
+
+ td.action = FST_ACTION_TEAR_DOWN;
+ td.fsts_id = host_to_le32(s->data.fsts_id);
+
+ res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
+ if (!res)
+ fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
+ else
+ fst_printf_sframe(s, TRUE, MSG_ERROR,
+ "failed to send FST TearDown");
+
+ return res;
+}
+
+
+static void fst_session_handle_setup_request(struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ struct fst_session *s;
+ const struct fst_setup_req *req;
+ struct fst_iface *new_iface = NULL;
+ struct fst_group *g;
+ u8 new_iface_peer_addr[ETH_ALEN];
+ const struct wpabuf *peer_mbies;
+ size_t plen;
+
+ if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req)) {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Request dropped: too short (%zu < %zu)",
+ frame_len,
+ IEEE80211_HDRLEN + 1 + sizeof(*req));
+ return;
+ }
+ plen = frame_len - IEEE80211_HDRLEN - 1;
+ req = (const struct fst_setup_req *)
+ (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+ if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+ req->stie.length < 11) {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Request dropped: invalid STIE");
+ return;
+ }
+
+ if (req->stie.new_band_id == req->stie.old_band_id) {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Request dropped: new and old band IDs are the same");
+ return;
+ }
+
+ g = fst_iface_get_group(iface);
+
+ if (plen > sizeof(*req)) {
+ fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
+ plen - sizeof(*req));
+ fst_printf_iface(iface, MSG_INFO,
+ "FST Request: MB IEs updated for " MACSTR,
+ MAC2STR(mgmt->sa));
+ }
+
+ peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa);
+ if (peer_mbies) {
+ new_iface = fst_group_get_new_iface_by_stie_and_mbie(
+ g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies),
+ &req->stie, new_iface_peer_addr);
+ if (new_iface)
+ fst_printf_iface(iface, MSG_INFO,
+ "FST Request: new iface (%s:" MACSTR
+ ") found by MB IEs",
+ fst_iface_get_name(new_iface),
+ MAC2STR(new_iface_peer_addr));
+ }
+
+ if (!new_iface) {
+ new_iface = fst_group_find_new_iface_by_stie(
+ g, iface, mgmt->sa, &req->stie,
+ new_iface_peer_addr);
+ if (new_iface)
+ fst_printf_iface(iface, MSG_INFO,
+ "FST Request: new iface (%s:" MACSTR
+ ") found by others",
+ fst_iface_get_name(new_iface),
+ MAC2STR(new_iface_peer_addr));
+ }
+
+ if (!new_iface) {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Request dropped: new iface not found");
+ return;
+ }
+
+ s = fst_find_session_in_progress(mgmt->sa, g);
+ if (s) {
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_SETUP,
+ },
+ };
+
+ /*
+ * 10.32.2.2 Transitioning between states:
+ * Upon receipt of an FST Setup Request frame, the responder
+ * shall respond with an FST Setup Response frame unless it has
+ * a pending FST Setup Request frame addressed to the initiator
+ * and the responder has a numerically larger MAC address than
+ * the initiator’s MAC address, in which case, the responder
+ * shall delete the received FST Setup Request.
+ */
+ if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Request dropped due to MAC comparison (our MAC is "
+ MACSTR ")",
+ MAC2STR(mgmt->da));
+ return;
+ }
+
+ if (!fst_session_is_ready_pending(s)) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Request from " MACSTR
+ " dropped due to inappropriate state %s",
+ MAC2STR(mgmt->da),
+ fst_session_state_name(s->state));
+ return;
+ }
+
+
+ /*
+ * If FST Setup Request arrived with the same FSTS ID as one we
+ * initialized before, it means the other side either didn't
+ * receive our FST Request or skipped it for some reason (for
+ * example, due to numerical MAC comparison).
+ *
+ * In this case, there's no need to tear down the session.
+ * Moreover, as FSTS ID is the same, the other side will
+ * associate this tear down with the session it initiated that
+ * will break the sync.
+ */
+ if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
+ fst_session_send_tear_down(s);
+ else
+ fst_printf_session(s, MSG_WARNING,
+ "Skipping TearDown as the FST request has the same FSTS ID as initiated");
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+ fst_session_stt_disarm(s);
+ fst_printf_session(s, MSG_WARNING, "reset due to FST request");
+ }
+
+ s = fst_session_create(g);
+ if (!s) {
+ fst_printf(MSG_WARNING,
+ "FST Request dropped: cannot create session for %s and %s",
+ fst_iface_get_name(iface),
+ fst_iface_get_name(new_iface));
+ return;
+ }
+
+ fst_session_set_iface(s, iface, TRUE);
+ fst_session_set_peer_addr(s, mgmt->sa, TRUE);
+ fst_session_set_iface(s, new_iface, FALSE);
+ fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
+ fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
+ s->data.pending_setup_req_dlgt = req->dialog_token;
+ s->data.fsts_id = le_to_host32(req->stie.fsts_id);
+
+ fst_session_stt_arm(s);
+
+ fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
+
+ fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
+}
+
+
+static void fst_session_handle_setup_response(struct fst_session *s,
+ struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ const struct fst_setup_res *res;
+ size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {0},
+ };
+
+ if (iface != s->data.old_iface) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Response dropped: %s is not the old iface",
+ fst_iface_get_name(iface));
+ return;
+ }
+
+ if (!fst_session_is_ready_pending(s)) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Response dropped due to wrong state: %s",
+ fst_session_state_name(s->state));
+ return;
+ }
+
+ if (plen < sizeof(*res)) {
+ fst_printf_session(s, MSG_WARNING,
+ "Too short FST Response dropped");
+ return;
+ }
+ res = (const struct fst_setup_res *)
+ (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+ if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+ res->stie.length < 11) {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Response dropped: invalid STIE");
+ return;
+ }
+
+ if (res->dialog_token != s->data.pending_setup_req_dlgt) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Response dropped due to wrong dialog token (%u != %u)",
+ s->data.pending_setup_req_dlgt,
+ res->dialog_token);
+ return;
+ }
+
+ if (res->status_code == WLAN_STATUS_SUCCESS &&
+ le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
+ fst_printf_session(s, MSG_WARNING,
+ "FST Response dropped due to wrong FST Session ID (%u)",
+ le_to_host32(res->stie.fsts_id));
+ return;
+ }
+
+ fst_session_stt_disarm(s);
+
+ if (res->status_code != WLAN_STATUS_SUCCESS) {
+ /*
+ * 10.32.2.2 Transitioning between states
+ * The initiator shall set the STT to the value of the
+ * FSTSessionTimeOut field at ... and at each ACK frame sent in
+ * response to a received FST Setup Response with the Status
+ * Code field equal to PENDING_ADMITTING_FST_SESSION or
+ * PENDING_GAP_IN_BA_WINDOW.
+ */
+ evext.to_initial.reason = REASON_REJECT;
+ evext.to_initial.reject_code = res->status_code;
+ evext.to_initial.initiator = FST_INITIATOR_REMOTE;
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+ fst_printf_session(s, MSG_WARNING,
+ "FST Setup rejected by remote side with status %u",
+ res->status_code);
+ return;
+ }
+
+ fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
+
+ if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
+ evext.to_initial.reason = REASON_ERROR_PARAMS;
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+ fst_printf_session(s, MSG_WARNING,
+ "invalid FST Setup parameters");
+ fst_session_tear_down_setup(s);
+ return;
+ }
+
+ fst_printf_session(s, MSG_INFO,
+ "%s: FST Setup established for %s (llt=%u)",
+ fst_iface_get_name(s->data.old_iface),
+ fst_iface_get_name(s->data.new_iface),
+ s->data.llt_ms);
+
+ fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
+
+ if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
+ fst_session_initiate_switch(s);
+}
+
+
+static void fst_session_handle_tear_down(struct fst_session *s,
+ struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ const struct fst_tear_down *td;
+ size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_TEARDOWN,
+ .initiator = FST_INITIATOR_REMOTE,
+ },
+ };
+
+ if (plen < sizeof(*td)) {
+ fst_printf_session(s, MSG_WARNING,
+ "Too short FST Tear Down dropped");
+ return;
+ }
+ td = (const struct fst_tear_down *)
+ (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+ if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
+ fst_printf_siface(s, iface, MSG_WARNING,
+ "tear down for wrong FST Setup ID (%u)",
+ le_to_host32(td->fsts_id));
+ return;
+ }
+
+ fst_session_stt_disarm(s);
+
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static void fst_session_handle_ack_request(struct fst_session *s,
+ struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ const struct fst_ack_req *req;
+ size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+ struct fst_ack_res res;
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_SWITCH,
+ .initiator = FST_INITIATOR_REMOTE,
+ },
+ };
+
+ if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "cannot initiate switch due to wrong session state (%s)",
+ fst_session_state_name(s->state));
+ return;
+ }
+
+ WPA_ASSERT(s->data.new_iface != NULL);
+
+ if (iface != s->data.new_iface) {
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "Ack received on wrong interface");
+ return;
+ }
+
+ if (plen < sizeof(*req)) {
+ fst_printf_session(s, MSG_WARNING,
+ "Too short FST Ack Request dropped");
+ return;
+ }
+ req = (const struct fst_ack_req *)
+ (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+ if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
+ fst_printf_siface(s, iface, MSG_WARNING,
+ "Ack for wrong FST Setup ID (%u)",
+ le_to_host32(req->fsts_id));
+ return;
+ }
+
+ os_memset(&res, 0, sizeof(res));
+
+ res.action = FST_ACTION_ACK_RESPONSE;
+ res.dialog_token = req->dialog_token;
+ res.fsts_id = req->fsts_id;
+
+ if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
+ fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
+ fst_session_stt_disarm(s);
+ fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+ NULL);
+ fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
+ NULL);
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+ }
+}
+
+
+static void
+fst_session_handle_ack_response(struct fst_session *s,
+ struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ const struct fst_ack_res *res;
+ size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_SWITCH,
+ .initiator = FST_INITIATOR_LOCAL,
+ },
+ };
+
+ if (!fst_session_is_switch_requested(s)) {
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "Ack Response in inappropriate session state (%s)",
+ fst_session_state_name(s->state));
+ return;
+ }
+
+ WPA_ASSERT(s->data.new_iface != NULL);
+
+ if (iface != s->data.new_iface) {
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "Ack response received on wrong interface");
+ return;
+ }
+
+ if (plen < sizeof(*res)) {
+ fst_printf_session(s, MSG_WARNING,
+ "Too short FST Ack Response dropped");
+ return;
+ }
+ res = (const struct fst_ack_res *)
+ (((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+ if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
+ fst_printf_siface(s, iface, MSG_ERROR,
+ "Ack response for wrong FST Setup ID (%u)",
+ le_to_host32(res->fsts_id));
+ return;
+ }
+
+ fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+ fst_session_stt_disarm(s);
+}
+
+
+struct fst_session * fst_session_create(struct fst_group *g)
+{
+ struct fst_session *s;
+ u32 id;
+
+ WPA_ASSERT(!is_zero_ether_addr(own_addr));
+
+ id = fst_find_free_session_id();
+ if (id == FST_INVALID_SESSION_ID) {
+ fst_printf(MSG_ERROR, "Cannot assign new session ID");
+ return NULL;
+ }
+
+ s = os_zalloc(sizeof(*s));
+ if (!s) {
+ fst_printf(MSG_ERROR, "Cannot allocate new session object");
+ return NULL;
+ }
+
+ s->id = id;
+ s->group = g;
+ s->state = FST_SESSION_STATE_INITIAL;
+
+ s->data.llt_ms = FST_LLT_MS_DEFAULT;
+
+ fst_printf(MSG_INFO, "Session %u created", s->id);
+
+ dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
+
+ foreach_fst_ctrl_call(on_session_added, s);
+
+ return s;
+}
+
+
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+ Boolean is_old)
+{
+ if (is_old)
+ s->data.old_iface = iface;
+ else
+ s->data.new_iface = iface;
+
+}
+
+
+void fst_session_set_llt(struct fst_session *s, u32 llt)
+{
+ s->data.llt_ms = llt;
+}
+
+
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+ Boolean is_old)
+{
+ u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+
+ os_memcpy(a, addr, ETH_ALEN);
+}
+
+
+int fst_session_initiate_setup(struct fst_session *s)
+{
+ struct fst_setup_req req;
+ int res;
+ u32 fsts_id;
+ u8 dialog_token;
+ struct fst_session *_s;
+
+ if (fst_session_is_in_progress(s)) {
+ fst_printf_session(s, MSG_ERROR, "Session in progress");
+ return -EINVAL;
+ }
+
+ if (is_zero_ether_addr(s->data.old_peer_addr)) {
+ fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
+ return -EINVAL;
+ }
+
+ if (is_zero_ether_addr(s->data.new_peer_addr)) {
+ fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
+ return -EINVAL;
+ }
+
+ if (!s->data.old_iface) {
+ fst_printf_session(s, MSG_ERROR, "No old interface defined");
+ return -EINVAL;
+ }
+
+ if (!s->data.new_iface) {
+ fst_printf_session(s, MSG_ERROR, "No new interface defined");
+ return -EINVAL;
+ }
+
+ if (s->data.new_iface == s->data.old_iface) {
+ fst_printf_session(s, MSG_ERROR,
+ "Same interface set as old and new");
+ return -EINVAL;
+ }
+
+ if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
+ FALSE)) {
+ fst_printf_session(s, MSG_ERROR,
+ "The preset old peer address is not connected");
+ return -EINVAL;
+ }
+
+ if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
+ FALSE)) {
+ fst_printf_session(s, MSG_ERROR,
+ "The preset new peer address is not connected");
+ return -EINVAL;
+ }
+
+ _s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
+ if (_s) {
+ fst_printf_session(s, MSG_ERROR,
+ "There is another session in progress (old): %u",
+ _s->id);
+ return -EINVAL;
+ }
+
+ _s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
+ if (_s) {
+ fst_printf_session(s, MSG_ERROR,
+ "There is another session in progress (new): %u",
+ _s->id);
+ return -EINVAL;
+ }
+
+ dialog_token = fst_group_assign_dialog_token(s->group);
+ fsts_id = fst_group_assign_fsts_id(s->group);
+
+ os_memset(&req, 0, sizeof(req));
+
+ fst_printf_siface(s, s->data.old_iface, MSG_INFO,
+ "initiating FST setup for %s (llt=%u ms)",
+ fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
+
+ req.action = FST_ACTION_SETUP_REQUEST;
+ req.dialog_token = dialog_token;
+ req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
+ /* 8.4.2.147 Session Transition element */
+ req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+ req.stie.length = sizeof(req.stie) - 2;
+ req.stie.fsts_id = host_to_le32(fsts_id);
+ req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+ req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
+ req.stie.new_band_op = 1;
+ req.stie.new_band_setup = 0;
+
+ req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
+ req.stie.old_band_op = 1;
+ req.stie.old_band_setup = 0;
+
+ res = fst_session_send_action(s, TRUE, &req, sizeof(req),
+ fst_iface_get_mbie(s->data.old_iface));
+ if (!res) {
+ s->data.fsts_id = fsts_id;
+ s->data.pending_setup_req_dlgt = dialog_token;
+ fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
+ fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
+ NULL);
+
+ fst_session_stt_arm(s);
+ }
+
+ return res;
+}
+
+
+int fst_session_respond(struct fst_session *s, u8 status_code)
+{
+ struct fst_setup_res res;
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+
+ if (!fst_session_is_ready_pending(s)) {
+ fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
+ fst_session_state_name(s->state));
+ return -EINVAL;
+ }
+
+ if (is_zero_ether_addr(s->data.old_peer_addr)) {
+ fst_printf_session(s, MSG_ERROR, "No peer MAC address");
+ return -EINVAL;
+ }
+
+ if (!s->data.old_iface) {
+ fst_printf_session(s, MSG_ERROR, "No old interface defined");
+ return -EINVAL;
+ }
+
+ if (!s->data.new_iface) {
+ fst_printf_session(s, MSG_ERROR, "No new interface defined");
+ return -EINVAL;
+ }
+
+ if (s->data.new_iface == s->data.old_iface) {
+ fst_printf_session(s, MSG_ERROR,
+ "Same interface set as old and new");
+ return -EINVAL;
+ }
+
+ if (!fst_iface_is_connected(s->data.old_iface,
+ s->data.old_peer_addr, FALSE)) {
+ fst_printf_session(s, MSG_ERROR,
+ "The preset peer address is not in the peer list");
+ return -EINVAL;
+ }
+
+ fst_session_stt_disarm(s);
+
+ os_memset(&res, 0, sizeof(res));
+
+ res.action = FST_ACTION_SETUP_RESPONSE;
+ res.dialog_token = s->data.pending_setup_req_dlgt;
+ res.status_code = status_code;
+
+ res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+ res.stie.length = sizeof(res.stie) - 2;
+
+ if (status_code == WLAN_STATUS_SUCCESS) {
+ res.stie.fsts_id = s->data.fsts_id;
+ res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+ fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
+ &channel);
+ res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+ res.stie.new_band_op = 1;
+ res.stie.new_band_setup = 0;
+
+ fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
+ &channel);
+ res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+ res.stie.old_band_op = 1;
+ res.stie.old_band_setup = 0;
+
+ fst_printf_session(s, MSG_INFO,
+ "%s: FST Setup Request accepted for %s (llt=%u)",
+ fst_iface_get_name(s->data.old_iface),
+ fst_iface_get_name(s->data.new_iface),
+ s->data.llt_ms);
+ } else {
+ fst_printf_session(s, MSG_WARNING,
+ "%s: FST Setup Request rejected with code %d",
+ fst_iface_get_name(s->data.old_iface),
+ status_code);
+ }
+
+ if (fst_session_send_action(s, TRUE, &res, sizeof(res),
+ fst_iface_get_mbie(s->data.old_iface))) {
+ fst_printf_sframe(s, TRUE, MSG_ERROR,
+ "cannot send FST Setup Response with code %d",
+ status_code);
+ return -EINVAL;
+ }
+
+ fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
+
+ if (status_code != WLAN_STATUS_SUCCESS) {
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_REJECT,
+ .reject_code = status_code,
+ .initiator = FST_INITIATOR_LOCAL,
+ },
+ };
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+ }
+
+ return 0;
+}
+
+
+int fst_session_initiate_switch(struct fst_session *s)
+{
+ struct fst_ack_req req;
+ int res;
+ u8 dialog_token;
+
+ if (!fst_session_is_ready(s)) {
+ fst_printf_session(s, MSG_ERROR,
+ "cannot initiate switch due to wrong setup state (%d)",
+ s->state);
+ return -1;
+ }
+
+ dialog_token = fst_group_assign_dialog_token(s->group);
+
+ WPA_ASSERT(s->data.new_iface != NULL);
+ WPA_ASSERT(s->data.old_iface != NULL);
+
+ fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
+ fst_iface_get_name(s->data.old_iface),
+ fst_iface_get_name(s->data.new_iface));
+
+ os_memset(&req, 0, sizeof(req));
+
+ req.action = FST_ACTION_ACK_REQUEST;
+ req.dialog_token = dialog_token;
+ req.fsts_id = host_to_le32(s->data.fsts_id);
+
+ res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
+ if (!res) {
+ fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
+ fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+ NULL);
+ fst_session_stt_arm(s);
+ } else {
+ fst_printf_sframe(s, FALSE, MSG_ERROR,
+ "Cannot send FST Ack Request");
+ }
+
+ return res;
+}
+
+
+void fst_session_handle_action(struct fst_session *s,
+ struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len)
+{
+ switch (mgmt->u.action.u.fst_action.action) {
+ case FST_ACTION_SETUP_REQUEST:
+ WPA_ASSERT(0);
+ break;
+ case FST_ACTION_SETUP_RESPONSE:
+ fst_session_handle_setup_response(s, iface, mgmt, frame_len);
+ break;
+ case FST_ACTION_TEAR_DOWN:
+ fst_session_handle_tear_down(s, iface, mgmt, frame_len);
+ break;
+ case FST_ACTION_ACK_REQUEST:
+ fst_session_handle_ack_request(s, iface, mgmt, frame_len);
+ break;
+ case FST_ACTION_ACK_RESPONSE:
+ fst_session_handle_ack_response(s, iface, mgmt, frame_len);
+ break;
+ case FST_ACTION_ON_CHANNEL_TUNNEL:
+ default:
+ fst_printf_sframe(s, FALSE, MSG_ERROR,
+ "Unsupported FST Action frame");
+ break;
+ }
+}
+
+
+int fst_session_tear_down_setup(struct fst_session *s)
+{
+ int res;
+ union fst_session_state_switch_extra evext = {
+ .to_initial = {
+ .reason = REASON_TEARDOWN,
+ .initiator = FST_INITIATOR_LOCAL,
+ },
+ };
+
+ res = fst_session_send_tear_down(s);
+
+ fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+ return res;
+}
+
+
+void fst_session_reset(struct fst_session *s)
+{
+ fst_session_reset_ex(s, REASON_RESET);
+}
+
+
+void fst_session_delete(struct fst_session *s)
+{
+ fst_printf(MSG_INFO, "Session %u deleted", s->id);
+ dl_list_del(&s->global_sessions_lentry);
+ foreach_fst_ctrl_call(on_session_removed, s);
+ os_free(s);
+}
+
+
+struct fst_group * fst_session_get_group(struct fst_session *s)
+{
+ return s->group;
+}
+
+
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
+{
+ return is_old ? s->data.old_iface : s->data.new_iface;
+}
+
+
+u32 fst_session_get_id(struct fst_session *s)
+{
+ return s->id;
+}
+
+
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
+{
+ return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+}
+
+
+u32 fst_session_get_llt(struct fst_session *s)
+{
+ return s->data.llt_ms;
+}
+
+
+enum fst_session_state fst_session_get_state(struct fst_session *s)
+{
+ return s->state;
+}
+
+
+struct fst_session * fst_session_get_by_id(u32 id)
+{
+ struct fst_session *s;
+
+ foreach_fst_session(s) {
+ if (id == s->id)
+ return s;
+ }
+
+ return NULL;
+}
+
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
+{
+ struct fst_session *s;
+
+ foreach_fst_session(s) {
+ if (!g || s->group == g)
+ clb(s->group, s, ctx);
+ }
+}
+
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct fst_session *s;
+
+ if (len < IEEE80211_HDRLEN + 2 ||
+ mgmt->u.action.category != WLAN_ACTION_FST) {
+ fst_printf_iface(iface, MSG_ERROR,
+ "invalid Action frame received");
+ return;
+ }
+
+ if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
+ fst_printf_iface(iface, MSG_DEBUG,
+ "FST Action '%s' received!",
+ fst_action_names[mgmt->u.action.u.fst_action.action]);
+ } else {
+ fst_printf_iface(iface, MSG_WARNING,
+ "unknown FST Action (%u) received!",
+ mgmt->u.action.u.fst_action.action);
+ return;
+ }
+
+ if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
+ fst_session_handle_setup_request(iface, mgmt, len);
+ return;
+ }
+
+ s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
+ if (s) {
+ fst_session_handle_action(s, iface, mgmt, len);
+ } else {
+ fst_printf_iface(iface, MSG_WARNING,
+ "FST Action '%s' dropped: no session in progress found",
+ fst_action_names[mgmt->u.action.u.fst_action.action]);
+ }
+}
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+ Boolean is_old)
+{
+ struct fst_group *g = fst_session_get_group(s);
+ struct fst_iface *i;
+
+ i = fst_group_get_iface_by_name(g, ifname);
+ if (!i) {
+ fst_printf_session(s, MSG_WARNING,
+ "Cannot set iface %s: no such iface within group '%s'",
+ ifname, fst_group_get_id(g));
+ return -1;
+ }
+
+ fst_session_set_iface(s, i, is_old);
+
+ return 0;
+}
+
+
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+ Boolean is_old)
+{
+ u8 peer_addr[ETH_ALEN];
+ int res = fst_read_peer_addr(mac, peer_addr);
+
+ if (res)
+ return res;
+
+ fst_session_set_peer_addr(s, peer_addr, is_old);
+
+ return 0;
+}
+
+
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
+{
+ char *endp;
+ long int llt = strtol(llt_str, &endp, 0);
+
+ if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
+ fst_printf_session(s, MSG_WARNING,
+ "Cannot set llt %s: Invalid llt value (1..%u expected)",
+ llt_str, FST_MAX_LLT_MS);
+ return -1;
+ }
+ fst_session_set_llt(s, (u32) llt);
+
+ return 0;
+}
+
+
+void fst_session_global_on_iface_detached(struct fst_iface *iface)
+{
+ struct fst_session *s;
+
+ foreach_fst_session(s) {
+ if (fst_session_is_in_progress(s) &&
+ (s->data.new_iface == iface ||
+ s->data.old_iface == iface))
+ fst_session_reset_ex(s, REASON_DETACH_IFACE);
+ }
+}
+
+
+struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
+{
+ struct fst_session *s;
+
+ foreach_fst_session(s) {
+ if (s->group == g)
+ return s;
+ }
+
+ return NULL;
+}
+
+
+#ifdef CONFIG_FST_TEST
+
+static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
+{
+ const u8 *old_addr, *new_addr;
+ struct fst_get_peer_ctx *ctx;
+
+ os_memset(s, 0, sizeof(*s));
+ foreach_fst_group(*g) {
+ s->data.new_iface = fst_group_first_iface(*g);
+ if (s->data.new_iface)
+ break;
+ }
+ if (!s->data.new_iface)
+ return -EINVAL;
+
+ s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
+ struct fst_iface, group_lentry);
+ if (!s->data.old_iface)
+ return -EINVAL;
+
+ old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
+ if (!old_addr)
+ return -EINVAL;
+
+ new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
+ if (!new_addr)
+ return -EINVAL;
+
+ os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
+ os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
+
+ return 0;
+}
+
+
+#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
+
+int fst_test_req_send_fst_request(const char *params)
+{
+ int fsts_id;
+ Boolean is_valid;
+ char *endp;
+ struct fst_setup_req req;
+ struct fst_session s;
+ struct fst_group *g;
+ enum hostapd_hw_mode hw_mode;
+ u8 channel;
+ char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+
+ if (params[0] != ' ')
+ return -EINVAL;
+ params++;
+ fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return -EINVAL;
+
+ if (get_group_fill_session(&g, &s))
+ return -EINVAL;
+
+ req.action = FST_ACTION_SETUP_REQUEST;
+ req.dialog_token = g->dialog_token;
+ req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
+ /* 8.4.2.147 Session Transition element */
+ req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+ req.stie.length = sizeof(req.stie) - 2;
+ req.stie.fsts_id = host_to_le32(fsts_id);
+ req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+ fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
+ req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+ req.stie.new_band_op = 1;
+ req.stie.new_band_setup = 0;
+
+ fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
+ req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+ req.stie.old_band_op = 1;
+ req.stie.old_band_setup = 0;
+
+ if (!fst_read_next_text_param(endp, additional_param,
+ sizeof(additional_param), &endp)) {
+ if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
+ req.stie.new_band_id = req.stie.old_band_id;
+ }
+
+ return fst_session_send_action(&s, TRUE, &req, sizeof(req),
+ s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_fst_response(const char *params)
+{
+ int fsts_id;
+ Boolean is_valid;
+ char *endp;
+ struct fst_setup_res res;
+ struct fst_session s;
+ struct fst_group *g;
+ enum hostapd_hw_mode hw_mode;
+ u8 status_code;
+ u8 channel;
+ char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+ struct fst_session *_s;
+
+ if (params[0] != ' ')
+ return -EINVAL;
+ params++;
+ fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return -EINVAL;
+
+ if (get_group_fill_session(&g, &s))
+ return -EINVAL;
+
+ status_code = WLAN_STATUS_SUCCESS;
+ if (!fst_read_next_text_param(endp, response, sizeof(response),
+ &endp)) {
+ if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
+ status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+ }
+
+ os_memset(&res, 0, sizeof(res));
+
+ res.action = FST_ACTION_SETUP_RESPONSE;
+ /*
+ * If some session has just received an FST Setup Request, then
+ * use the correct dialog token copied from this request.
+ */
+ _s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
+ g);
+ res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
+ _s->data.pending_setup_req_dlgt : g->dialog_token;
+ res.status_code = status_code;
+
+ res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+ res.stie.length = sizeof(res.stie) - 2;
+
+ if (res.status_code == WLAN_STATUS_SUCCESS) {
+ res.stie.fsts_id = fsts_id;
+ res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+ fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
+ &channel);
+ res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+ res.stie.new_band_op = 1;
+ res.stie.new_band_setup = 0;
+
+ fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
+ &channel);
+ res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+ res.stie.old_band_op = 1;
+ res.stie.old_band_setup = 0;
+ }
+
+ if (!fst_read_next_text_param(endp, response, sizeof(response),
+ &endp)) {
+ if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
+ res.stie.new_band_id = res.stie.old_band_id;
+ }
+
+ return fst_session_send_action(&s, TRUE, &res, sizeof(res),
+ s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_ack_request(const char *params)
+{
+ int fsts_id;
+ Boolean is_valid;
+ char *endp;
+ struct fst_ack_req req;
+ struct fst_session s;
+ struct fst_group *g;
+
+ if (params[0] != ' ')
+ return -EINVAL;
+ params++;
+ fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return -EINVAL;
+
+ if (get_group_fill_session(&g, &s))
+ return -EINVAL;
+
+ os_memset(&req, 0, sizeof(req));
+ req.action = FST_ACTION_ACK_REQUEST;
+ req.dialog_token = g->dialog_token;
+ req.fsts_id = fsts_id;
+
+ return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
+}
+
+
+int fst_test_req_send_ack_response(const char *params)
+{
+ int fsts_id;
+ Boolean is_valid;
+ char *endp;
+ struct fst_ack_res res;
+ struct fst_session s;
+ struct fst_group *g;
+
+ if (params[0] != ' ')
+ return -EINVAL;
+ params++;
+ fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return -EINVAL;
+
+ if (get_group_fill_session(&g, &s))
+ return -EINVAL;
+
+ os_memset(&res, 0, sizeof(res));
+ res.action = FST_ACTION_ACK_RESPONSE;
+ res.dialog_token = g->dialog_token;
+ res.fsts_id = fsts_id;
+
+ return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
+}
+
+
+int fst_test_req_send_tear_down(const char *params)
+{
+ int fsts_id;
+ Boolean is_valid;
+ char *endp;
+ struct fst_tear_down td;
+ struct fst_session s;
+ struct fst_group *g;
+
+ if (params[0] != ' ')
+ return -EINVAL;
+ params++;
+ fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return -EINVAL;
+
+ if (get_group_fill_session(&g, &s))
+ return -EINVAL;
+
+ os_memset(&td, 0, sizeof(td));
+ td.action = FST_ACTION_TEAR_DOWN;
+ td.fsts_id = fsts_id;
+
+ return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
+}
+
+
+u32 fst_test_req_get_fsts_id(const char *params)
+{
+ int sid;
+ Boolean is_valid;
+ char *endp;
+ struct fst_session *s;
+
+ if (params[0] != ' ')
+ return FST_FSTS_ID_NOT_FOUND;
+ params++;
+ sid = fst_read_next_int_param(params, &is_valid, &endp);
+ if (!is_valid)
+ return FST_FSTS_ID_NOT_FOUND;
+
+ s = fst_session_get_by_id(sid);
+ if (!s)
+ return FST_FSTS_ID_NOT_FOUND;
+
+ return s->data.fsts_id;
+}
+
+
+int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
+{
+ char *endp;
+ char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+ struct fst_group *g;
+ struct fst_iface *iface;
+
+ if (request[0] != ' ')
+ return -EINVAL;
+ request++;
+ if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
+ !*ifname)
+ goto problem;
+ g = dl_list_first(&fst_global_groups_list, struct fst_group,
+ global_groups_lentry);
+ if (!g)
+ goto problem;
+ iface = fst_group_get_iface_by_name(g, ifname);
+ if (!iface || !iface->mb_ie)
+ goto problem;
+ return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
+ wpabuf_len(iface->mb_ie));
+
+problem:
+ return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+#endif /* CONFIG_FST_TEST */
diff --git a/src/fst/fst_session.h b/src/fst/fst_session.h
new file mode 100644
index 0000000..1162de4
--- /dev/null
+++ b/src/fst/fst_session.h
@@ -0,0 +1,80 @@
+/*
+ * FST module - FST Session related definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_SESSION_H
+#define FST_SESSION_H
+
+#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */
+
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+enum fst_session_state;
+
+int fst_session_global_init(void);
+void fst_session_global_deinit(void);
+void fst_session_global_on_iface_detached(struct fst_iface *iface);
+struct fst_session *
+fst_session_global_get_first_by_group(struct fst_group *g);
+
+struct fst_session * fst_session_create(struct fst_group *g);
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+ Boolean is_old);
+void fst_session_set_llt(struct fst_session *s, u32 llt);
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+ Boolean is_old);
+int fst_session_initiate_setup(struct fst_session *s);
+int fst_session_respond(struct fst_session *s, u8 status_code);
+int fst_session_initiate_switch(struct fst_session *s);
+void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt,
+ size_t frame_len);
+int fst_session_tear_down_setup(struct fst_session *s);
+void fst_session_reset(struct fst_session *s);
+void fst_session_delete(struct fst_session *s);
+
+struct fst_group * fst_session_get_group(struct fst_session *s);
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old);
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old);
+u32 fst_session_get_id(struct fst_session *s);
+u32 fst_session_get_llt(struct fst_session *s);
+enum fst_session_state fst_session_get_state(struct fst_session *s);
+
+struct fst_session *fst_session_get_by_id(u32 id);
+
+typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s,
+ void *ctx);
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx);
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+ Boolean is_old);
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+ Boolean is_old);
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str);
+
+#ifdef CONFIG_FST_TEST
+
+#define FST_FSTS_ID_NOT_FOUND ((u32) -1)
+
+int fst_test_req_send_fst_request(const char *params);
+int fst_test_req_send_fst_response(const char *params);
+int fst_test_req_send_ack_request(const char *params);
+int fst_test_req_send_ack_response(const char *params);
+int fst_test_req_send_tear_down(const char *params);
+u32 fst_test_req_get_fsts_id(const char *params);
+int fst_test_req_get_local_mbies(const char *request, char *buf,
+ size_t buflen);
+
+#endif /* CONFIG_FST_TEST */
+
+#endif /* FST_SESSION_H */
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 41de2f8..f52f7a2 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -30,11 +30,13 @@
int l2_hdr; /* whether to include layer 2 (Ethernet) header data
* buffers */
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
/* For working around Linux packet socket behavior and regression. */
int fd_br_rx;
int last_from_br;
u8 last_hash[SHA1_MAC_LEN];
- unsigned int num_rx, num_rx_br;
+ unsigned int num_rx_br;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
};
/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
@@ -127,7 +129,6 @@
struct sockaddr_ll ll;
socklen_t fromlen;
- l2->num_rx++;
os_memset(&ll, 0, sizeof(ll));
fromlen = sizeof(ll);
res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
@@ -141,6 +142,7 @@
wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
__func__, MAC2STR(ll.sll_addr), (int) res);
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
if (l2->fd_br_rx >= 0) {
u8 hash[SHA1_MAC_LEN];
const u8 *addr[1];
@@ -173,10 +175,12 @@
}
l2->last_from_br = 0;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
{
struct l2_packet_data *l2 = eloop_ctx;
@@ -214,6 +218,7 @@
os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
}
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
struct l2_packet_data * l2_packet_init(
@@ -233,7 +238,9 @@
l2->rx_callback = rx_callback;
l2->rx_callback_ctx = rx_callback_ctx;
l2->l2_hdr = l2_hdr;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
l2->fd_br_rx = -1;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
htons(protocol));
@@ -289,6 +296,7 @@
void *rx_callback_ctx, int l2_hdr)
{
struct l2_packet_data *l2;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
struct sock_filter ethertype_sock_filter_insns[] = {
/* Load ethertype */
BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
@@ -304,12 +312,14 @@
.filter = ethertype_sock_filter_insns,
};
struct sockaddr_ll ll;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
rx_callback_ctx, l2_hdr);
if (!l2)
return NULL;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
/*
* The Linux packet socket behavior has changed over the years and there
* is an inconvenient regression in it that breaks RX for a specific
@@ -357,6 +367,7 @@
}
eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
return l2;
}
@@ -372,10 +383,12 @@
close(l2->fd);
}
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
if (l2->fd_br_rx >= 0) {
eloop_unregister_read_sock(l2->fd_br_rx);
close(l2->fd_br_rx);
}
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
os_free(l2);
}
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b87ff96..d901169 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -48,9 +48,8 @@
#define P2P_PEER_EXPIRATION_AGE 60
#endif /* P2P_PEER_EXPIRATION_AGE */
-#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
-static void p2p_expire_peers(struct p2p_data *p2p)
+void p2p_expire_peers(struct p2p_data *p2p)
{
struct p2p_device *dev, *n;
struct os_reltime now;
@@ -103,15 +102,6 @@
}
-static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
-{
- struct p2p_data *p2p = eloop_ctx;
- p2p_expire_peers(p2p);
- eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
- p2p_expiration_timeout, p2p, NULL);
-}
-
-
static const char * p2p_state_txt(int state)
{
switch (state) {
@@ -455,8 +445,9 @@
static void p2p_copy_client_info(struct p2p_device *dev,
struct p2p_client_info *cli)
{
- os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
- dev->info.device_name[cli->dev_name_len] = '\0';
+ p2p_copy_filter_devname(dev->info.device_name,
+ sizeof(dev->info.device_name),
+ cli->dev_name, cli->dev_name_len);
dev->info.dev_capab = cli->dev_capab;
dev->info.config_methods = cli->config_methods;
os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
@@ -646,11 +637,11 @@
end = ies + ies_len;
- for (pos = ies; pos + 1 < end; pos += len) {
+ for (pos = ies; end - pos > 1; pos += len) {
id = *pos++;
len = *pos++;
- if (pos + len > end)
+ if (len > end - pos)
break;
if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
@@ -1142,7 +1133,7 @@
if (adv_len >= sizeof(str_buf))
return 0;
- for (i = 0; str[i] && i < adv_len; i++) {
+ for (i = 0; i < adv_len; i++) {
if (str[i] >= 'A' && str[i] <= 'Z')
str_buf[i] = str[i] - 'A' + 'a';
else
@@ -1469,7 +1460,7 @@
/**
- * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD
* @p2p: P2P module context from p2p_init()
* @dev: Selected peer device
* @force_freq: Forced frequency in MHz or 0 if not forced
@@ -1478,9 +1469,9 @@
* Returns: 0 on success, -1 on failure (channel not supported for P2P)
*
* This function is used to do initial operating channel selection for GO
- * Negotiation prior to having received peer information. The selected channel
- * may be further optimized in p2p_reselect_channel() once the peer information
- * is available.
+ * Negotiation prior to having received peer information or for P2PS PD
+ * signalling. The selected channel may be further optimized in
+ * p2p_reselect_channel() once the peer information is available.
*/
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq, int go)
@@ -2689,13 +2680,14 @@
int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
const char *adv_str, u8 svc_state, u16 config_methods,
- const char *svc_info)
+ const char *svc_info, const u8 *cpt_priority)
{
struct p2ps_advertisement *adv_data, *tmp, **prev;
u8 buf[P2PS_HASH_LEN];
size_t adv_data_len, adv_len, info_len = 0;
+ int i;
- if (!p2p || !adv_str || !adv_str[0])
+ if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
return -1;
if (!(config_methods & p2p->cfg->config_methods)) {
@@ -2724,6 +2716,11 @@
adv_data->auto_accept = (u8) auto_accept;
os_memcpy(adv_data->svc_name, adv_str, adv_len);
+ for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) {
+ adv_data->cpt_priority[i] = cpt_priority[i];
+ adv_data->cpt_mask |= cpt_priority[i];
+ }
+
if (svc_info && info_len) {
adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
os_memcpy(adv_data->svc_info, svc_info, info_len);
@@ -2762,8 +2759,9 @@
inserted:
p2p_dbg(p2p,
- "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
- adv_id, adv_data->config_methods, svc_state, adv_str);
+ "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x",
+ adv_id, adv_data->config_methods, svc_state, adv_str,
+ adv_data->cpt_mask);
return 0;
}
@@ -2919,9 +2917,6 @@
dl_list_init(&p2p->devices);
- eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
- p2p_expiration_timeout, p2p, NULL);
-
p2p->go_timeout = 100;
p2p->client_timeout = 20;
p2p->num_p2p_sd_queries = 0;
@@ -2950,8 +2945,6 @@
wpabuf_free(p2p->wfd_coupled_sink_info);
#endif /* CONFIG_WIFI_DISPLAY */
- eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
- 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);
@@ -2978,6 +2971,8 @@
void p2p_flush(struct p2p_data *p2p)
{
struct p2p_device *dev, *prev;
+
+ p2p_ext_listen(p2p, 0, 0);
p2p_stop_find(p2p);
dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
list) {
@@ -2987,6 +2982,7 @@
p2p_free_sd_queries(p2p);
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
+ p2p->ssid_set = 0;
}
@@ -3332,6 +3328,43 @@
}
/*
+ * If after PD Request the peer doesn't expect to receive PD Response
+ * the PD Request ACK indicates a completion of the current PD. This
+ * happens only on the advertiser side sending the follow-on PD Request
+ * with the status different than 12 (Success: accepted by user).
+ */
+ if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker &&
+ p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) {
+ p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK");
+
+ if (p2p->send_action_in_progress) {
+ p2p->send_action_in_progress = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (p2p->cfg->p2ps_prov_complete) {
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx,
+ p2p->p2ps_prov->status,
+ p2p->p2ps_prov->adv_mac,
+ p2p->p2ps_prov->adv_mac,
+ p2p->p2ps_prov->session_mac,
+ NULL, p2p->p2ps_prov->adv_id,
+ p2p->p2ps_prov->session_id,
+ 0, 0, NULL, 0, 0, 0,
+ NULL, NULL, 0, 0);
+ }
+
+ if (p2p->user_initiated_pd)
+ p2p_reset_pending_pd(p2p);
+
+ p2ps_prov_free(p2p);
+ return;
+ }
+
+ /*
* This postponing, of resetting pending_action_state, needs to be
* done only for user initiated PD requests and not internal ones.
*/
@@ -5389,3 +5422,20 @@
"Timeout on waiting peer to become ready for GO Negotiation");
p2p_go_neg_failed(p2p, -1);
}
+
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+ const unsigned int *pref_freq_list,
+ unsigned int size)
+{
+ unsigned int i;
+
+ if (size > P2P_MAX_PREF_CHANNELS)
+ size = P2P_MAX_PREF_CHANNELS;
+ p2p->num_pref_freq = size;
+ for (i = 0; i < size; i++) {
+ p2p->pref_freq_list[i] = pref_freq_list[i];
+ p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
+ i, p2p->pref_freq_list[i]);
+ }
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 67b8bdb..bfdb2c9 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -21,11 +21,17 @@
#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
#define P2PS_HASH_LEN 6
#define P2P_MAX_QUERY_HASH 6
+#define P2PS_FEATURE_CAPAB_CPT_MAX 2
+
+/**
+ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
+ */
+#define P2P_MAX_PREF_CHANNELS 100
/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
-#define P2P_MAX_REG_CLASSES 10
+#define P2P_MAX_REG_CLASSES 15
/**
* P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
@@ -93,6 +99,10 @@
int vht;
+ u8 max_oper_chwidth;
+
+ unsigned int vht_center_freq2;
+
/**
* ssid - SSID of the group
*/
@@ -156,6 +166,11 @@
struct p2ps_provision {
/**
+ * pd_seeker - P2PS provision discovery seeker role
+ */
+ unsigned int pd_seeker:1;
+
+ /**
* status - Remote returned provisioning status code
*/
int status;
@@ -196,6 +211,33 @@
u8 adv_mac[ETH_ALEN];
/**
+ * cpt_mask - Supported Coordination Protocol Transport mask
+ *
+ * A bitwise mask of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_priority.
+ */
+ u8 cpt_mask;
+
+ /**
+ * cpt_priority - Coordination Protocol Transport priority list
+ *
+ * Priorities of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_mask.
+ * The CPT priority list is 0 terminated.
+ */
+ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+ /**
+ * force_freq - The only allowed channel frequency in MHz or 0.
+ */
+ unsigned int force_freq;
+
+ /**
+ * pref_freq - Preferred operating frequency in MHz or 0.
+ */
+ unsigned int pref_freq;
+
+ /**
* info - Vendor defined extra Provisioning information
*/
char info[0];
@@ -235,6 +277,23 @@
u8 hash[P2PS_HASH_LEN];
/**
+ * cpt_mask - supported Coordination Protocol Transport mask
+ *
+ * A bitwise mask of supported ASP Coordination Protocol Transports.
+ * This property is set together and corresponds with cpt_priority.
+ */
+ u8 cpt_mask;
+
+ /**
+ * cpt_priority - Coordination Protocol Transport priority list
+ *
+ * Priorities of supported ASP Coordinatin Protocol Transports.
+ * This property is set together and corresponds with cpt_mask.
+ * The CPT priority list is 0 terminated.
+ */
+ u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+ /**
* svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
*/
char svc_name[0];
@@ -955,18 +1014,21 @@
/**
* Determine if we have a persistent group we share with remote peer
+ * and allocate interface for this group if needed
* @ctx: Callback context from cb_ctx
* @addr: Peer device address to search for
* @ssid: Persistent group SSID or %NULL if any
* @ssid_len: Length of @ssid
- * @go_dev_addr: Buffer for returning intended GO P2P Device Address
+ * @go_dev_addr: Buffer for returning GO P2P Device Address
* @ret_ssid: Buffer for returning group SSID
* @ret_ssid_len: Buffer for returning length of @ssid
+ * @intended_iface_addr: Buffer for returning intended iface address
* Returns: 1 if a matching persistent group was found, 0 otherwise
*/
int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
size_t ssid_len, u8 *go_dev_addr,
- u8 *ret_ssid, size_t *ret_ssid_len);
+ u8 *ret_ssid, size_t *ret_ssid_len,
+ u8 *intended_iface_addr);
/**
* Get information about a possible local GO role
@@ -976,6 +1038,8 @@
* @ssid_len: Buffer for returning length of @ssid
* @group_iface: Buffer for returning whether a separate group interface
* would be used
+ * @freq: Variable for returning the current operating frequency of a
+ * currently running P2P GO.
* Returns: 1 if GO info found, 0 otherwise
*
* This is used to compose New Group settings (SSID, and intended
@@ -983,7 +1047,8 @@
* result in our being an autonomous GO.
*/
int (*get_go_info)(void *ctx, u8 *intended_addr,
- u8 *ssid, size_t *ssid_len, int *group_iface);
+ u8 *ssid, size_t *ssid_len, int *group_iface,
+ unsigned int *freq);
/**
* remove_stale_groups - Remove stale P2PS groups
@@ -1007,7 +1072,9 @@
u8 conncap, int passwd_id,
const u8 *persist_ssid,
size_t persist_ssid_size, int response_done,
- int prov_start, const char *session_info);
+ int prov_start, const char *session_info,
+ const u8 *feat_cap, size_t feat_cap_len,
+ unsigned int freq);
/**
* prov_disc_resp_cb - Callback for indicating completion of PD Response
@@ -1021,14 +1088,34 @@
/**
* p2ps_group_capability - Determine group capability
+ * @ctx: Callback context from cb_ctx
+ * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap.
+ * @role: Local roles, expressed with P2PS_SETUP_* bitmap.
+ * @force_freq: Variable for returning forced frequency for the group.
+ * @pref_freq: Variable for returning preferred frequency for the group.
+ * Returns: P2PS_SETUP_* bitmap of group capability result.
*
- * This function can be used to determine group capability based on
- * information from P2PS PD exchange and the current state of ongoing
- * groups and driver capabilities.
- *
- * P2PS_SETUP_* bitmap is used as the parameters and return value.
+ * This function can be used to determine group capability and
+ * frequencies based on information from P2PS PD exchange and the
+ * current state of ongoing groups and driver capabilities.
*/
- u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role,
+ unsigned int *force_freq,
+ unsigned int *pref_freq);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @ctx: Callback context from cb_ctx
+ * @go: Whether the use if for GO role
+ * @len: Length of freq_list in entries (both IN and OUT)
+ * @freq_list: Buffer for returning the preferred frequencies (MHz)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type.
+ */
+ int (*get_pref_freq_list)(void *ctx, int go,
+ unsigned int *len, unsigned int *freq_list);
};
@@ -2248,9 +2335,33 @@
p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
const char *adv_str, u8 svc_state,
- u16 config_methods, const char *svc_info);
+ u16 config_methods, const char *svc_info,
+ const u8 *cpt_priority);
int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
void p2p_service_flush_asp(struct p2p_data *p2p);
struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+/**
+ * p2p_expire_peers - Periodic cleanup function to expire peers
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This is a cleanup function that the entity calling p2p_init() is
+ * expected to call periodically to clean up expired peer entries.
+ */
+void p2p_expire_peers(struct p2p_data *p2p);
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+ const unsigned int *pref_freq_list,
+ unsigned int size);
+
+/**
+ * p2p_group_get_common_freqs - Get the group common frequencies
+ * @group: P2P group context from p2p_group_init()
+ * @common_freqs: On return will hold the group common frequencies
+ * @num: On return will hold the number of group common frequencies
+ * Returns: 0 on success, -1 otherwise
+ */
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+ unsigned int *num);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index c733543..793d28b 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
@@ -109,6 +110,44 @@
}
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+ const u32 *preferred_freq_list,
+ unsigned int size)
+{
+ unsigned int i, count = 0;
+ u8 op_class, op_channel;
+
+ if (!size)
+ return;
+
+ /*
+ * First, determine the number of P2P supported channels in the
+ * pref_freq_list returned from driver. This is needed for calculations
+ * of the vendor IE size.
+ */
+ for (i = 0; i < size; i++) {
+ if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+ &op_channel) == 0)
+ count++;
+ }
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + count * sizeof(u16));
+ wpabuf_put_be24(buf, OUI_QCA);
+ wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
+ for (i = 0; i < size; i++) {
+ if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+ &op_channel) < 0) {
+ wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
+ preferred_freq_list[i]);
+ continue;
+ }
+ wpabuf_put_u8(buf, op_class);
+ wpabuf_put_u8(buf, op_channel);
+ }
+}
+
+
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
struct p2p_channels *chan)
{
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 19f1daa..049ce6e 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -38,7 +38,7 @@
{
const u8 *pos, *end;
struct p2p_channels *ch;
- size_t channels;
+ u8 channels;
struct p2p_channels intersection;
ch = &dev->channels;
@@ -58,14 +58,14 @@
}
pos += 3;
- while (pos + 2 < end) {
+ while (end - pos > 2) {
struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
cl->reg_class = *pos++;
- if (pos + 1 + pos[0] > end) {
+ channels = *pos++;
+ if (channels > end - pos) {
p2p_info(p2p, "Invalid peer Channel List");
return -1;
}
- channels = *pos++;
cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
P2P_MAX_REG_CLASS_CHANNELS : channels;
os_memcpy(cl->channel, pos, cl->channels);
@@ -185,6 +185,9 @@
p2p->op_reg_class, p2p->op_channel);
p2p_buf_update_ie_hdr(buf, len);
+ p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+ p2p->num_pref_freq);
+
/* WPS IE with Device Password ID attribute */
pw_id = p2p_wps_method_pw_id(peer->wps_method);
if (peer->oob_pw_id)
@@ -312,7 +315,7 @@
group_capab);
p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
- if (peer && peer->go_state == REMOTE_GO) {
+ if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
p2p_dbg(p2p, "Omit Operating Channel attribute");
} else {
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -381,7 +384,7 @@
unsigned int i;
const int op_classes_5ghz[] = { 124, 125, 115, 0 };
const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
- const int op_classes_vht[] = { 128, 0 };
+ const int op_classes_vht[] = { 128, 129, 130, 0 };
if (p2p->own_freq_preference > 0 &&
p2p_freq_to_channel(p2p->own_freq_preference,
@@ -542,6 +545,195 @@
}
+static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
+ struct p2p_device *dev,
+ struct p2p_message *msg,
+ unsigned freq_list[], unsigned int size)
+{
+ u8 op_class, op_channel;
+ unsigned int oper_freq = 0, i, j;
+ int found = 0;
+
+ p2p_dbg(p2p,
+ "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
+
+ /*
+ * Search for a common channel in our preferred frequency list which is
+ * also supported by the peer device.
+ */
+ for (i = 0; i < size && !found; i++) {
+ /*
+ * Make sure that the common frequency is:
+ * 1. Supported by peer
+ * 2. Allowed for P2P use.
+ */
+ oper_freq = freq_list[i];
+ if (p2p_freq_to_channel(oper_freq, &op_class,
+ &op_channel) < 0) {
+ p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
+ continue;
+ }
+ if (!p2p_channels_includes(&p2p->cfg->channels,
+ op_class, op_channel) &&
+ (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
+ op_class, op_channel))) {
+ p2p_dbg(p2p,
+ "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+ oper_freq, op_class, op_channel);
+ break;
+ }
+ for (j = 0; j < msg->channel_list_len; j++) {
+
+ if (op_channel != msg->channel_list[j])
+ continue;
+
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ p2p_dbg(p2p,
+ "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
+ oper_freq);
+ } else {
+ p2p_dbg(p2p,
+ "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
+ dev->oper_freq);
+ }
+}
+
+
+static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
+ struct p2p_device *dev,
+ struct p2p_message *msg,
+ unsigned freq_list[], unsigned int size)
+{
+ u8 op_class, op_channel;
+ unsigned int oper_freq = 0, i, j;
+ int found = 0;
+
+ /*
+ * Peer device supports a Preferred Frequency List.
+ * Search for a common channel in the preferred frequency lists
+ * of both peer and local devices.
+ */
+ for (i = 0; i < size && !found; i++) {
+ for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
+ oper_freq = p2p_channel_to_freq(
+ msg->pref_freq_list[2 * j],
+ msg->pref_freq_list[2 * j + 1]);
+ if (freq_list[i] != oper_freq)
+ continue;
+
+ /*
+ * Make sure that the found frequency is:
+ * 1. Supported
+ * 2. Allowed for P2P use.
+ */
+ if (p2p_freq_to_channel(oper_freq, &op_class,
+ &op_channel) < 0) {
+ p2p_dbg(p2p, "Unsupported frequency %u MHz",
+ oper_freq);
+ continue;
+ }
+
+ if (!p2p_channels_includes(&p2p->cfg->channels,
+ op_class, op_channel) &&
+ (go ||
+ !p2p_channels_includes(&p2p->cfg->cli_channels,
+ op_class, op_channel))) {
+ p2p_dbg(p2p,
+ "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+ oper_freq, op_class, op_channel);
+ break;
+ }
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ p2p_dbg(p2p,
+ "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
+ oper_freq);
+ } else {
+ p2p_dbg(p2p,
+ "No common preferred channels found! Use: %d MHz for oper_channel",
+ dev->oper_freq);
+ }
+}
+
+
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+ struct p2p_device *dev, struct p2p_message *msg)
+{
+ unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
+ unsigned int i;
+ u8 op_class, op_channel;
+
+ /*
+ * Use the preferred channel list from the driver only if there is no
+ * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
+ * channel hardcoded in the configuration file.
+ */
+ if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
+ (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
+ return;
+
+ /* Obtain our preferred frequency list from driver based on P2P role. */
+ size = P2P_MAX_PREF_CHANNELS;
+ if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
+ freq_list))
+ return;
+
+ /*
+ * Check if peer's preference of operating channel is in
+ * our preferred channel list.
+ */
+ for (i = 0; i < size; i++) {
+ if (freq_list[i] == (unsigned int) dev->oper_freq)
+ break;
+ }
+ if (i != size) {
+ /* Peer operating channel preference matches our preference */
+ if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
+ 0) {
+ p2p_dbg(p2p,
+ "Peer operating channel preference is unsupported frequency %u MHz",
+ freq_list[i]);
+ } else {
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ return;
+ }
+ }
+
+ p2p_dbg(p2p,
+ "Peer operating channel preference: %d MHz is not in our preferred channel list",
+ dev->oper_freq);
+
+ /*
+ Check if peer's preferred channel list is
+ * _not_ included in the GO Negotiation Request or Invitation Request.
+ */
+ if (msg->pref_freq_list_len == 0)
+ p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
+ else
+ p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
+}
+
+
void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
@@ -709,6 +901,14 @@
return;
}
+ if (dev->go_neg_req_sent &&
+ (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
+ p2p_dbg(p2p,
+ "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent");
+ p2p_parse_free(&msg);
+ return;
+ }
+
go = p2p_go_det(p2p->go_intent, *msg.go_intent);
if (go < 0) {
p2p_dbg(p2p, "Incompatible GO Intent");
@@ -799,6 +999,12 @@
p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
dev->oper_freq);
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
if (msg.config_timeout) {
dev->go_timeout = msg.config_timeout[0];
dev->client_timeout = msg.config_timeout[1];
@@ -1150,6 +1356,13 @@
if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
goto fail;
+ /*
+ * Use the driver preferred frequency list extension if local device is
+ * GO.
+ */
+ if (go)
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
p2p_set_state(p2p, P2P_GO_NEG);
p2p_clear_timeout(p2p);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 41ca99f..2cf2450 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -296,14 +296,14 @@
os_memset(zero_addr, 0, ETH_ALEN);
pos = wpabuf_head_u8(m->wfd_ie);
end = pos + wpabuf_len(m->wfd_ie);
- while (pos + 1 < end) {
+ while (end - pos >= 3) {
u8 id;
u16 len;
id = *pos++;
len = WPA_GET_BE16(pos);
pos += 2;
- if (pos + len > end)
+ if (len > end - pos)
break;
switch (id) {
@@ -1071,3 +1071,43 @@
break;
}
}
+
+
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+ unsigned int *num)
+
+{
+ struct p2p_channels intersect, res;
+ struct p2p_group_member *m;
+
+ if (!group || !common_freqs || !num)
+ return -1;
+
+ os_memset(&intersect, 0, sizeof(intersect));
+ os_memset(&res, 0, sizeof(res));
+
+ p2p_channels_union(&intersect, &group->p2p->cfg->channels,
+ &intersect);
+
+ p2p_channels_dump(group->p2p,
+ "Group common freqs before iterating members",
+ &intersect);
+
+ for (m = group->members; m; m = m->next) {
+ struct p2p_device *dev;
+
+ dev = p2p_get_device(group->p2p, m->dev_addr);
+ if (!dev)
+ continue;
+
+ p2p_channels_intersect(&intersect, &dev->channels, &res);
+ intersect = res;
+ }
+
+ p2p_channels_dump(group->p2p, "Group common channels", &intersect);
+
+ os_memset(common_freqs, 0, *num * sizeof(int));
+ *num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
+
+ return 0;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index a1042d2..7f65ace 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -53,6 +53,9 @@
* from Beacon/Probe Response), the interface address is stored here.
* p2p_device_addr must still be set in such a case to the unique
* identifier for the P2P Device.
+ *
+ * This field is also used during P2PS PD to store the intended GO
+ * address of the peer.
*/
u8 interface_addr[ETH_ALEN];
@@ -535,6 +538,9 @@
u16 authorized_oob_dev_pw_id;
struct wpabuf **vendor_elem;
+
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+ unsigned int num_pref_freq;
};
/**
@@ -637,6 +643,9 @@
const u8 *persistent_dev;
const u8 *persistent_ssid;
size_t persistent_ssid_len;
+
+ const u8 *pref_freq_list;
+ size_t pref_freq_list_len;
};
@@ -682,6 +691,8 @@
u8 *op_channel);
/* p2p_parse.c */
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+ const void *src, size_t src_len);
int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
@@ -763,6 +774,8 @@
const u8 *ssid, size_t ssid_len);
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr);
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+ const u32 *preferred_freq_list, u32 size);
/* p2p_sd.c */
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -792,6 +805,8 @@
u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
void p2p_reselect_channel(struct p2p_data *p2p,
struct p2p_channels *intersection);
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+ struct p2p_device *dev, struct p2p_message *msg);
/* p2p_pd.c */
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index f5454f7..70da15a 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -85,6 +85,9 @@
p2p_buf_add_device_info(buf, p2p, peer);
p2p_buf_update_ie_hdr(buf, len);
+ p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+ p2p->num_pref_freq);
+
#ifdef CONFIG_WIFI_DISPLAY
if (wfd_ie)
wpabuf_put_buf(buf, wfd_ie);
@@ -281,7 +284,7 @@
if (!p2p_channels_includes(&intersection, reg_class, channel))
{
- p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction",
+ p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection",
op_freq);
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
@@ -343,6 +346,12 @@
p2p_reselect_channel(p2p, &intersection);
}
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
op_freq = p2p_channel_to_freq(p2p->op_reg_class,
p2p->op_channel);
if (op_freq < 0) {
@@ -534,6 +543,12 @@
peer_oper_freq = 0;
}
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, 0, dev, &msg);
+
p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
msg.group_bssid, channels, sa,
freq, peer_oper_freq);
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 980dddf..5d2299c 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -15,11 +15,29 @@
#include "p2p_i.h"
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+ const void *src, size_t src_len)
+{
+ size_t i;
+
+ if (src_len >= dst_len)
+ src_len = dst_len - 1;
+ os_memcpy(dst, src, src_len);
+ dst[src_len] = '\0';
+ for (i = 0; i < src_len; i++) {
+ if (dst[i] == '\0')
+ break;
+ if (is_ctrl_char(dst[i]))
+ dst[i] = '_';
+ }
+}
+
+
static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
struct p2p_message *msg)
{
const u8 *pos;
- size_t i, nlen;
+ u16 nlen;
char devtype[WPS_DEV_TYPE_BUFSIZE];
switch (id) {
@@ -149,21 +167,14 @@
pos += 2;
nlen = WPA_GET_BE16(pos);
pos += 2;
- if (data + len - pos < (int) nlen ||
- nlen > WPS_DEV_NAME_MAX_LEN) {
+ if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) {
wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
- "length %d (buf len %d)", (int) nlen,
+ "length %u (buf len %d)", nlen,
(int) (data + len - pos));
return -1;
}
- os_memcpy(msg->device_name, pos, nlen);
- msg->device_name[nlen] = '\0';
- for (i = 0; i < nlen; i++) {
- if (msg->device_name[i] == '\0')
- break;
- if (is_ctrl_char(msg->device_name[i]))
- msg->device_name[i] = '_';
- }
+ p2p_copy_filter_devname(msg->device_name,
+ sizeof(msg->device_name), pos, nlen);
wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
" primary device type %s device name '%s' "
"config methods 0x%x",
@@ -548,6 +559,9 @@
}
#endif /* CONFIG_WIFI_DISPLAY */
+ msg->pref_freq_list = elems.pref_freq_list;
+ msg->pref_freq_list_len = elems.pref_freq_list_len;
+
return 0;
}
@@ -634,49 +648,48 @@
gend = gi + gi_len;
while (g < gend) {
struct p2p_client_info *cli;
- const u8 *t, *cend;
- int count;
+ const u8 *cend;
+ u16 count;
+ u8 len;
cli = &info->client[info->num_clients];
- cend = g + 1 + g[0];
- if (cend > gend)
+ len = *g++;
+ if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1)
return -1; /* invalid data */
+ cend = g + len;
/* g at start of P2P Client Info Descriptor */
- /* t at Device Capability Bitmap */
- t = g + 1 + 2 * ETH_ALEN;
- if (t > cend)
- return -1; /* invalid data */
- cli->p2p_device_addr = g + 1;
- cli->p2p_interface_addr = g + 1 + ETH_ALEN;
- cli->dev_capab = t[0];
+ cli->p2p_device_addr = g;
+ g += ETH_ALEN;
+ cli->p2p_interface_addr = g;
+ g += ETH_ALEN;
+ cli->dev_capab = *g++;
- if (t + 1 + 2 + 8 + 1 > cend)
- return -1; /* invalid data */
+ cli->config_methods = WPA_GET_BE16(g);
+ g += 2;
+ cli->pri_dev_type = g;
+ g += 8;
- cli->config_methods = WPA_GET_BE16(&t[1]);
- cli->pri_dev_type = &t[3];
-
- t += 1 + 2 + 8;
- /* t at Number of Secondary Device Types */
- cli->num_sec_dev_types = *t++;
- if (t + 8 * cli->num_sec_dev_types > cend)
+ /* g at Number of Secondary Device Types */
+ len = *g++;
+ if (8 * len > cend - g)
return -1; /* invalid data */
- cli->sec_dev_types = t;
- t += 8 * cli->num_sec_dev_types;
+ cli->num_sec_dev_types = len;
+ cli->sec_dev_types = g;
+ g += 8 * len;
- /* t at Device Name in WPS TLV format */
- if (t + 2 + 2 > cend)
+ /* g at Device Name in WPS TLV format */
+ if (cend - g < 2 + 2)
return -1; /* invalid data */
- if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+ if (WPA_GET_BE16(g) != ATTR_DEV_NAME)
return -1; /* invalid Device Name TLV */
- t += 2;
- count = WPA_GET_BE16(t);
- t += 2;
- if (count > cend - t)
+ g += 2;
+ count = WPA_GET_BE16(g);
+ g += 2;
+ if (count > cend - g)
return -1; /* invalid Device Name TLV */
if (count >= WPS_DEV_NAME_MAX_LEN)
count = WPS_DEV_NAME_MAX_LEN;
- cli->dev_name = (const char *) t;
+ cli->dev_name = (const char *) g;
cli->dev_name_len = count;
g = cend;
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 86558f7..c416ab6 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,24 +40,38 @@
}
-static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+static void p2ps_add_new_group_info(struct p2p_data *p2p,
+ struct p2p_device *dev,
+ struct wpabuf *buf)
{
int found;
u8 intended_addr[ETH_ALEN];
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
int group_iface;
+ unsigned int force_freq;
if (!p2p->cfg->get_go_info)
return;
found = p2p->cfg->get_go_info(
p2p->cfg->cb_ctx, intended_addr, ssid,
- &ssid_len, &group_iface);
+ &ssid_len, &group_iface, &force_freq);
if (found) {
+ if (force_freq > 0) {
+ p2p->p2ps_prov->force_freq = force_freq;
+ p2p->p2ps_prov->pref_freq = 0;
+
+ if (dev)
+ p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
+ }
p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
ssid, ssid_len);
- p2p_buf_add_intended_addr(buf, intended_addr);
+
+ if (group_iface)
+ p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+ else
+ p2p_buf_add_intended_addr(buf, intended_addr);
} else {
if (!p2p->ssid_set) {
p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
@@ -82,76 +96,82 @@
struct wpabuf *buf, u16 config_methods)
{
struct p2ps_provision *prov = p2p->p2ps_prov;
- u8 feat_cap_mask[] = { 1, 0 };
+ struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
int shared_group = 0;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 go_dev_addr[ETH_ALEN];
+ u8 intended_addr[ETH_ALEN];
+ int follow_on_req_fail = prov->status >= 0 &&
+ prov->status != P2P_SC_SUCCESS_DEFERRED;
/* If we might be explicite group owner, add GO details */
- if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
- P2PS_SETUP_NEW))
- p2ps_add_new_group_info(p2p, buf);
+ if (!follow_on_req_fail &&
+ (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
+ p2ps_add_new_group_info(p2p, dev, buf);
if (prov->status >= 0)
p2p_buf_add_status(buf, (u8) prov->status);
else
prov->method = config_methods;
- if (p2p->cfg->get_persistent_group) {
- shared_group = p2p->cfg->get_persistent_group(
- p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
- go_dev_addr, ssid, &ssid_len);
- }
+ if (!follow_on_req_fail) {
+ if (p2p->cfg->get_persistent_group) {
+ shared_group = p2p->cfg->get_persistent_group(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+ NULL, 0, go_dev_addr, ssid, &ssid_len,
+ intended_addr);
+ }
- /* Add Operating Channel if conncap includes GO */
- if (shared_group ||
- (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
- P2PS_SETUP_NEW))) {
- u8 tmp;
+ if (shared_group ||
+ (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
- p2p_go_select_channel(p2p, dev, &tmp);
-
- if (p2p->op_reg_class && p2p->op_channel)
+ if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
+ (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class,
p2p->op_channel);
- else
- p2p_buf_add_operating_channel(buf, p2p->cfg->country,
- p2p->cfg->op_reg_class,
- p2p->cfg->op_channel);
}
- p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
-
- if (prov->info[0])
+ if (prov->status < 0 && prov->info[0])
p2p_buf_add_session_info(buf, prov->info);
- p2p_buf_add_connection_capability(buf, prov->conncap);
+ if (!follow_on_req_fail)
+ p2p_buf_add_connection_capability(buf, prov->conncap);
p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
- if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
- prov->conncap ==
- (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
- prov->conncap ==
- (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
- /* Add Config Timeout */
- p2p_buf_add_config_timeout(buf, p2p->go_timeout,
- p2p->client_timeout);
- }
+ if (!follow_on_req_fail) {
+ if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+ /* Add Config Timeout */
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+ }
- p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
- p2p->cfg->channel);
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country,
+ p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ }
p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
- p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
- feat_cap_mask);
+ p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
- if (shared_group)
+ if (shared_group) {
p2p_buf_add_persistent_group_info(buf, go_dev_addr,
ssid, ssid_len);
+ /* Add intended interface address if it is not added yet */
+ if ((prov->conncap == P2PS_SETUP_NONE ||
+ prov->conncap == P2PS_SETUP_CLIENT) &&
+ !is_zero_ether_addr(intended_addr))
+ p2p_buf_add_intended_addr(buf, intended_addr);
+ }
}
@@ -232,7 +252,9 @@
const u8 *group_id,
size_t group_id_len,
const u8 *persist_ssid,
- size_t persist_ssid_len)
+ size_t persist_ssid_len,
+ const u8 *fcap,
+ u16 fcap_len)
{
struct wpabuf *buf;
size_t extra = 0;
@@ -270,10 +292,14 @@
/* Add P2P IE for P2PS */
if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
- u8 feat_cap_mask[] = { 1, 0 };
u8 *len = p2p_buf_add_ie_hdr(buf);
struct p2ps_provision *prov = p2p->p2ps_prov;
u8 group_capab;
+ u8 conncap = 0;
+
+ if (status == P2P_SC_SUCCESS ||
+ status == P2P_SC_SUCCESS_DEFERRED)
+ conncap = prov->conncap;
if (!status && prov->status != -1)
status = prov->status;
@@ -290,33 +316,33 @@
group_capab);
p2p_buf_add_device_info(buf, p2p, NULL);
- if (persist_ssid && p2p->cfg->get_persistent_group &&
+ if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
(status == P2P_SC_SUCCESS ||
status == P2P_SC_SUCCESS_DEFERRED)) {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 go_dev_addr[ETH_ALEN];
+ u8 intended_addr[ETH_ALEN];
persist = p2p->cfg->get_persistent_group(
p2p->cfg->cb_ctx,
dev->info.p2p_device_addr,
persist_ssid, persist_ssid_len, go_dev_addr,
- ssid, &ssid_len);
- if (persist)
+ ssid, &ssid_len, intended_addr);
+ if (persist) {
p2p_buf_add_persistent_group_info(
buf, go_dev_addr, ssid, ssid_len);
+ if (!is_zero_ether_addr(intended_addr))
+ p2p_buf_add_intended_addr(
+ buf, intended_addr);
+ }
}
- if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
- p2ps_add_new_group_info(p2p, buf);
+ if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
+ p2ps_add_new_group_info(p2p, dev, buf);
/* Add Operating Channel if conncap indicates GO */
- if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
- u8 tmp;
-
- if (dev)
- p2p_go_select_channel(p2p, dev, &tmp);
-
+ if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
if (p2p->op_reg_class && p2p->op_channel)
p2p_buf_add_operating_channel(
buf, p2p->cfg->country,
@@ -329,23 +355,25 @@
p2p->cfg->op_channel);
}
- p2p_buf_add_channel_list(buf, p2p->cfg->country,
- &p2p->cfg->channels);
+ if (persist ||
+ (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->channels);
- if (!persist && (status == P2P_SC_SUCCESS ||
- status == P2P_SC_SUCCESS_DEFERRED))
- p2p_buf_add_connection_capability(buf, prov->conncap);
+ if (!persist && conncap)
+ p2p_buf_add_connection_capability(buf, conncap);
p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
- p2p_buf_add_config_timeout(buf, p2p->go_timeout,
- p2p->client_timeout);
+ if (persist ||
+ (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
p2p_buf_add_session_id(buf, prov->session_id,
prov->session_mac);
- p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
- feat_cap_mask);
+ p2p_buf_add_feature_capability(buf, fcap_len, fcap);
p2p_buf_update_ie_hdr(buf, len);
} else if (status != P2P_SC_SUCCESS || adv_id) {
u8 *len = p2p_buf_add_ie_hdr(buf);
@@ -400,6 +428,129 @@
}
+static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
+{
+ int i;
+
+ for (i = 0; cpt_priority[i]; i++)
+ if (req_cpt_mask & cpt_priority[i])
+ return cpt_priority[i];
+
+ return 0;
+}
+
+
+/* Check if the message contains a valid P2PS PD Request */
+static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
+ const u8 *addr)
+{
+ u8 group_id = 0;
+ u8 intended_addr = 0;
+ u8 operating_channel = 0;
+ u8 channel_list = 0;
+ u8 config_timeout = 0;
+ u8 listen_channel = 0;
+
+#define P2PS_PD_REQ_CHECK(_val, _attr) \
+do { \
+ if ((_val) && !msg->_attr) { \
+ p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
+ return -1; \
+ } \
+} while (0)
+
+ P2PS_PD_REQ_CHECK(1, adv_id);
+ P2PS_PD_REQ_CHECK(1, session_id);
+ P2PS_PD_REQ_CHECK(1, session_mac);
+ P2PS_PD_REQ_CHECK(1, adv_mac);
+ P2PS_PD_REQ_CHECK(1, capability);
+ P2PS_PD_REQ_CHECK(1, p2p_device_info);
+ P2PS_PD_REQ_CHECK(1, feature_cap);
+
+ /*
+ * We don't need to check Connection Capability, Persistent Group,
+ * and related attributes for follow-on PD Request with a status
+ * other than SUCCESS_DEFERRED.
+ */
+ if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
+ return 0;
+
+ P2PS_PD_REQ_CHECK(1, conn_cap);
+
+ /*
+ * Note 1: A feature capability attribute structure can be changed
+ * in the future. The assumption is that such modifications are
+ * backward compatible, therefore we allow processing of msg.feature_cap
+ * exceeding the size of the p2ps_feature_capab structure.
+ * Note 2: Verification of msg.feature_cap_len below has to be changed
+ * to allow 2 byte feature capability processing if
+ * struct p2ps_feature_capab is extended to include additional fields
+ * and it affects the structure size.
+ */
+ if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
+ p2p_dbg(p2p, "P2PS: Invalid feature capability len");
+ return -1;
+ }
+
+ switch (*msg->conn_cap) {
+ case P2PS_SETUP_NEW:
+ group_id = 1;
+ intended_addr = 1;
+ operating_channel = 1;
+ channel_list = 1;
+ config_timeout = 1;
+ listen_channel = 1;
+ break;
+ case P2PS_SETUP_CLIENT:
+ channel_list = 1;
+ listen_channel = 1;
+ break;
+ case P2PS_SETUP_GROUP_OWNER:
+ group_id = 1;
+ intended_addr = 1;
+ operating_channel = 1;
+ break;
+ case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
+ group_id = 1;
+ operating_channel = 1;
+ intended_addr = 1;
+ channel_list = 1;
+ config_timeout = 1;
+ break;
+ case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
+ group_id = 1;
+ intended_addr = 1;
+ operating_channel = 1;
+ channel_list = 1;
+ config_timeout = 1;
+ break;
+ default:
+ p2p_dbg(p2p, "Invalid P2PS PD connection capability");
+ return -1;
+ }
+
+ if (msg->persistent_dev) {
+ channel_list = 1;
+ config_timeout = 1;
+ if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
+ intended_addr = 1;
+ operating_channel = 1;
+ }
+ }
+
+ P2PS_PD_REQ_CHECK(group_id, group_id);
+ P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
+ P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
+ P2PS_PD_REQ_CHECK(channel_list, channel_list);
+ P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
+ P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
+
+#undef P2PS_PD_REQ_CHECK
+
+ return 0;
+}
+
+
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
@@ -413,11 +564,16 @@
u8 conncap = P2PS_SETUP_NEW;
u8 auto_accept = 0;
u32 session_id = 0;
- u8 session_mac[ETH_ALEN];
- u8 adv_mac[ETH_ALEN];
- u8 group_mac[ETH_ALEN];
+ u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+ u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+ const u8 *group_mac;
int passwd_id = DEV_PW_DEFAULT;
u16 config_methods;
+ u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ struct p2ps_feature_capab resp_fcap = { 0, 0 };
+ struct p2ps_feature_capab *req_fcap = NULL;
+ u8 remote_conncap;
+ u16 method;
if (p2p_parse(data, len, &msg))
return;
@@ -425,6 +581,7 @@
p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
" with config methods 0x%x (freq=%d)",
MAC2STR(sa), msg.wps_config_methods, rx_freq);
+ group_mac = msg.intended_addr;
dev = p2p_get_device(p2p, sa);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -435,256 +592,431 @@
0)) {
p2p_dbg(p2p, "Provision Discovery Request add device failed "
MACSTR, MAC2STR(sa));
+ goto out;
+ }
+
+ if (!dev) {
+ dev = p2p_get_device(p2p, sa);
+ if (!dev) {
+ p2p_dbg(p2p,
+ "Provision Discovery device not found "
+ MACSTR, MAC2STR(sa));
+ goto out;
+ }
}
} else if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
- if (!(msg.wps_config_methods &
- (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
- WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
- p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
- goto out;
- }
-
- /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
- if (!msg.adv_id && msg.group_id) {
- size_t i;
- for (i = 0; i < p2p->num_groups; i++) {
- if (p2p_group_is_group_id_match(p2p->groups[i],
- msg.group_id,
- msg.group_id_len))
- break;
- }
- if (i == p2p->num_groups) {
- p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
+ if (!msg.adv_id) {
+ allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
+ if (!(msg.wps_config_methods & allowed_config_methods)) {
+ p2p_dbg(p2p,
+ "Unsupported Config Methods in Provision Discovery Request");
goto out;
}
- }
- if (dev) {
- dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
- P2P_DEV_PD_PEER_KEYPAD |
- P2P_DEV_PD_PEER_P2PS);
+ /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+ if (msg.group_id) {
+ size_t i;
- /* Remove stale persistent groups */
- if (p2p->cfg->remove_stale_groups) {
- p2p->cfg->remove_stale_groups(
- p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
- msg.persistent_dev,
- msg.persistent_ssid, msg.persistent_ssid_len);
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (p2p_group_is_group_id_match(
+ p2p->groups[i],
+ msg.group_id, msg.group_id_len))
+ break;
+ }
+ if (i == p2p->num_groups) {
+ p2p_dbg(p2p,
+ "PD request for unknown P2P Group ID - reject");
+ goto out;
+ }
}
- }
- if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
- p2p_dbg(p2p, "Peer " MACSTR
- " requested us to show a PIN on display", MAC2STR(sa));
- if (dev)
- dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
- passwd_id = DEV_PW_USER_SPECIFIED;
- } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
- p2p_dbg(p2p, "Peer " MACSTR
- " requested us to write its PIN using keypad",
- MAC2STR(sa));
- if (dev)
- dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
- passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
- } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
- p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
- MAC2STR(sa));
- if (dev)
- dev->flags |= P2P_DEV_PD_PEER_P2PS;
- passwd_id = DEV_PW_P2PS_DEFAULT;
- }
+ } else {
+ allowed_config_methods |= WPS_CONFIG_P2PS;
- reject = P2P_SC_SUCCESS;
+ /*
+ * Set adv_id here, so in case of an error, a P2PS PD Response
+ * will be sent.
+ */
+ adv_id = WPA_GET_LE32(msg.adv_id);
+ if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
+ reject = P2P_SC_FAIL_INVALID_PARAMS;
+ goto out;
+ }
- os_memset(session_mac, 0, ETH_ALEN);
- os_memset(adv_mac, 0, ETH_ALEN);
- os_memset(group_mac, 0, ETH_ALEN);
-
- if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
- (msg.status || msg.conn_cap)) {
- u8 remote_conncap;
-
- if (msg.intended_addr)
- os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+ req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
session_id = WPA_GET_LE32(msg.session_id);
- adv_id = WPA_GET_LE32(msg.adv_id);
-
- if (!msg.status)
- p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
-
- p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
if (msg.conn_cap)
conncap = *msg.conn_cap;
- remote_conncap = conncap;
- if (p2ps_adv) {
- auto_accept = p2ps_adv->auto_accept;
- conncap = p2p->cfg->p2ps_group_capability(
- p2p->cfg->cb_ctx, conncap, auto_accept);
+ /*
+ * We need to verify a P2PS config methog in an initial PD
+ * request or in a follow-on PD request with the status
+ * SUCCESS_DEFERRED.
+ */
+ if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
+ !(msg.wps_config_methods & allowed_config_methods)) {
+ p2p_dbg(p2p,
+ "Unsupported Config Methods in Provision Discovery Request");
+ goto out;
+ }
- p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
- auto_accept, remote_conncap, conncap);
+ /*
+ * TODO: since we don't support multiple PD, reject PD request
+ * if we are in the middle of P2PS PD with some other peer
+ */
+ }
- if (p2ps_adv->config_methods &&
- !(msg.wps_config_methods &
- p2ps_adv->config_methods)) {
- p2p_dbg(p2p,
- "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
- p2ps_adv->config_methods,
- msg.wps_config_methods);
- reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- } else if (!p2ps_adv->state) {
- p2p_dbg(p2p, "P2PS state unavailable");
- reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
- } else if (!conncap) {
- p2p_dbg(p2p, "Conncap resolution failed");
- reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- }
+ dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+ P2P_DEV_PD_PEER_KEYPAD |
+ P2P_DEV_PD_PEER_P2PS);
- if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
- p2p_dbg(p2p, "Keypad - always defer");
- auto_accept = 0;
- }
+ if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+ p2p_dbg(p2p, "Peer " MACSTR
+ " requested us to show a PIN on display", MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ passwd_id = DEV_PW_USER_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ p2p_dbg(p2p, "Peer " MACSTR
+ " requested us to write its PIN using keypad",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+ p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_P2PS;
+ passwd_id = DEV_PW_P2PS_DEFAULT;
+ }
- if (auto_accept || reject != P2P_SC_SUCCESS) {
- struct p2ps_provision *tmp;
+ /* Remove stale persistent groups */
+ if (p2p->cfg->remove_stale_groups) {
+ p2p->cfg->remove_stale_groups(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+ msg.persistent_dev,
+ msg.persistent_ssid, msg.persistent_ssid_len);
+ }
- if (reject == P2P_SC_SUCCESS && !conncap) {
- reject =
- P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- }
+ reject = P2P_SC_SUCCESS;
- if (p2ps_setup_p2ps_prov(
- p2p, adv_id, session_id,
- msg.wps_config_methods,
- session_mac, adv_mac) < 0) {
- reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
- goto out;
- }
+ /*
+ * End of a legacy P2P PD Request processing, from this point continue
+ * with P2PS one.
+ */
+ if (!msg.adv_id)
+ goto out;
- tmp = p2p->p2ps_prov;
- if (conncap) {
- tmp->conncap = conncap;
- tmp->status = P2P_SC_SUCCESS;
- } else {
- tmp->conncap = auto_accept;
- tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- }
+ remote_conncap = conncap;
- if (reject != P2P_SC_SUCCESS)
- goto out;
- }
- } else if (!msg.status) {
+ if (!msg.status) {
+ unsigned int forced_freq, pref_freq;
+
+ if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
+ p2p_dbg(p2p,
+ "P2PS PD adv mac does not match the local one");
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
goto out;
}
- if (!msg.status && !auto_accept &&
- (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
- struct p2ps_provision *tmp;
+ p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+ if (!p2ps_adv) {
+ p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+ p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
- if (!conncap) {
- reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- goto out;
+ auto_accept = p2ps_adv->auto_accept;
+ conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+ conncap, auto_accept,
+ &forced_freq,
+ &pref_freq);
+
+ p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+ auto_accept, remote_conncap, conncap);
+
+ p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
+
+ resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
+ req_fcap->cpt);
+
+ p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
+ p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
+
+ if (!resp_fcap.cpt) {
+ p2p_dbg(p2p,
+ "Incompatible P2PS feature capability CPT bitmask");
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if (p2ps_adv->config_methods &&
+ !(msg.wps_config_methods &
+ p2ps_adv->config_methods)) {
+ p2p_dbg(p2p,
+ "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+ p2ps_adv->config_methods,
+ msg.wps_config_methods);
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if (!p2ps_adv->state) {
+ p2p_dbg(p2p, "P2PS state unavailable");
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else if (!conncap) {
+ p2p_dbg(p2p, "Conncap resolution failed");
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ p2p_dbg(p2p, "Keypad - always defer");
+ auto_accept = 0;
+ }
+
+ if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+ msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+ msg.channel_list && msg.channel_list_len &&
+ p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ msg.channel_list,
+ msg.channel_list_len) < 0) {
+ p2p_dbg(p2p,
+ "No common channels - force deferred flow");
+ auto_accept = 0;
+ }
+
+ if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
+ msg.persistent_dev) && msg.operating_channel) {
+ struct p2p_channels intersect;
+
+ /*
+ * There are cases where only the operating channel is
+ * provided. This requires saving the channel as the
+ * supported channel list, and verifying that it is
+ * supported.
+ */
+ if (dev->channels.reg_classes == 0 ||
+ !p2p_channels_includes(&dev->channels,
+ msg.operating_channel[3],
+ msg.operating_channel[4])) {
+ struct p2p_channels *ch = &dev->channels;
+
+ os_memset(ch, 0, sizeof(*ch));
+ ch->reg_class[0].reg_class =
+ msg.operating_channel[3];
+ ch->reg_class[0].channel[0] =
+ msg.operating_channel[4];
+ ch->reg_class[0].channels = 1;
+ ch->reg_classes = 1;
}
+ p2p_channels_intersect(&p2p->channels, &dev->channels,
+ &intersect);
+
+ if (intersect.reg_classes == 0) {
+ p2p_dbg(p2p,
+ "No common channels - force deferred flow");
+ auto_accept = 0;
+ }
+ }
+
+ if (auto_accept || reject != P2P_SC_SUCCESS) {
+ struct p2ps_provision *tmp;
+
if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
msg.wps_config_methods,
session_mac, adv_mac) < 0) {
reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
goto out;
}
+
tmp = p2p->p2ps_prov;
- reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
- tmp->status = reject;
- }
-
- if (msg.status) {
- if (*msg.status &&
- *msg.status != P2P_SC_SUCCESS_DEFERRED) {
- reject = *msg.status;
- } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
- p2p->p2ps_prov) {
- u16 method = p2p->p2ps_prov->method;
-
- conncap = p2p->cfg->p2ps_group_capability(
- p2p->cfg->cb_ctx, remote_conncap,
- p2p->p2ps_prov->conncap);
-
- p2p_dbg(p2p,
- "Conncap: local:%d remote:%d result:%d",
- p2p->p2ps_prov->conncap,
- remote_conncap, conncap);
-
- /*
- * Ensure that if we asked for PIN originally,
- * our method is consistent with original
- * request.
- */
- if (method & WPS_CONFIG_DISPLAY)
- method = WPS_CONFIG_KEYPAD;
- else if (method & WPS_CONFIG_KEYPAD)
- method = WPS_CONFIG_DISPLAY;
-
- /* Reject this "Deferred Accept* if incompatible
- * conncap or method */
- if (!conncap ||
- !(msg.wps_config_methods & method))
- reject =
- P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
- else
- reject = P2P_SC_SUCCESS;
-
- p2p->p2ps_prov->status = reject;
- p2p->p2ps_prov->conncap = conncap;
+ tmp->force_freq = forced_freq;
+ tmp->pref_freq = pref_freq;
+ if (conncap) {
+ tmp->conncap = conncap;
+ tmp->status = P2P_SC_SUCCESS;
+ } else {
+ tmp->conncap = auto_accept;
+ tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
}
+
+ if (reject != P2P_SC_SUCCESS)
+ goto out;
}
}
+ if (!msg.status && !auto_accept &&
+ (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+ struct p2ps_provision *tmp;
+
+ if (!conncap) {
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+
+ if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+ msg.wps_config_methods,
+ session_mac, adv_mac) < 0) {
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ goto out;
+ }
+ tmp = p2p->p2ps_prov;
+ reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ tmp->status = reject;
+ }
+
+ /* Not a P2PS Follow-on PD */
+ if (!msg.status)
+ goto out;
+
+ if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+ reject = *msg.status;
+ goto out;
+ }
+
+ if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
+ goto out;
+
+ if (p2p->p2ps_prov->adv_id != adv_id ||
+ os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
+ p2p_dbg(p2p,
+ "P2PS Follow-on PD with mismatch Advertisement ID/MAC");
+ goto out;
+ }
+
+ if (p2p->p2ps_prov->session_id != session_id ||
+ os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
+ p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
+ goto out;
+ }
+
+ method = p2p->p2ps_prov->method;
+
+ conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+ remote_conncap,
+ p2p->p2ps_prov->conncap,
+ &p2p->p2ps_prov->force_freq,
+ &p2p->p2ps_prov->pref_freq);
+
+ resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
+ req_fcap->cpt);
+
+ p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
+ p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
+
+ p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+ p2p->p2ps_prov->pref_freq, 0);
+
+ /*
+ * Ensure that if we asked for PIN originally, our method is consistent
+ * with original request.
+ */
+ if (method & WPS_CONFIG_DISPLAY)
+ method = WPS_CONFIG_KEYPAD;
+ else if (method & WPS_CONFIG_KEYPAD)
+ method = WPS_CONFIG_DISPLAY;
+
+ if (!conncap || !(msg.wps_config_methods & method)) {
+ /*
+ * Reject this "Deferred Accept*
+ * if incompatible conncap or method
+ */
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if (!resp_fcap.cpt) {
+ p2p_dbg(p2p,
+ "Incompatible P2PS feature capability CPT bitmask");
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+ msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+ msg.channel_list && msg.channel_list_len &&
+ p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ msg.channel_list,
+ msg.channel_list_len) < 0) {
+ p2p_dbg(p2p,
+ "No common channels in Follow-On Provision Discovery Request");
+ reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ } else {
+ reject = P2P_SC_SUCCESS;
+ }
+
+ dev->oper_freq = 0;
+ if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
+ u8 tmp;
+
+ if (msg.operating_channel)
+ dev->oper_freq =
+ p2p_channel_to_freq(msg.operating_channel[3],
+ msg.operating_channel[4]);
+
+ if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
+ p2p_go_select_channel(p2p, dev, &tmp) < 0)
+ reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ }
+
+ p2p->p2ps_prov->status = reject;
+ p2p->p2ps_prov->conncap = conncap;
+
out:
if (reject == P2P_SC_SUCCESS ||
reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
config_methods = msg.wps_config_methods;
else
config_methods = 0;
- resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
- config_methods, adv_id,
- msg.group_id, msg.group_id_len,
- msg.persistent_ssid,
- msg.persistent_ssid_len);
- if (resp == NULL) {
- p2p_parse_free(&msg);
- return;
- }
- p2p_dbg(p2p, "Sending Provision Discovery Response");
- if (rx_freq > 0)
- freq = rx_freq;
- else
- freq = p2p_channel_to_freq(p2p->cfg->reg_class,
- p2p->cfg->channel);
- if (freq < 0) {
- p2p_dbg(p2p, "Unknown regulatory class/channel");
- wpabuf_free(resp);
- p2p_parse_free(&msg);
- return;
- }
- p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
- if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
- p2p->cfg->dev_addr,
- wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
- p2p_dbg(p2p, "Failed to send Action frame");
- } else
- p2p->send_action_in_progress = 1;
- wpabuf_free(resp);
+ /*
+ * Send PD Response for an initial PD Request or for follow-on
+ * PD Request with P2P_SC_SUCCESS_DEFERRED status.
+ */
+ if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
+ resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
+ reject, config_methods, adv_id,
+ msg.group_id, msg.group_id_len,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ (const u8 *) &resp_fcap,
+ sizeof(resp_fcap));
+ if (!resp) {
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p_dbg(p2p, "Sending Provision Discovery Response");
+ if (rx_freq > 0)
+ freq = rx_freq;
+ else
+ freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+ p2p->cfg->channel);
+ if (freq < 0) {
+ p2p_dbg(p2p, "Unknown regulatory class/channel");
+ wpabuf_free(resp);
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
+ if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+ p2p->cfg->dev_addr,
+ wpabuf_head(resp), wpabuf_len(resp),
+ 200) < 0)
+ p2p_dbg(p2p, "Failed to send Action frame");
+ else
+ p2p->send_action_in_progress = 1;
+
+ wpabuf_free(resp);
+ }
+
+ if (!dev) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
+ freq = 0;
+ if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
+ freq = p2p_channel_to_freq(p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq < 0)
+ freq = 0;
+ }
if (!p2p->cfg->p2ps_prov_complete) {
/* Don't emit anything */
@@ -696,7 +1028,7 @@
NULL, adv_id, session_id,
0, 0, msg.persistent_ssid,
msg.persistent_ssid_len,
- 0, 0, NULL);
+ 0, 0, NULL, NULL, 0, freq);
} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
p2p->p2ps_prov) {
p2p->p2ps_prov->status = reject;
@@ -709,7 +1041,7 @@
session_id, conncap, 0,
msg.persistent_ssid,
msg.persistent_ssid_len, 0,
- 0, NULL);
+ 0, NULL, NULL, 0, freq);
else
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
*msg.status,
@@ -719,7 +1051,9 @@
passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len, 0,
- 0, NULL);
+ 0, NULL,
+ (const u8 *) &resp_fcap,
+ sizeof(resp_fcap), freq);
} else if (msg.status && p2p->p2ps_prov) {
p2p->p2ps_prov->status = P2P_SC_SUCCESS;
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
@@ -728,7 +1062,9 @@
passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
- 0, 0, NULL);
+ 0, 0, NULL,
+ (const u8 *) &resp_fcap,
+ sizeof(resp_fcap), freq);
} else if (msg.status) {
} else if (auto_accept && reject == P2P_SC_SUCCESS) {
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
@@ -737,7 +1073,9 @@
conncap, passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
- 0, 0, NULL);
+ 0, 0, NULL,
+ (const u8 *) &resp_fcap,
+ sizeof(resp_fcap), freq);
} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
(!msg.session_info || !msg.session_info_len)) {
p2p->p2ps_prov->method = msg.wps_config_methods;
@@ -748,7 +1086,9 @@
conncap, passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
- 0, 1, NULL);
+ 0, 1, NULL,
+ (const u8 *) &resp_fcap,
+ sizeof(resp_fcap), freq);
} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
size_t buf_len = msg.session_info_len;
char *buf = os_malloc(2 * buf_len + 1);
@@ -764,14 +1104,46 @@
adv_mac, session_mac, group_mac, adv_id,
session_id, conncap, passwd_id,
msg.persistent_ssid, msg.persistent_ssid_len,
- 0, 1, buf);
+ 0, 1, buf,
+ (const u8 *) &resp_fcap, sizeof(resp_fcap),
+ freq);
os_free(buf);
}
}
- if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
+ /*
+ * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+ * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+ * Call it either on legacy P2P PD or on P2PS PD only if we need to
+ * enter/show PIN.
+ *
+ * The callback is called in the following cases:
+ * 1. Legacy P2P PD request, response status SUCCESS
+ * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
+ * response status: SUCCESS
+ * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE,
+ * response status: INFO_CURRENTLY_UNAVAILABLE
+ * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
+ * response status: INFO_CURRENTLY_UNAVAILABLE
+ * 5. P2PS follow-on with SUCCESS_DEFERRED,
+ * advertiser role: DISPLAY, autoaccept: FALSE,
+ * seeker: KEYPAD, response status: SUCCESS
+ */
+ if (p2p->cfg->prov_disc_req &&
+ ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
+ (!msg.status &&
+ (reject == P2P_SC_SUCCESS ||
+ reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
+ passwd_id == DEV_PW_USER_SPECIFIED) ||
+ (!msg.status &&
+ reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+ (reject == P2P_SC_SUCCESS &&
+ msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+ passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
const u8 *dev_addr = sa;
+
if (msg.p2p_device_addr)
dev_addr = msg.p2p_device_addr;
p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
@@ -783,10 +1155,133 @@
0,
msg.group_id, msg.group_id_len);
}
+
+ if (reject == P2P_SC_SUCCESS) {
+ switch (config_methods) {
+ case WPS_CONFIG_DISPLAY:
+ dev->wps_prov_info = WPS_CONFIG_KEYPAD;
+ break;
+ case WPS_CONFIG_KEYPAD:
+ dev->wps_prov_info = WPS_CONFIG_DISPLAY;
+ break;
+ case WPS_CONFIG_PUSHBUTTON:
+ dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
+ break;
+ case WPS_CONFIG_P2PS:
+ dev->wps_prov_info = WPS_CONFIG_P2PS;
+ break;
+ default:
+ dev->wps_prov_info = 0;
+ break;
+ }
+
+ if (msg.intended_addr)
+ os_memcpy(dev->interface_addr, msg.intended_addr,
+ ETH_ALEN);
+ }
p2p_parse_free(&msg);
}
+static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
+ struct p2p_message *msg)
+{
+ u8 conn_cap_go = 0;
+ u8 conn_cap_cli = 0;
+ u32 session_id;
+ u32 adv_id;
+
+#define P2PS_PD_RESP_CHECK(_val, _attr) \
+ do { \
+ if ((_val) && !msg->_attr) { \
+ p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
+ return -1; \
+ } \
+ } while (0)
+
+ P2PS_PD_RESP_CHECK(1, status);
+ P2PS_PD_RESP_CHECK(1, adv_id);
+ P2PS_PD_RESP_CHECK(1, adv_mac);
+ P2PS_PD_RESP_CHECK(1, capability);
+ P2PS_PD_RESP_CHECK(1, p2p_device_info);
+ P2PS_PD_RESP_CHECK(1, session_id);
+ P2PS_PD_RESP_CHECK(1, session_mac);
+ P2PS_PD_RESP_CHECK(1, feature_cap);
+
+ session_id = WPA_GET_LE32(msg->session_id);
+ adv_id = WPA_GET_LE32(msg->adv_id);
+
+ if (p2p->p2ps_prov->session_id != session_id) {
+ p2p_dbg(p2p,
+ "Ignore PD Response with unexpected Session ID");
+ return -1;
+ }
+
+ if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
+ ETH_ALEN)) {
+ p2p_dbg(p2p,
+ "Ignore PD Response with unexpected Session MAC");
+ return -1;
+ }
+
+ if (p2p->p2ps_prov->adv_id != adv_id) {
+ p2p_dbg(p2p,
+ "Ignore PD Response with unexpected Advertisement ID");
+ return -1;
+ }
+
+ if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
+ p2p_dbg(p2p,
+ "Ignore PD Response with unexpected Advertisement MAC");
+ return -1;
+ }
+
+ if (msg->listen_channel) {
+ p2p_dbg(p2p,
+ "Ignore malformed PD Response - unexpected Listen Channel");
+ return -1;
+ }
+
+ if (*msg->status == P2P_SC_SUCCESS &&
+ !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
+ p2p_dbg(p2p,
+ "Ignore malformed PD Response - either conn_cap or persistent group should be present");
+ return -1;
+ }
+
+ if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
+ p2p_dbg(p2p,
+ "Ignore malformed PD Response - persistent group is present, but the status isn't success");
+ return -1;
+ }
+
+ if (msg->conn_cap) {
+ conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
+ conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
+ }
+
+ P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+ channel_list);
+ P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+ config_timeout);
+
+ P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
+ P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
+ P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
+ /*
+ * TODO: Also validate that operating channel is present if the device
+ * is a GO in a persistent group. We can't do it here since we don't
+ * know what is the role of the peer. It should be probably done in
+ * p2ps_prov_complete callback, but currently operating channel isn't
+ * passed to it.
+ */
+
+#undef P2PS_PD_RESP_CHECK
+
+ return 0;
+}
+
+
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
@@ -794,24 +1289,26 @@
struct p2p_device *dev;
u16 report_config_methods = 0, req_config_methods;
u8 status = P2P_SC_SUCCESS;
- int success = 0;
u32 adv_id = 0;
u8 conncap = P2PS_SETUP_NEW;
u8 adv_mac[ETH_ALEN];
- u8 group_mac[ETH_ALEN];
+ const u8 *group_mac;
int passwd_id = DEV_PW_DEFAULT;
+ int p2ps_seeker;
if (p2p_parse(data, len, &msg))
return;
+ if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
+ p2p_parse_free(&msg);
+ return;
+ }
+
/* Parse the P2PS members present */
if (msg.status)
status = *msg.status;
- if (msg.intended_addr)
- os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
- else
- os_memset(group_mac, 0, ETH_ALEN);
+ group_mac = msg.intended_addr;
if (msg.adv_mac)
os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
@@ -859,6 +1356,8 @@
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
}
+ p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
+
/*
* Use a local copy of the requested config methods since
* p2p_reset_pending_pd() can clear this in the peer entry.
@@ -907,30 +1406,80 @@
passwd_id = DEV_PW_P2PS_DEFAULT;
}
- if ((msg.conn_cap || msg.persistent_dev) &&
- msg.adv_id &&
- (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+ if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
p2p->p2ps_prov) {
+ dev->oper_freq = 0;
+
+ /*
+ * Save the reported channel list and operating frequency.
+ * Note that the specification mandates that the responder
+ * should include in the channel list only channels reported by
+ * the initiator, so this is only a sanity check, and if this
+ * fails the flow would continue, although it would probably
+ * fail. Same is true for the operating channel.
+ */
+ if (msg.channel_list && msg.channel_list_len &&
+ p2p_peer_channels_check(p2p, &p2p->channels, dev,
+ msg.channel_list,
+ msg.channel_list_len) < 0)
+ p2p_dbg(p2p, "P2PS PD Response - no common channels");
+
+ if (msg.operating_channel) {
+ if (p2p_channels_includes(&p2p->channels,
+ msg.operating_channel[3],
+ msg.operating_channel[4]) &&
+ p2p_channels_includes(&dev->channels,
+ msg.operating_channel[3],
+ msg.operating_channel[4])) {
+ dev->oper_freq =
+ p2p_channel_to_freq(
+ msg.operating_channel[3],
+ msg.operating_channel[4]);
+ } else {
+ p2p_dbg(p2p,
+ "P2PS PD Response - invalid operating channel");
+ }
+ }
+
if (p2p->cfg->p2ps_prov_complete) {
+ int freq = 0;
+
+ if (conncap == P2PS_SETUP_GROUP_OWNER) {
+ u8 tmp;
+
+ /*
+ * Re-select the operating channel as it is
+ * possible that original channel is no longer
+ * valid. This should not really fail.
+ */
+ if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
+ p2p_dbg(p2p,
+ "P2PS PD channel selection failed");
+
+ freq = p2p_channel_to_freq(p2p->op_reg_class,
+ p2p->op_channel);
+ if (freq < 0)
+ freq = 0;
+ }
+
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx, status, sa, adv_mac,
p2p->p2ps_prov->session_mac,
group_mac, adv_id, p2p->p2ps_prov->session_id,
conncap, passwd_id, msg.persistent_ssid,
- msg.persistent_ssid_len, 1, 0, NULL);
+ msg.persistent_ssid_len, 1, 0, NULL,
+ msg.feature_cap, msg.feature_cap_len, freq);
}
p2ps_prov_free(p2p);
- }
-
- if (status != P2P_SC_SUCCESS &&
- status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
- status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+ } else if (status != P2P_SC_SUCCESS &&
+ status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
if (p2p->cfg->p2ps_prov_complete)
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx, status, sa, adv_mac,
p2p->p2ps_prov->session_mac,
group_mac, adv_id, p2p->p2ps_prov->session_id,
- 0, 0, NULL, 0, 1, 0, NULL);
+ 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0);
p2ps_prov_free(p2p);
}
@@ -966,13 +1515,12 @@
p2p->cfg->cb_ctx, sa,
P2P_PROV_DISC_INFO_UNAVAILABLE,
adv_id, adv_mac, NULL);
- } else if (msg.wps_config_methods != dev->req_config_methods ||
- status != P2P_SC_SUCCESS) {
+ } else if (status != P2P_SC_SUCCESS) {
p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
- P2P_PROV_DISC_REJECTED, 0,
- NULL, NULL);
+ P2P_PROV_DISC_REJECTED,
+ adv_id, adv_mac, NULL);
p2p_parse_free(&msg);
p2ps_prov_free(p2p);
goto out;
@@ -980,9 +1528,10 @@
/* Store the provisioning info */
dev->wps_prov_info = msg.wps_config_methods;
+ if (msg.intended_addr)
+ os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
p2p_parse_free(&msg);
- success = 1;
out:
dev->req_config_methods = 0;
@@ -994,7 +1543,28 @@
p2p_connect_send(p2p, dev);
return;
}
- if (success && p2p->cfg->prov_disc_resp)
+
+ /*
+ * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+ * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+ * Call it only for a legacy P2P PD or for P2PS PD scenarios where
+ * show/enter PIN events are needed.
+ *
+ * The callback is called in the following cases:
+ * 1. Legacy P2P PD response with a status SUCCESS
+ * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
+ * response status: SUCCESS, local method KEYPAD
+ * 3. P2PS, advertiser method: KEYPAD,Seeker side,
+ * response status: INFO_CURRENTLY_UNAVAILABLE,
+ * local method: DISPLAY
+ */
+ if (p2p->cfg->prov_disc_resp &&
+ ((status == P2P_SC_SUCCESS && !adv_id) ||
+ (p2ps_seeker && status == P2P_SC_SUCCESS &&
+ passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+ (p2ps_seeker &&
+ status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ passwd_id == DEV_PW_USER_SPECIFIED)))
p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
report_config_methods);
@@ -1058,6 +1628,10 @@
"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
p2p->p2ps_prov->method, p2p->p2ps_prov->status,
dev->req_config_methods);
+
+ if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+ p2p->p2ps_prov->pref_freq, 1) < 0)
+ return -1;
}
req = p2p_build_prov_disc_req(p2p, dev, join);
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 1a2af04..559af9d 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -28,11 +28,11 @@
pos = wpabuf_head(wfd);
end = pos + wpabuf_len(wfd);
- while (pos + 3 <= end) {
+ while (end - pos >= 3) {
subelem = *pos++;
len = WPA_GET_BE16(pos);
pos += 2;
- if (pos + len > end)
+ if (len > end - pos)
break;
if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
@@ -355,11 +355,11 @@
pos++;
slen = *pos++;
- next = pos + slen;
- if (next > end || slen < 2) {
+ if (slen > end - pos || slen < 2) {
p2p_dbg(p2p, "Invalid IE in GAS Initial Request");
return;
}
+ next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -370,16 +370,16 @@
pos = next;
/* Query Request */
- if (pos + 2 > end)
+ if (end - pos < 2)
return;
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end)
+ if (slen > end - pos)
return;
end = pos + slen;
/* ANQP Query Request */
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -389,7 +389,7 @@
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end || slen < 3 + 1) {
+ if (slen > end - pos || slen < 3 + 1) {
p2p_dbg(p2p, "Invalid ANQP Query Request length");
return;
}
@@ -401,7 +401,7 @@
}
pos += 4;
- if (pos + 2 > end)
+ if (end - pos < 2)
return;
update_indic = WPA_GET_LE16(pos);
p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -512,11 +512,11 @@
pos++;
slen = *pos++;
- next = pos + slen;
- if (next > end || slen < 2) {
+ if (slen > end - pos || slen < 2) {
p2p_dbg(p2p, "Invalid IE in GAS Initial Response");
return;
}
+ next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -527,14 +527,14 @@
pos = next;
/* Query Response */
- if (pos + 2 > end) {
+ if (end - pos < 2) {
p2p_dbg(p2p, "Too short Query Response");
return;
}
slen = WPA_GET_LE16(pos);
pos += 2;
p2p_dbg(p2p, "Query Response Length: %d", slen);
- if (pos + slen > end) {
+ if (slen > end - pos) {
p2p_dbg(p2p, "Not enough Query Response data");
return;
}
@@ -552,7 +552,7 @@
}
/* ANQP Query Response */
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -562,7 +562,7 @@
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end || slen < 3 + 1) {
+ if (slen > end - pos || slen < 3 + 1) {
p2p_dbg(p2p, "Invalid ANQP Query Response length");
return;
}
@@ -574,7 +574,7 @@
}
pos += 4;
- if (pos + 2 > end)
+ if (end - pos < 2)
return;
update_indic = WPA_GET_LE16(pos);
p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -727,11 +727,11 @@
pos++;
slen = *pos++;
- next = pos + slen;
- if (next > end || slen < 2) {
+ if (slen > end - pos || slen < 2) {
p2p_dbg(p2p, "Invalid IE in GAS Comeback Response");
return;
}
+ next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -742,14 +742,14 @@
pos = next;
/* Query Response */
- if (pos + 2 > end) {
+ if (end - pos < 2) {
p2p_dbg(p2p, "Too short Query Response");
return;
}
slen = WPA_GET_LE16(pos);
pos += 2;
p2p_dbg(p2p, "Query Response Length: %d", slen);
- if (pos + slen > end) {
+ if (slen > end - pos) {
p2p_dbg(p2p, "Not enough Query Response data");
return;
}
@@ -768,7 +768,7 @@
}
/* ANQP Query Response */
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -783,7 +783,7 @@
p2p_dbg(p2p, "Invalid ANQP Query Response length");
return;
}
- if (pos + 4 > end)
+ if (end - pos < 4)
return;
if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
@@ -793,7 +793,7 @@
}
pos += 4;
- if (pos + 2 > end)
+ if (end - pos < 2)
return;
p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index eee3c5a..2e2aa8a 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/defs.h"
#include "common/ieee802_11_common.h"
#include "p2p_i.h"
@@ -67,59 +68,11 @@
*/
int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
{
- /* TODO: more operating classes */
- if (freq >= 2412 && freq <= 2472) {
- if ((freq - 2407) % 5)
- return -1;
+ if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) ==
+ NUM_HOSTAPD_MODES)
+ return -1;
- *op_class = 81; /* 2.407 GHz, channels 1..13 */
- *channel = (freq - 2407) / 5;
- return 0;
- }
-
- if (freq == 2484) {
- *op_class = 82; /* channel 14 */
- *channel = 14;
- return 0;
- }
-
- if (freq >= 5180 && freq <= 5240) {
- if ((freq - 5000) % 5)
- return -1;
-
- *op_class = 115; /* 5 GHz, channels 36..48 */
- *channel = (freq - 5000) / 5;
- return 0;
- }
-
- if (freq >= 5745 && freq <= 5805) {
- if ((freq - 5000) % 5)
- return -1;
-
- *op_class = 124; /* 5 GHz, channels 149..161 */
- *channel = (freq - 5000) / 5;
- return 0;
- }
-
- if (freq >= 5745 && freq <= 5845) {
- if ((freq - 5000) % 5)
- return -1;
-
- *op_class = 125; /* 5 GHz, channels 149..169 */
- *channel = (freq - 5000) / 5;
- return 0;
- }
-
- if (freq >= 58320 && freq <= 64800) {
- if ((freq - 58320) % 2160)
- return -1;
-
- *op_class = 180; /* 60 GHz, channels 1..4 */
- *channel = (freq - 56160) / 2160;
- return 0;
- }
-
- return -1;
+ return 0;
}
@@ -506,12 +459,22 @@
break;
for (j = 0; j < c->channels; j++) {
int freq;
+ unsigned int k;
+
if (idx + 1 == max_len)
break;
freq = p2p_channel_to_freq(c->reg_class,
c->channel[j]);
if (freq < 0)
continue;
+
+ for (k = 0; k < idx; k++) {
+ if (freq_list[k] == freq)
+ break;
+ }
+
+ if (k < idx)
+ continue;
freq_list[idx++] = freq;
}
}
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 1ebfd11..266b29f 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -173,6 +173,7 @@
{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+ { RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
@@ -703,7 +704,7 @@
attr = (struct radius_attr_hdr *) pos;
- if (pos + attr->length > end || attr->length < sizeof(*attr))
+ if (attr->length > end - pos || attr->length < sizeof(*attr))
goto fail;
/* TODO: check that attr->length is suitable for attr->type */
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 5977339..f14de53 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
/*
* RADIUS message processing
- * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -52,6 +52,7 @@
RADIUS_ATTR_USER_PASSWORD = 2,
RADIUS_ATTR_NAS_IP_ADDRESS = 4,
RADIUS_ATTR_NAS_PORT = 5,
+ RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
RADIUS_ATTR_FRAMED_MTU = 12,
RADIUS_ATTR_REPLY_MESSAGE = 18,
RADIUS_ATTR_STATE = 24,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 693f61e..0bcdb45 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -407,7 +407,6 @@
static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
struct radius_client_data *radius = eloop_ctx;
- struct hostapd_radius_servers *conf = radius->conf;
struct os_reltime now;
os_time_t first;
struct radius_msg_list *entry, *prev, *tmp;
@@ -476,10 +475,10 @@
(long int) (first - now.sec));
}
- if (auth_failover && conf->num_auth_servers > 1)
+ if (auth_failover)
radius_client_auth_failover(radius);
- if (acct_failover && conf->num_acct_servers > 1)
+ if (acct_failover)
radius_client_acct_failover(radius);
}
@@ -1015,6 +1014,9 @@
int sel_sock;
struct radius_msg_list *entry;
struct hostapd_radius_servers *conf = radius->conf;
+ struct sockaddr_in disconnect_addr = {
+ .sin_family = AF_UNSPEC,
+ };
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -1023,6 +1025,12 @@
hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
nserv->port);
+ if (oserv && oserv == nserv) {
+ /* Reconnect to same server, flush */
+ if (auth)
+ radius_client_flush(radius, 1);
+ }
+
if (oserv && oserv != nserv &&
(nserv->shared_secret_len != oserv->shared_secret_len ||
os_memcmp(nserv->shared_secret, oserv->shared_secret,
@@ -1125,6 +1133,11 @@
}
}
+ /* Force a reconnect by disconnecting the socket first */
+ if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
+ sizeof(disconnect_addr)) < 0)
+ wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+
if (connect(sel_sock, addr, addrlen) < 0) {
wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
return -1;
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 39ceea8..b7d991b 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -245,7 +245,7 @@
(u8 *) &val, 4);
if (res == 4) {
u32 timestamp = ntohl(val);
- if ((unsigned int) abs(now.sec - timestamp) >
+ if ((unsigned int) abs((int) (now.sec - timestamp)) >
das->time_window) {
wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
"Event-Timestamp (%u; local time %u) in "
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index bdb7e42..744283c 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -265,6 +265,8 @@
struct dl_list erp_keys; /* struct eap_server_erp_key */
+ unsigned int tls_session_lifetime;
+
/**
* wps - Wi-Fi Protected Setup context
*
@@ -688,6 +690,7 @@
eap_conf.server_id = (const u8 *) data->server_id;
eap_conf.server_id_len = os_strlen(data->server_id);
eap_conf.erp = data->erp;
+ eap_conf.tls_session_lifetime = data->tls_session_lifetime;
radius_server_testing_options(sess, &eap_conf);
sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
&eap_conf);
@@ -1745,6 +1748,7 @@
}
data->erp = conf->erp;
data->erp_domain = conf->erp_domain;
+ data->tls_session_lifetime = conf->tls_session_lifetime;
if (conf->subscr_remediation_url) {
data->subscr_remediation_url =
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index ca4e38c..7a25802 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -170,6 +170,8 @@
const char *erp_domain;
+ unsigned int tls_session_lifetime;
+
/**
* wps - Wi-Fi Protected Setup context
*
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index ef7b683..b221e12 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -130,7 +130,7 @@
struct rsn_pmksa_cache_entry *entry, *pos, *prev;
struct os_reltime now;
- if (pmk_len > PMK_LEN)
+ if (pmk_len > PMK_LEN_MAX)
return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck)
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index f8e040e..7ec09ea 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -15,7 +15,7 @@
struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next;
u8 pmkid[PMKID_LEN];
- u8 pmk[PMK_LEN];
+ u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 6b1df71..48752d7 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "utils/os.h"
#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
#include "crypto/aes_wrap.h"
@@ -626,9 +627,15 @@
*/
if (peer->initiator) {
+ u8 addr[ETH_ALEN];
+
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
" - try to renew", MAC2STR(peer->addr));
- wpa_tdls_start(sm, peer->addr);
+ /* cache the peer address before do_teardown */
+ os_memcpy(addr, peer->addr, ETH_ALEN);
+ wpa_tdls_do_teardown(sm, peer,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+ wpa_tdls_start(sm, addr);
} else {
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
" - tear down", MAC2STR(peer->addr));
@@ -2385,7 +2392,7 @@
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
"TPK Handshake Message 3");
if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
- goto error;
+ goto error_no_msg;
if (!peer->tpk_success) {
/*
@@ -2406,6 +2413,7 @@
error:
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
status);
+error_no_msg:
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
@@ -2859,14 +2867,14 @@
}
-static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
+static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
{
/* bit 38 - TDLS Prohibited */
return !!(elems->ext_capab[2 + 4] & 0x40);
}
-static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
+static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
{
/* bit 39 - TDLS Channel Switch Prohibited */
return !!(elems->ext_capab[2 + 4] & 0x80);
@@ -2875,12 +2883,13 @@
void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
{
- struct wpa_eapol_ie_parse elems;
+ struct ieee802_11_elems elems;
sm->tdls_prohibited = 0;
sm->tdls_chan_switch_prohibited = 0;
- if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+ if (ies == NULL ||
+ ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
return;
@@ -2896,9 +2905,10 @@
void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
{
- struct wpa_eapol_ie_parse elems;
+ struct ieee802_11_elems elems;
- if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+ if (ies == NULL ||
+ ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
return;
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index faffe36..0b5fe51 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,7 @@
/*
* WPA Supplicant - WPA state machine and EAPOL-Key processing
* Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright(c) 2015 Intel Deutschland GmbH
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -23,6 +24,9 @@
#include "peerkey.h"
+static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -34,11 +38,13 @@
* @msg: EAPOL-Key message
* @msg_len: Length of message
* @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: >= 0 on success, < 0 on failure
*/
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
- int ver, const u8 *dest, u16 proto,
- u8 *msg, size_t msg_len, u8 *key_mic)
+int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+ int ver, const u8 *dest, u16 proto,
+ u8 *msg, size_t msg_len, u8 *key_mic)
{
+ int ret = -1;
size_t mic_len = wpa_mic_len(sm->key_mgmt);
if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
@@ -69,10 +75,11 @@
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
- wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+ ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
out:
os_free(msg);
+ return ret;
}
@@ -206,15 +213,21 @@
#endif /* CONFIG_IEEE80211R */
} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
int res, pmk_len;
- pmk_len = PMK_LEN;
- res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+
+ if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ pmk_len = PMK_LEN_SUITE_B_192;
+ else
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len);
if (res) {
- /*
- * EAP-LEAP is an exception from other EAP methods: it
- * uses only 16-byte PMK.
- */
- res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
- pmk_len = 16;
+ if (pmk_len == PMK_LEN) {
+ /*
+ * EAP-LEAP is an exception from other EAP
+ * methods: it uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+ pmk_len = 16;
+ }
} else {
#ifdef CONFIG_IEEE80211R
u8 buf[2 * PMK_LEN];
@@ -318,7 +331,7 @@
* @wpa_ie: WPA/RSN IE
* @wpa_ie_len: Length of the WPA/RSN IE
* @ptk: PTK to use for keyed hash and encryption
- * Returns: 0 on success, -1 on failure
+ * Returns: >= 0 on success, < 0 on failure
*/
int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
@@ -409,10 +422,8 @@
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
- wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
- rbuf, rlen, key_mic);
-
- return 0;
+ return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
}
@@ -500,6 +511,7 @@
os_memset(buf, 0, sizeof(buf));
}
sm->tptk_set = 1;
+ sm->tk_to_set = 1;
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
@@ -525,7 +537,7 @@
#endif /* CONFIG_P2P */
if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
- kde, kde_len, ptk))
+ kde, kde_len, ptk) < 0)
goto failed;
os_free(kde_buf);
@@ -603,7 +615,12 @@
int keylen, rsclen;
enum wpa_alg alg;
const u8 *key_rsc;
- u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ if (!sm->tk_to_set) {
+ wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+ "WPA: Do not re-install same PTK to the driver");
+ return 0;
+ }
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Installing PTK to the driver");
@@ -643,6 +660,7 @@
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+ sm->tk_to_set = 0;
if (sm->wpa_ptk_rekey) {
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -753,12 +771,43 @@
}
+static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm,
+ const u8 *rsc)
+{
+ int rsclen;
+
+ if (!sm->wpa_rsc_relaxation)
+ return 0;
+
+ rsclen = wpa_cipher_rsc_len(sm->group_cipher);
+
+ /*
+ * Try to detect RSC (endian) corruption issue where the AP sends
+ * the RSC bytes in EAPOL-Key message in the wrong order, both if
+ * it's actually a 6-byte field (as it should be) and if it treats
+ * it as an 8-byte field.
+ * An AP model known to have this bug is the Sapido RB-1632.
+ */
+ if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0",
+ rsc[0], rsc[1], rsc[2], rsc[3],
+ rsc[4], rsc[5], rsc[6], rsc[7]);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
const u8 *gtk, size_t gtk_len,
int key_info)
{
struct wpa_gtk_data gd;
+ const u8 *key_rsc;
/*
* IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
@@ -784,11 +833,15 @@
os_memcpy(gd.gtk, gtk, gtk_len);
gd.gtk_len = gtk_len;
+ key_rsc = key->key_rsc;
+ if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+ key_rsc = null_rsc;
+
if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
(wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, gtk_len,
&gd.key_rsc_len, &gd.alg) ||
- wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
+ wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Failed to install GTK");
os_memset(&gd, 0, sizeof(gd));
@@ -989,8 +1042,8 @@
if (sm->assoc_resp_ies) {
pos = sm->assoc_resp_ies;
end = pos + sm->assoc_resp_ies_len;
- while (pos + 2 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 2) {
+ if (2 + pos[1] > end - pos)
break;
switch (*pos) {
case WLAN_EID_MOBILITY_DOMAIN:
@@ -1086,7 +1139,7 @@
* @ver: Version bits from EAPOL-Key Key Info
* @key_info: Key Info
* @ptk: PTK to use for keyed hash and encryption
- * Returns: 0 on success, -1 on failure
+ * Returns: >= 0 on success, < 0 on failure
*/
int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
@@ -1126,10 +1179,8 @@
WPA_PUT_BE16(reply->key_data_length, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
- wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
- rbuf, rlen, key_mic);
-
- return 0;
+ return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+ ETH_P_EAPOL, rbuf, rlen, key_mic);
}
@@ -1202,7 +1253,7 @@
#endif /* CONFIG_P2P */
if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
- &sm->ptk)) {
+ &sm->ptk) < 0) {
goto failed;
}
@@ -1292,8 +1343,8 @@
&gd->key_rsc_len, &gd->alg))
return -1;
- wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
- ie.gtk, ie.gtk_len);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+ ie.gtk, ie.gtk_len);
gd->keyidx = ie.gtk[0] & 0x3;
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(ie.gtk[0] & BIT(2)));
@@ -1344,6 +1395,11 @@
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 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
+ return -1;
+#else /* CONFIG_NO_RC4 */
u8 ek[32];
if (key_data_len > sizeof(gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1361,6 +1417,7 @@
return -1;
}
os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1431,10 +1488,8 @@
WPA_PUT_BE16(reply->key_data_length, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
- wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
- ETH_P_EAPOL, rbuf, rlen, key_mic);
-
- return 0;
+ return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
+ sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
}
@@ -1447,6 +1502,7 @@
u16 key_info;
int rekey, ret;
struct wpa_gtk_data gd;
+ const u8 *key_rsc;
if (!sm->msg_3_of_4_ok) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1477,8 +1533,12 @@
if (ret)
goto failed;
- if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
- wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+ key_rsc = key->key_rsc;
+ if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+ key_rsc = null_rsc;
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+ wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
goto failed;
os_memset(&gd, 0, sizeof(gd));
@@ -1575,6 +1635,11 @@
/* Decrypt key data here so that this operation does not need
* to be implemented separately for each message type. */
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: RC4 not supported in the build");
+ return -1;
+#else /* CONFIG_NO_RC4 */
u8 ek[32];
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
@@ -1585,6 +1650,7 @@
return -1;
}
os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
} 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 ||
@@ -2427,6 +2493,7 @@
sm->ssid_len = 0;
sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
sm->p2p = config->p2p;
+ sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
} else {
sm->network_ctx = NULL;
sm->peerkey_enabled = 0;
@@ -2437,6 +2504,7 @@
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
sm->p2p = 0;
+ sm->wpa_rsc_relaxation = 0;
}
}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index e163b70..1d27453 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -104,6 +104,7 @@
size_t ssid_len;
int wpa_ptk_rekey;
int p2p;
+ int wpa_rsc_relaxation;
};
#ifndef CONFIG_NO_WPA
@@ -180,7 +181,7 @@
}
static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
- size_t pmk_len)
+ size_t pmk_len, const u8 *bssid)
{
}
@@ -320,7 +321,8 @@
}
static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
- const u8 *ptk_kek)
+ size_t ptk_kck_len,
+ const u8 *ptk_kek, size_t ptk_kek_len)
{
}
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 06dea05..205793e 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -168,9 +168,7 @@
pos = (u8 *) (rsnie + 1);
/* Group Suite Selector */
- if (sm->group_cipher != WPA_CIPHER_CCMP &&
- sm->group_cipher != WPA_CIPHER_GCMP &&
- sm->group_cipher != WPA_CIPHER_TKIP) {
+ if (!wpa_cipher_valid_group(sm->group_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
sm->group_cipher);
os_free(buf);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 965a9c1..dba6b62 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -19,11 +19,12 @@
* struct wpa_sm - Internal WPA state machine data
*/
struct wpa_sm {
- u8 pmk[PMK_LEN];
+ u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
struct wpa_ptk ptk, tptk;
int ptk_set, tptk_set;
unsigned int msg_3_of_4_ok:1;
+ unsigned int tk_to_set:1;
u8 snonce[WPA_NONCE_LEN];
u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
int renew_snonce;
@@ -60,6 +61,7 @@
size_t ssid_len;
int wpa_ptk_rekey;
int p2p;
+ int wpa_rsc_relaxation;
u8 own_addr[ETH_ALEN];
const char *ifname;
@@ -342,16 +344,14 @@
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, size_t kck_len,
- int ver, const u8 *dest, u16 proto,
- u8 *msg, size_t msg_len, u8 *key_mic);
+int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+ int ver, const u8 *dest, u16 proto,
+ u8 *msg, size_t msg_len, u8 *key_mic);
int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
int ver, const u8 *nonce,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 0c37b35..c44844e 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -378,7 +378,7 @@
return 0;
}
- if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ if (1 + RSN_SELECTOR_LEN < end - pos &&
pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -491,13 +491,13 @@
int ret = 0;
os_memset(ie, 0, sizeof(*ie));
- for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
if (pos[0] == 0xdd &&
((pos == buf + len - 1) || pos[1] == 0)) {
/* Ignore padding */
break;
}
- if (pos + 2 + pos[1] > end) {
+ if (2 + pos[1] > end - pos) {
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
"underflow (ie=%d len=%d pos=%d)",
pos[0], pos[1], (int) (pos - buf));
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 533286c..846d293 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -691,18 +691,16 @@
if (data == NULL || data_len == 0)
return 0;
- pos = conn->client_hello_ext = os_malloc(6 + data_len);
+ pos = conn->client_hello_ext = os_malloc(4 + data_len);
if (pos == NULL)
return -1;
- WPA_PUT_BE16(pos, 4 + data_len);
- pos += 2;
WPA_PUT_BE16(pos, ext_type);
pos += 2;
WPA_PUT_BE16(pos, data_len);
pos += 2;
os_memcpy(pos, data, data_len);
- conn->client_hello_ext_len = 6 + data_len;
+ conn->client_hello_ext_len = 4 + data_len;
if (ext_type == TLS_EXT_PAC_OPAQUE) {
conn->session_ticket_included = 1;
@@ -714,12 +712,12 @@
/**
- * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * tlsv1_client_get_random - Get random data from TLS connection
* @conn: TLSv1 client connection data from tlsv1_client_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
* Returns: 0 on success, -1 on failure
*/
-int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
{
os_memset(keys, 0, sizeof(*keys));
if (conn->state == CLIENT_HELLO)
@@ -813,9 +811,14 @@
}
-void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+/**
+ * tlsv1_client_set_flags - Set connection flags
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @flags: TLS_CONN_* bitfield
+ */
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags)
{
- conn->disable_time_checks = !enabled;
+ conn->flags = flags;
}
@@ -828,3 +831,38 @@
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
}
+
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data),
+ void *cb_ctx,
+ int cert_in_cb)
+{
+ conn->event_cb = event_cb;
+ conn->cb_ctx = cb_ctx;
+ conn->cert_in_cb = !!cert_in_cb;
+}
+
+
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+ size_t buflen)
+{
+ if (!conn)
+ return -1;
+ switch (conn->rl.tls_version) {
+ case TLS_VERSION_1:
+ os_strlcpy(buf, "TLSv1", buflen);
+ break;
+ case TLS_VERSION_1_1:
+ os_strlcpy(buf, "TLSv1.1", buflen);
+ break;
+ case TLS_VERSION_1_2:
+ os_strlcpy(buf, "TLSv1.2", buflen);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index 8ec85f1..40fa6c7 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -36,12 +36,12 @@
int tlsv1_client_resumed(struct tlsv1_client *conn);
int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
const u8 *data, size_t data_len);
-int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys);
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
int tlsv1_client_set_cred(struct tlsv1_client *conn,
struct tlsv1_credentials *cred);
-void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags);
typedef int (*tlsv1_client_session_ticket_cb)
(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
@@ -51,4 +51,12 @@
tlsv1_client_session_ticket_cb cb,
void *ctx);
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data),
+ void *cb_ctx,
+ int cert_in_cb);
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+ size_t buflen);
+
#endif /* TLSV1_CLIENT_H */
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
index 55fdcf8..6c4dbc7 100644
--- a/src/tls/tlsv1_client_i.h
+++ b/src/tls/tlsv1_client_i.h
@@ -29,11 +29,13 @@
u8 alert_level;
u8 alert_description;
+ unsigned int flags; /* TLS_CONN_* bitfield */
+
unsigned int certificate_requested:1;
unsigned int session_resumed:1;
unsigned int session_ticket_included:1;
unsigned int use_session_ticket:1;
- unsigned int disable_time_checks:1;
+ unsigned int cert_in_cb:1;
struct crypto_public_key *server_rsa_key;
@@ -64,6 +66,10 @@
void *session_ticket_cb_ctx;
struct wpabuf *partial_input;
+
+ void (*event_cb)(void *ctx, enum tls_event ev,
+ union tls_event_data *data);
+ void *cb_ctx;
};
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 9ce9680..40c6a46 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -27,6 +27,17 @@
const u8 *in_data, size_t *in_len);
+static int tls_version_disabled(struct tlsv1_client *conn, u16 ver)
+{
+ return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+ ver == TLS_VERSION_1) ||
+ ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+ ver == TLS_VERSION_1_1) ||
+ ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+ ver == TLS_VERSION_1_2));
+}
+
+
static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
@@ -76,7 +87,8 @@
if (end - pos < 2)
goto decode_error;
tls_version = WPA_GET_BE16(pos);
- if (!tls_version_ok(tls_version)) {
+ if (!tls_version_ok(tls_version) ||
+ tls_version_disabled(conn, tls_version)) {
wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
"ServerHello %u.%u", pos[0], pos[1]);
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -211,6 +223,73 @@
}
+static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
+ struct x509_certificate *cert)
+{
+ union tls_event_data ev;
+ struct wpabuf *cert_buf = NULL;
+#ifdef CONFIG_SHA256
+ u8 hash[32];
+#endif /* CONFIG_SHA256 */
+ char subject[128];
+
+ if (!conn->event_cb)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ if (conn->cred->cert_probe || conn->cert_in_cb) {
+ cert_buf = wpabuf_alloc_copy(cert->cert_start,
+ cert->cert_len);
+ ev.peer_cert.cert = cert_buf;
+ }
+#ifdef CONFIG_SHA256
+ if (cert_buf) {
+ const u8 *addr[1];
+ size_t len[1];
+ addr[0] = wpabuf_head(cert_buf);
+ len[0] = wpabuf_len(cert_buf);
+ if (sha256_vector(1, addr, len, hash) == 0) {
+ ev.peer_cert.hash = hash;
+ ev.peer_cert.hash_len = sizeof(hash);
+ }
+ }
+#endif /* CONFIG_SHA256 */
+
+ ev.peer_cert.depth = depth;
+ x509_name_string(&cert->subject, subject, sizeof(subject));
+ ev.peer_cert.subject = subject;
+
+ conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+ wpabuf_free(cert_buf);
+}
+
+
+static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth,
+ struct x509_certificate *cert,
+ enum tls_fail_reason reason,
+ const char *reason_txt)
+{
+ struct wpabuf *cert_buf = NULL;
+ union tls_event_data ev;
+ char subject[128];
+
+ if (!conn->event_cb)
+ return;
+
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.depth = depth;
+ x509_name_string(&cert->subject, subject, sizeof(subject));
+ ev.peer_cert.subject = subject;
+ ev.cert_fail.reason = reason;
+ ev.cert_fail.reason_txt = reason_txt;
+ cert_buf = wpabuf_alloc_copy(cert->cert_start,
+ cert->cert_len);
+ ev.cert_fail.cert = cert_buf;
+ conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+ wpabuf_free(cert_buf);
+}
+
+
static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
const u8 *in_data, size_t *in_len)
{
@@ -354,6 +433,8 @@
return -1;
}
+ tls_peer_cert_event(conn, idx, cert);
+
if (last == NULL)
chain = cert;
else
@@ -364,31 +445,99 @@
pos += cert_len;
}
- if (conn->cred &&
- x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
- &reason, conn->disable_time_checks)
- < 0) {
+ if (conn->cred && conn->cred->server_cert_only && chain) {
+ u8 hash[SHA256_MAC_LEN];
+ char buf[128];
+
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Validate server certificate hash");
+ x509_name_string(&chain->subject, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf);
+ if (sha256_vector(1, &chain->cert_start, &chain->cert_len,
+ hash) < 0 ||
+ os_memcmp(conn->cred->srv_cert_hash, hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Server certificate hash mismatch");
+ wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash",
+ hash, SHA256_MAC_LEN);
+ if (conn->event_cb) {
+ union tls_event_data ev;
+
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED;
+ ev.cert_fail.reason_txt =
+ "Server certificate mismatch";
+ ev.cert_fail.subject = buf;
+ conn->event_cb(conn->cb_ctx,
+ TLS_CERT_CHAIN_FAILURE, &ev);
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+ } else if (conn->cred && conn->cred->cert_probe) {
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Reject server certificate on probe-only rune");
+ if (conn->event_cb) {
+ union tls_event_data ev;
+ char buf[128];
+
+ os_memset(&ev, 0, sizeof(ev));
+ ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE;
+ ev.cert_fail.reason_txt =
+ "Server certificate chain probe";
+ if (chain) {
+ x509_name_string(&chain->subject, buf,
+ sizeof(buf));
+ ev.cert_fail.subject = buf;
+ }
+ conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE,
+ &ev);
+ }
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ } else if (conn->cred && conn->cred->ca_cert_verify &&
+ x509_certificate_chain_validate(
+ conn->cred->trusted_certs, chain, &reason,
+ !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS))
+ < 0) {
int tls_reason;
wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
"validation failed (reason=%d)", reason);
switch (reason) {
case X509_VALIDATE_BAD_CERTIFICATE:
tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+ tls_cert_chain_failure_event(
+ conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+ "bad certificate");
break;
case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
break;
case X509_VALIDATE_CERTIFICATE_REVOKED:
tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+ tls_cert_chain_failure_event(
+ conn, 0, chain, TLS_FAIL_REVOKED,
+ "certificate revoked");
break;
case X509_VALIDATE_CERTIFICATE_EXPIRED:
tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+ tls_cert_chain_failure_event(
+ conn, 0, chain, TLS_FAIL_EXPIRED,
+ "certificate has expired or is not yet valid");
break;
case X509_VALIDATE_CERTIFICATE_UNKNOWN:
tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
break;
case X509_VALIDATE_UNKNOWN_CA:
tls_reason = TLS_ALERT_UNKNOWN_CA;
+ tls_cert_chain_failure_event(
+ conn, 0, chain, TLS_FAIL_UNTRUSTED,
+ "unknown CA");
break;
default:
tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -399,6 +548,19 @@
return -1;
}
+ if (conn->cred && !conn->cred->server_cert_only && chain &&
+ (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+ !(chain->ext_key_usage &
+ (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) {
+ tls_cert_chain_failure_event(
+ conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+ "certificate not allowed for server authentication");
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
x509_certificate_chain_free(chain);
*in_len = end - in_data;
@@ -507,7 +669,7 @@
server_params_end = pos;
if (key_exchange == TLS_KEY_X_DHE_RSA) {
- u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+ u8 hash[64];
int hlen;
if (conn->rl.tls_version == TLS_VERSION_1_2) {
@@ -524,18 +686,21 @@
*/
if (end - pos < 2)
goto fail;
- if (pos[0] != TLS_HASH_ALG_SHA256 ||
+ if ((pos[0] != TLS_HASH_ALG_SHA256 &&
+ pos[0] != TLS_HASH_ALG_SHA384 &&
+ pos[0] != TLS_HASH_ALG_SHA512) ||
pos[1] != TLS_SIGN_ALG_RSA) {
wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
pos[0], pos[1]);
goto fail;
}
- pos += 2;
hlen = tlsv12_key_x_server_params_hash(
- conn->rl.tls_version, conn->client_random,
+ conn->rl.tls_version, pos[0],
+ conn->client_random,
conn->server_random, server_params,
server_params_end - server_params, hash);
+ pos += 2;
#else /* CONFIG_TLSV12 */
goto fail;
#endif /* CONFIG_TLSV12 */
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index d192f44..b1906b2 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -47,8 +47,28 @@
u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
struct os_time now;
size_t len, i;
+ u8 *ext_start;
+ u16 tls_version = TLS_VERSION;
- wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+ /* Pick the highest locally enabled TLS version */
+#ifdef CONFIG_TLSV12
+ if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+ tls_version == TLS_VERSION_1_2)
+ tls_version = TLS_VERSION_1_1;
+#endif /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+ if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+ tls_version == TLS_VERSION_1_1)
+ tls_version = TLS_VERSION_1;
+#endif /* CONFIG_TLSV11 */
+ if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+ tls_version == TLS_VERSION_1) {
+ wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)",
+ tls_version_str(tls_version));
*out_len = 0;
os_get_time(&now);
@@ -61,7 +81,7 @@
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
conn->client_random, TLS_RANDOM_LEN);
- len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+ len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
hello = os_malloc(len);
if (hello == NULL)
return NULL;
@@ -81,7 +101,7 @@
pos += 3;
/* body - ClientHello */
/* ProtocolVersion client_version */
- WPA_PUT_BE16(pos, TLS_VERSION);
+ WPA_PUT_BE16(pos, tls_version);
pos += 2;
/* Random random: uint32 gmt_unix_time, opaque random_bytes */
os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
@@ -101,12 +121,46 @@
*pos++ = 1;
*pos++ = TLS_COMPRESSION_NULL;
+ /* Extension */
+ ext_start = pos;
+ pos += 2;
+
+#ifdef CONFIG_TLSV12
+ if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+ /*
+ * Add signature_algorithms extension since we support only
+ * SHA256 (and not the default SHA1) with TLSv1.2.
+ */
+ /* ExtensionsType extension_type = signature_algorithms(13) */
+ WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS);
+ pos += 2;
+ /* opaque extension_data<0..2^16-1> length */
+ WPA_PUT_BE16(pos, 8);
+ pos += 2;
+ /* supported_signature_algorithms<2..2^16-2> length */
+ WPA_PUT_BE16(pos, 6);
+ pos += 2;
+ /* supported_signature_algorithms */
+ *pos++ = TLS_HASH_ALG_SHA512;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ *pos++ = TLS_HASH_ALG_SHA384;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ *pos++ = TLS_HASH_ALG_SHA256;
+ *pos++ = TLS_SIGN_ALG_RSA;
+ }
+#endif /* CONFIG_TLSV12 */
+
if (conn->client_hello_ext) {
os_memcpy(pos, conn->client_hello_ext,
conn->client_hello_ext_len);
pos += conn->client_hello_ext_len;
}
+ if (pos == ext_start + 2)
+ pos -= 2; /* no extensions */
+ else
+ WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
WPA_PUT_BE24(hs_length, pos - hs_length - 3);
tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
@@ -134,6 +188,11 @@
struct x509_certificate *cert;
pos = *msgpos;
+ if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
rhdr = pos;
@@ -154,7 +213,7 @@
pos += 3;
cert = conn->cred ? conn->cred->cert : NULL;
while (cert) {
- if (pos + 3 + cert->cert_len > end) {
+ if (3 + cert->cert_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
"for Certificate (cert_len=%lu left=%lu)",
(unsigned long) cert->cert_len,
@@ -265,9 +324,16 @@
wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
dh_yc, dh_yc_len);
+ if (end - *pos < 2) {
+ tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ os_free(csecret);
+ os_free(dh_yc);
+ return -1;
+ }
WPA_PUT_BE16(*pos, dh_yc_len);
*pos += 2;
- if (*pos + dh_yc_len > end) {
+ if (dh_yc_len > (size_t) (end - *pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
"message buffer for Yc");
tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -789,6 +855,8 @@
wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
"successfully");
+ if (!conn->session_resumed && conn->use_session_ticket)
+ conn->session_resumed = 1;
conn->state = ESTABLISHED;
return msg;
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index dabc12a..6b28417 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -335,7 +335,7 @@
#ifdef CONFIG_TLSV12
-int tlsv12_key_x_server_params_hash(u16 tls_version,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg,
const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
@@ -343,14 +343,30 @@
{
size_t hlen;
struct crypto_hash *ctx;
+ enum crypto_hash_alg alg;
- ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+ switch (hash_alg) {
+ case TLS_HASH_ALG_SHA256:
+ alg = CRYPTO_HASH_ALG_SHA256;
+ hlen = SHA256_MAC_LEN;
+ break;
+ case TLS_HASH_ALG_SHA384:
+ alg = CRYPTO_HASH_ALG_SHA384;
+ hlen = 48;
+ break;
+ case TLS_HASH_ALG_SHA512:
+ alg = CRYPTO_HASH_ALG_SHA512;
+ hlen = 64;
+ break;
+ default:
+ return -1;
+ }
+ ctx = crypto_hash_init(alg, NULL, 0);
if (ctx == NULL)
return -1;
crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
crypto_hash_update(ctx, server_params, server_params_len);
- hlen = SHA256_MAC_LEN;
if (crypto_hash_finish(ctx, hash, &hlen) < 0)
return -1;
@@ -469,6 +485,21 @@
wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
decrypted = buf + 19;
buflen -= 19;
+ } else if (buflen >= 19 + 48 &&
+ os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
+ "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+ decrypted = buf + 19;
+ buflen -= 19;
+ } else if (buflen >= 19 + 64 &&
+ os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
+ "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
+ {
+ wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+ decrypted = buf + 19;
+ buflen -= 19;
+
} else {
wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
os_free(buf);
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index 26e68af..7a252fe 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -169,6 +169,7 @@
#define TLS_EXT_TRUSTED_CA_KEYS 3 /* RFC 4366 */
#define TLS_EXT_TRUNCATED_HMAC 4 /* RFC 4366 */
#define TLS_EXT_STATUS_REQUEST 5 /* RFC 4366 */
+#define TLS_EXT_SIGNATURE_ALGORITHMS 13 /* RFC 5246 */
#define TLS_EXT_SESSION_TICKET 35 /* RFC 4507 */
#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
@@ -257,7 +258,8 @@
const char * tls_version_str(u16 ver);
int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
-int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg,
+ const u8 *client_random,
const u8 *server_random,
const u8 *server_params,
size_t server_params_len, u8 *hash);
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 1ea6827..067562b 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -190,6 +190,43 @@
const u8 *cert_blob, size_t cert_blob_len,
const char *path)
{
+ if (cert && os_strncmp(cert, "hash://", 7) == 0) {
+ const char *pos = cert + 7;
+ if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Unsupported ca_cert hash value '%s'",
+ cert);
+ return -1;
+ }
+ pos += 14;
+ if (os_strlen(pos) != 32 * 2) {
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'",
+ cert);
+ return -1;
+ }
+ if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Invalid SHA256 hash value in ca_cert '%s'",
+ cert);
+ return -1;
+ }
+ cred->server_cert_only = 1;
+ cred->ca_cert_verify = 0;
+ wpa_printf(MSG_DEBUG,
+ "TLSv1: Checking only server certificate match");
+ return 0;
+ }
+
+ if (cert && os_strncmp(cert, "probe://", 8) == 0) {
+ cred->cert_probe = 1;
+ cred->ca_cert_verify = 0;
+ wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
+ return 0;
+ }
+
+ cred->ca_cert_verify = cert || cert_blob || path;
+
if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
cert_blob, cert_blob_len) < 0)
return -1;
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
index 68fbdc9..b4bfe38 100644
--- a/src/tls/tlsv1_cred.h
+++ b/src/tls/tlsv1_cred.h
@@ -14,6 +14,11 @@
struct x509_certificate *cert;
struct crypto_private_key *key;
+ unsigned int cert_probe:1;
+ unsigned int ca_cert_verify:1;
+ unsigned int server_cert_only:1;
+ u8 srv_cert_hash[32];
+
/* Diffie-Hellman parameters */
u8 *dh_p; /* prime */
size_t dh_p_len;
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 4df756f..ba47337 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -610,12 +610,12 @@
/**
- * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * tlsv1_server_get_random - Get random data from TLS connection
* @conn: TLSv1 server connection data from tlsv1_server_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
* Returns: 0 on success, -1 on failure
*/
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
{
os_memset(keys, 0, sizeof(*keys));
if (conn->state == CLIENT_HELLO)
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index b2b28d1..10e7699 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -32,7 +32,7 @@
size_t buflen);
int tlsv1_server_shutdown(struct tlsv1_server *conn);
int tlsv1_server_resumed(struct tlsv1_server *conn);
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 0f237ba..8347d7a 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -471,6 +471,15 @@
return -1;
}
+ if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+ !(chain->ext_key_usage &
+ (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_BAD_CERTIFICATE);
+ x509_certificate_chain_free(chain);
+ return -1;
+ }
+
x509_certificate_chain_free(chain);
*in_len = end - in_data;
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 15e6692..e7c5e22 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -168,6 +168,11 @@
}
pos = *msgpos;
+ if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
+ return -1;
+ }
tlsv1_server_log(conn, "Send Certificate");
rhdr = pos;
@@ -188,7 +193,7 @@
pos += 3;
cert = conn->cred->cert;
while (cert) {
- if (pos + 3 + cert->cert_len > end) {
+ if (3 + cert->cert_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
"for Certificate (cert_len=%lu left=%lu)",
(unsigned long) cert->cert_len,
@@ -371,7 +376,7 @@
/* body - ServerDHParams */
server_params = pos;
/* dh_p */
- if (pos + 2 + dh_p_len > end) {
+ if (2 + dh_p_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_p");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -385,7 +390,7 @@
pos += dh_p_len;
/* dh_g */
- if (pos + 2 + conn->cred->dh_g_len > end) {
+ if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_g");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -399,7 +404,7 @@
pos += conn->cred->dh_g_len;
/* dh_Ys */
- if (pos + 2 + dh_ys_len > end) {
+ if (2 + dh_ys_len > (size_t) (end - pos)) {
wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
"dh_Ys");
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -443,7 +448,8 @@
if (conn->rl.tls_version >= TLS_VERSION_1_2) {
#ifdef CONFIG_TLSV12
hlen = tlsv12_key_x_server_params_hash(
- conn->rl.tls_version, conn->client_random,
+ conn->rl.tls_version, TLS_HASH_ALG_SHA256,
+ conn->client_random,
conn->server_random, server_params,
pos - server_params, hash + 19);
@@ -457,7 +463,7 @@
* SignatureAlgorithm signature;
* } SignatureAndHashAlgorithm;
*/
- if (hlen < 0 || pos + 2 > end) {
+ if (hlen < 0 || end - pos < 2) {
tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
TLS_ALERT_INTERNAL_ERROR);
return -1;
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index b51dfcd..75e3285 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -199,12 +199,11 @@
hdr.class, hdr.tag);
return -1;
}
+ if (hdr.length > buf + len - hdr.payload)
+ return -1;
pos = hdr.payload;
end = pos + hdr.length;
- if (end > buf + len)
- return -1;
-
*next = end;
if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
@@ -243,7 +242,7 @@
}
pos = hdr.payload;
- if (pos + hdr.length > end)
+ if (hdr.length > end - pos)
return -1;
end = pos + hdr.length;
*next = end;
@@ -319,7 +318,7 @@
}
pos = hdr.payload;
- if (pos + hdr.length > buf + len)
+ if (hdr.length > buf + len - pos)
return -1;
end = *next = pos + hdr.length;
@@ -677,7 +676,7 @@
pos = hdr.payload;
plen = hdr.length;
- if (pos + plen > buf + len)
+ if (plen > (size_t) (buf + len - pos))
return -1;
*next = pos + plen;
@@ -721,6 +720,15 @@
}
+static int x509_any_ext_key_usage_oid(struct asn1_oid *oid)
+{
+ return oid->len == 6 &&
+ x509_id_ce_oid(oid) &&
+ oid->oid[3] == 37 /* extKeyUsage */ &&
+ oid->oid[4] == 0 /* anyExtendedKeyUsage */;
+}
+
+
static int x509_parse_ext_key_usage(struct x509_certificate *cert,
const u8 *pos, size_t len)
{
@@ -801,7 +809,7 @@
}
cert->ca = hdr.payload[0];
- if (hdr.payload + hdr.length == pos + len) {
+ if (hdr.length == pos + len - hdr.payload) {
wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
cert->ca);
return 0;
@@ -1074,6 +1082,100 @@
}
+static int x509_id_pkix_oid(struct asn1_oid *oid)
+{
+ return oid->len >= 7 &&
+ oid->oid[0] == 1 /* iso */ &&
+ oid->oid[1] == 3 /* identified-organization */ &&
+ oid->oid[2] == 6 /* dod */ &&
+ oid->oid[3] == 1 /* internet */ &&
+ oid->oid[4] == 5 /* security */ &&
+ oid->oid[5] == 5 /* mechanisms */ &&
+ oid->oid[6] == 7 /* id-pkix */;
+}
+
+
+static int x509_id_kp_oid(struct asn1_oid *oid)
+{
+ /* id-kp */
+ return oid->len >= 8 &&
+ x509_id_pkix_oid(oid) &&
+ oid->oid[7] == 3 /* id-kp */;
+}
+
+
+static int x509_id_kp_server_auth_oid(struct asn1_oid *oid)
+{
+ /* id-kp */
+ return oid->len == 9 &&
+ x509_id_kp_oid(oid) &&
+ oid->oid[8] == 1 /* id-kp-serverAuth */;
+}
+
+
+static int x509_id_kp_client_auth_oid(struct asn1_oid *oid)
+{
+ /* id-kp */
+ return oid->len == 9 &&
+ x509_id_kp_oid(oid) &&
+ oid->oid[8] == 2 /* id-kp-clientAuth */;
+}
+
+
+static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert,
+ const u8 *pos, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *end;
+ struct asn1_oid oid;
+
+ /*
+ * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ *
+ * KeyPurposeId ::= OBJECT IDENTIFIER
+ */
+
+ if (asn1_get_next(pos, len, &hdr) < 0 ||
+ hdr.class != ASN1_CLASS_UNIVERSAL ||
+ hdr.tag != ASN1_TAG_SEQUENCE) {
+ wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+ "(ExtKeyUsageSyntax) - found class %d tag 0x%x",
+ hdr.class, hdr.tag);
+ return -1;
+ }
+ if (hdr.length > pos + len - hdr.payload)
+ return -1;
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos);
+
+ while (pos < end) {
+ char buf[80];
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos))
+ return -1;
+ if (x509_any_ext_key_usage_oid(&oid)) {
+ os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf));
+ cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY;
+ } else if (x509_id_kp_server_auth_oid(&oid)) {
+ os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf));
+ cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH;
+ } else if (x509_id_kp_client_auth_oid(&oid)) {
+ os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf));
+ cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH;
+ } else {
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ }
+ wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf);
+ }
+
+ cert->extensions_present |= X509_EXT_EXT_KEY_USAGE;
+
+ return 0;
+}
+
+
static int x509_parse_extension_data(struct x509_certificate *cert,
struct asn1_oid *oid,
const u8 *pos, size_t len)
@@ -1085,7 +1187,6 @@
* certificate policies (section 4.2.1.5)
* name constraints (section 4.2.1.11)
* policy constraints (section 4.2.1.12)
- * extended key usage (section 4.2.1.13)
* inhibit any-policy (section 4.2.1.15)
*/
switch (oid->oid[3]) {
@@ -1097,6 +1198,8 @@
return x509_parse_ext_issuer_alt_name(cert, pos, len);
case 19: /* id-ce-basicConstraints */
return x509_parse_ext_basic_constraints(cert, pos, len);
+ case 37: /* id-ce-extKeyUsage */
+ return x509_parse_ext_ext_key_usage(cert, pos, len);
default:
return 1;
}
@@ -1449,7 +1552,7 @@
}
-static int x509_sha256_oid(struct asn1_oid *oid)
+static int x509_sha2_oid(struct asn1_oid *oid)
{
return oid->len == 9 &&
oid->oid[0] == 2 /* joint-iso-itu-t */ &&
@@ -1459,11 +1562,31 @@
oid->oid[4] == 101 /* gov */ &&
oid->oid[5] == 3 /* csor */ &&
oid->oid[6] == 4 /* nistAlgorithm */ &&
- oid->oid[7] == 2 /* hashAlgs */ &&
+ oid->oid[7] == 2 /* hashAlgs */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+ return x509_sha2_oid(oid) &&
oid->oid[8] == 1 /* sha256 */;
}
+static int x509_sha384_oid(struct asn1_oid *oid)
+{
+ return x509_sha2_oid(oid) &&
+ oid->oid[8] == 2 /* sha384 */;
+}
+
+
+static int x509_sha512_oid(struct asn1_oid *oid)
+{
+ return x509_sha2_oid(oid) &&
+ oid->oid[8] == 3 /* sha512 */;
+}
+
+
/**
* x509_certificate_parse - Parse a X.509 certificate in DER format
* @buf: Pointer to the X.509 certificate in DER format
@@ -1503,12 +1626,12 @@
}
pos = hdr.payload;
- if (pos + hdr.length > end) {
+ if (hdr.length > end - pos) {
x509_certificate_free(cert);
return NULL;
}
- if (pos + hdr.length < end) {
+ if (hdr.length < end - pos) {
wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
"encoded certificate",
pos + hdr.length, end - (pos + hdr.length));
@@ -1588,7 +1711,7 @@
size_t data_len;
struct asn1_hdr hdr;
struct asn1_oid oid;
- u8 hash[32];
+ u8 hash[64];
size_t hash_len;
if (!x509_pkcs_oid(&cert->signature.oid) ||
@@ -1700,6 +1823,32 @@
goto skip_digest_oid;
}
+ if (x509_sha384_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 12 /* sha384WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
+ if (x509_sha512_oid(&oid)) {
+ if (cert->signature.oid.oid[6] !=
+ 13 /* sha512WithRSAEncryption */) {
+ wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 "
+ "does not match with certificate "
+ "signatureAlgorithm (%lu)",
+ cert->signature.oid.oid[6]);
+ os_free(data);
+ return -1;
+ }
+ goto skip_digest_oid;
+ }
+
if (!x509_digest_oid(&oid)) {
wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
os_free(data);
@@ -1765,9 +1914,21 @@
wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
hash, hash_len);
break;
- case 2: /* md2WithRSAEncryption */
case 12: /* sha384WithRSAEncryption */
+ sha384_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 48;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)",
+ hash, hash_len);
+ break;
case 13: /* sha512WithRSAEncryption */
+ sha512_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+ hash);
+ hash_len = 64;
+ wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)",
+ hash, hash_len);
+ break;
+ case 2: /* md2WithRSAEncryption */
default:
wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
"algorithm (%lu)", cert->signature.oid.oid[6]);
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
index 91a35ba..12ef86e 100644
--- a/src/tls/x509v3.h
+++ b/src/tls/x509v3.h
@@ -68,6 +68,7 @@
#define X509_EXT_KEY_USAGE (1 << 2)
#define X509_EXT_SUBJECT_ALT_NAME (1 << 3)
#define X509_EXT_ISSUER_ALT_NAME (1 << 4)
+#define X509_EXT_EXT_KEY_USAGE (1 << 5)
/* BasicConstraints */
int ca; /* cA */
@@ -85,6 +86,12 @@
#define X509_KEY_USAGE_ENCIPHER_ONLY (1 << 7)
#define X509_KEY_USAGE_DECIPHER_ONLY (1 << 8)
+ /* ExtKeyUsage */
+ unsigned long ext_key_usage;
+#define X509_EXT_KEY_USAGE_ANY (1 << 0)
+#define X509_EXT_KEY_USAGE_SERVER_AUTH (1 << 1)
+#define X509_EXT_KEY_USAGE_CLIENT_AUTH (1 << 2)
+
/*
* The DER format certificate follows struct x509_certificate. These
* pointers point to that buffer.
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
index 9ce1a5c..71a1652 100644
--- a/src/utils/browser-android.c
+++ b/src/utils/browser-android.c
@@ -95,7 +95,7 @@
if (pid == 0) {
/* run the external command in the child process */
- char *argv[9];
+ char *argv[7];
argv[0] = "browser-android";
argv[1] = "start";
@@ -103,9 +103,7 @@
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;
+ argv[6] = NULL;
execv("/system/bin/am", argv);
wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 5fc40fa..59ba4d1 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -96,7 +96,7 @@
if (pid == 0) {
/* run the external command in the child process */
- char *argv[12];
+ char *argv[14];
argv[0] = "browser-wpadebug";
argv[1] = "start";
@@ -109,7 +109,9 @@
argv[8] = "-e";
argv[9] = "w1.fi.wpadebug.URL";
argv[10] = (void *) url;
- argv[11] = NULL;
+ argv[11] = "--user";
+ argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
+ argv[13] = NULL;
execv("/system/bin/am", argv);
wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
diff --git a/src/utils/common.c b/src/utils/common.c
index 5cf0d57..e74a2ae 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -498,7 +498,7 @@
*txt++ = 't';
break;
default:
- if (data[i] >= 32 && data[i] <= 127) {
+ if (data[i] >= 32 && data[i] <= 126) {
*txt++ = data[i];
} else {
txt += os_snprintf(txt, end - txt, "\\x%02x",
@@ -973,6 +973,48 @@
/**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ * It has to be set to NULL for the first call and passed for any
+ * futher call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ * position in str buffer. If a return value is not NULL a size
+ * of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+ const char *end, *token = str;
+
+ if (!str || !delim || !last)
+ return NULL;
+
+ if (*last)
+ token = *last;
+
+ while (*token && os_strchr(delim, *token))
+ token++;
+
+ if (!*token)
+ return NULL;
+
+ end = token + 1;
+
+ while (*end && !os_strchr(delim, *end))
+ end++;
+
+ *last = end;
+ return token;
+}
+
+
+/**
* str_token - Get next token from a string
* @buf: String to tokenize. Note that the string might be modified.
* @delim: String of delimiters
@@ -982,25 +1024,12 @@
*/
char * str_token(char *str, const char *delim, char **context)
{
- char *end, *pos = str;
+ char *token = (char *) cstr_token(str, delim, (const char **) context);
- if (*context)
- pos = *context;
+ if (token && **context)
+ *(*context)++ = '\0';
- 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;
+ return token;
}
diff --git a/src/utils/common.h b/src/utils/common.h
index 88318f5..0b9cc3d 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,16 +53,6 @@
}
#endif /* __APPLE__ */
-#ifdef CONFIG_TI_COMPILER
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#ifdef __big_endian__
-#define __BYTE_ORDER __BIG_ENDIAN
-#else
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif
-#endif /* CONFIG_TI_COMPILER */
-
#ifdef CONFIG_NATIVE_WINDOWS
#include <winsock.h>
@@ -110,22 +100,6 @@
#define WPA_TYPES_DEFINED
#endif /* __vxworks */
-#ifdef CONFIG_TI_COMPILER
-#ifdef _LLONG_AVAILABLE
-typedef unsigned long long u64;
-#else
-/*
- * TODO: 64-bit variable not available. Using long as a workaround to test the
- * build, but this will likely not work for all operations.
- */
-typedef unsigned long u64;
-#endif
-typedef unsigned int u32;
-typedef unsigned short u16;
-typedef unsigned char u8;
-#define WPA_TYPES_DEFINED
-#endif /* CONFIG_TI_COMPILER */
-
#ifndef WPA_TYPES_DEFINED
#ifdef CONFIG_USE_INTTYPES_H
#include <inttypes.h>
@@ -262,7 +236,7 @@
static inline u32 WPA_GET_BE32(const u8 *a)
{
- return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+ return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
}
static inline void WPA_PUT_BE32(u8 *a, u32 val)
@@ -275,7 +249,7 @@
static inline u32 WPA_GET_LE32(const u8 *a)
{
- return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+ return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
}
static inline void WPA_PUT_LE32(u8 *a, u32 val)
@@ -433,7 +407,7 @@
#endif
#ifndef BIT
-#define BIT(x) (1 << (x))
+#define BIT(x) (1U << (x))
#endif
/*
@@ -518,6 +492,11 @@
return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
}
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+ return a[0] & 0x01;
+}
+
#define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
#include "wpa_debug.h"
@@ -549,6 +528,7 @@
int random_mac_addr(u8 *addr);
int random_mac_addr_keep_oui(u8 *addr);
+const char * cstr_token(const char *str, const char *delim, const char **last);
char * str_token(char *str, const char *delim, char **context);
size_t utf8_escape(const char *inp, size_t in_size,
char *outp, size_t out_size);
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 4a565eb..8647229 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -61,11 +61,8 @@
struct eloop_sock_table {
int count;
struct eloop_sock *table;
-#ifdef CONFIG_ELOOP_EPOLL
eloop_event_type type;
-#else /* CONFIG_ELOOP_EPOLL */
int changed;
-#endif /* CONFIG_ELOOP_EPOLL */
};
struct eloop_data {
@@ -256,9 +253,7 @@
table->table = tmp;
eloop.max_sock = new_max_sock;
eloop.count++;
-#ifndef CONFIG_ELOOP_EPOLL
table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
eloop_trace_sock_add_ref(table);
#ifdef CONFIG_ELOOP_EPOLL
@@ -314,9 +309,7 @@
}
table->count--;
eloop.count--;
-#ifndef CONFIG_ELOOP_EPOLL
table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
eloop_trace_sock_add_ref(table);
#ifdef CONFIG_ELOOP_EPOLL
if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
@@ -523,6 +516,10 @@
continue;
table->handler(table->sock, table->eloop_data,
table->user_data);
+ if (eloop.readers.changed ||
+ eloop.writers.changed ||
+ eloop.exceptions.changed)
+ break;
}
}
#endif /* CONFIG_ELOOP_EPOLL */
@@ -923,6 +920,20 @@
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
struct eloop_timeout *timeout;
+
+ if (eloop.pending_terminate) {
+ /*
+ * This may happen in some corner cases where a signal
+ * is received during a blocking operation. We need to
+ * process the pending signals and exit if requested to
+ * avoid hitting the SIGALRM limit if the blocking
+ * operation took more than two seconds.
+ */
+ eloop_process_pending_signals();
+ if (eloop.terminate)
+ break;
+ }
+
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
@@ -977,6 +988,11 @@
, strerror(errno));
goto out;
}
+
+ eloop.readers.changed = 0;
+ eloop.writers.changed = 0;
+ eloop.exceptions.changed = 0;
+
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
@@ -998,6 +1014,19 @@
if (res <= 0)
continue;
+ if (eloop.readers.changed ||
+ eloop.writers.changed ||
+ eloop.exceptions.changed) {
+ /*
+ * Sockets may have been closed and reopened with the
+ * same FD in the signal or timeout handlers, so we
+ * must skip the previous results and check again
+ * whether any of the currently registered sockets have
+ * events.
+ */
+ continue;
+ }
+
#ifdef CONFIG_ELOOP_POLL
eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
&eloop.exceptions, eloop.pollfds_map,
@@ -1073,7 +1102,7 @@
int eloop_terminated(void)
{
- return eloop.terminate;
+ return eloop.terminate || eloop.pending_terminate;
}
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 653eb54..9c49680 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -26,6 +26,9 @@
#include "common.h"
#include "xml-utils.h"
#include "http-utils.h"
+#ifdef EAP_TLS_OPENSSL
+#include "crypto/tls_openssl.h"
+#endif /* EAP_TLS_OPENSSL */
struct http_ctx {
@@ -421,6 +424,28 @@
IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+#ifdef OPENSSL_IS_BORINGSSL
+#define sk_LogotypeInfo_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st)))
+#define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i))
+#define sk_LogotypeImage_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st)))
+#define sk_LogotypeImage_value(st, i) (LogotypeImage *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i))
+#define sk_LogotypeAudio_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st)))
+#define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \
+sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i))
+#define sk_HashAlgAndValue_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st)))
+#define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i))
+#define sk_ASN1_IA5STRING_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st)))
+#define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i))
+#else /* OPENSSL_IS_BORINGSSL */
#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
@@ -431,6 +456,7 @@
#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+#endif /* OPENSSL_IS_BORINGSSL */
static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
@@ -981,6 +1007,26 @@
if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
return 0;
+#ifdef OPENSSL_IS_BORINGSSL
+ if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) {
+ enum ocsp_result res;
+
+ res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer,
+ ctx->peer_issuer_issuer);
+ if (res == OCSP_REVOKED) {
+ preverify_ok = 0;
+ wpa_printf(MSG_INFO, "OCSP: certificate revoked");
+ if (err == X509_V_OK)
+ X509_STORE_CTX_set_error(
+ x509_ctx, X509_V_ERR_CERT_REVOKED);
+ } else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) {
+ preverify_ok = 0;
+ wpa_printf(MSG_INFO,
+ "OCSP: bad certificate status response");
+ }
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+
if (!preverify_ok)
ctx->last_err = "TLS validation failed";
@@ -1273,6 +1319,16 @@
#ifdef EAP_TLS_OPENSSL
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#ifdef OPENSSL_IS_BORINGSSL
+ /* For now, using the CURLOPT_SSL_VERIFYSTATUS option only
+ * with BoringSSL since the OpenSSL specific callback hack to
+ * enable OCSP is not available with BoringSSL. The OCSP
+ * implementation within libcurl is not sufficient for the
+ * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL.
+ */
+ if (ctx->ocsp != NO_OCSP)
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
+#endif /* OPENSSL_IS_BORINGSSL */
#endif /* EAP_TLS_OPENSSL */
} else {
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
diff --git a/src/utils/includes.h b/src/utils/includes.h
index 6c6ec87..75513fc 100644
--- a/src/utils/includes.h
+++ b/src/utils/includes.h
@@ -17,26 +17,22 @@
#include "build_config.h"
#include <stdlib.h>
+#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef _WIN32_WCE
-#ifndef CONFIG_TI_COMPILER
#include <signal.h>
#include <sys/types.h>
-#endif /* CONFIG_TI_COMPILER */
#include <errno.h>
#endif /* _WIN32_WCE */
#include <ctype.h>
-#ifndef CONFIG_TI_COMPILER
#ifndef _MSC_VER
#include <unistd.h>
#endif /* _MSC_VER */
-#endif /* CONFIG_TI_COMPILER */
#ifndef CONFIG_NATIVE_WINDOWS
-#ifndef CONFIG_TI_COMPILER
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -44,7 +40,6 @@
#include <sys/uio.h>
#include <sys/time.h>
#endif /* __vxworks */
-#endif /* CONFIG_TI_COMPILER */
#endif /* CONFIG_NATIVE_WINDOWS */
#endif /* INCLUDES_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index 8913854..9e496fb 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -247,11 +247,11 @@
int os_file_exists(const char *fname);
/**
- * os_fsync - Sync a file's (for a given stream) state with storage device
+ * os_fdatasync - Sync a file's (for a given stream) state with storage device
* @stream: the stream to be flushed
* Returns: 0 if the operation succeeded or -1 on failure
*/
-int os_fsync(FILE *stream);
+int os_fdatasync(FILE *stream);
/**
* os_zalloc - Allocate and zero memory
@@ -653,4 +653,12 @@
#define strcpy OS_DO_NOT_USE_strcpy
#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+#else
+#define TEST_FAIL() 0
+#endif
+
#endif /* OS_H */
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index b8fb2db..ed6eb3c 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -243,7 +243,7 @@
}
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
{
return 0;
}
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 96d243d..0c3214d 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -102,7 +102,7 @@
}
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
{
return 0;
}
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index ac73f7a..8f8dc5b 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -17,6 +17,12 @@
#include <private/android_filesystem_config.h>
#endif /* ANDROID */
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
#include "os.h"
#include "common.h"
@@ -36,7 +42,7 @@
struct dl_list list;
size_t len;
WPA_TRACE_INFO
-};
+} __attribute__((aligned(16)));
#endif /* WPA_TRACE */
@@ -63,6 +69,7 @@
int os_get_reltime(struct os_reltime *t)
{
+#ifndef __MACH__
#if defined(CLOCK_BOOTTIME)
static clockid_t clock_id = CLOCK_BOOTTIME;
#elif defined(CLOCK_MONOTONIC)
@@ -95,6 +102,23 @@
return -1;
}
}
+#else /* __MACH__ */
+ uint64_t abstime, nano;
+ static mach_timebase_info_data_t info = { 0, 0 };
+
+ if (!info.denom) {
+ if (mach_timebase_info(&info) != KERN_SUCCESS)
+ return -1;
+ }
+
+ abstime = mach_absolute_time();
+ nano = (abstime * info.numer) / info.denom;
+
+ t->sec = nano / NSEC_PER_SEC;
+ t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+ return 0;
+#endif /* __MACH__ */
}
@@ -226,6 +250,9 @@
FILE *f;
size_t rc;
+ if (TEST_FAIL())
+ return -1;
+
f = fopen("/dev/urandom", "rb");
if (f == NULL) {
printf("Could not open /dev/urandom.\n");
@@ -415,10 +442,21 @@
}
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
{
- if (!fflush(stream))
+ if (!fflush(stream)) {
+#ifdef __linux__
+ return fdatasync(fileno(stream));
+#else /* !__linux__ */
+#ifdef F_FULLFSYNC
+ /* OS X does not implement fdatasync(). */
+ return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
return fsync(fileno(stream));
+#endif /* F_FULLFSYNC */
+#endif /* __linux__ */
+ }
+
return -1;
}
@@ -556,6 +594,78 @@
return 0;
}
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+ const char *func[WPA_TRACE_LEN];
+ size_t i, res, len;
+ char *pos, *next;
+ int match;
+
+ if (!wpa_trace_test_fail_after)
+ return 0;
+
+ res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+ i = 0;
+ if (i < res && os_strcmp(func[i], __func__) == 0)
+ i++;
+
+ pos = wpa_trace_test_fail_func;
+
+ match = 0;
+ while (i < res) {
+ int allow_skip = 1;
+ int maybe = 0;
+
+ if (*pos == '=') {
+ allow_skip = 0;
+ pos++;
+ } else if (*pos == '?') {
+ maybe = 1;
+ pos++;
+ }
+ next = os_strchr(pos, ';');
+ if (next)
+ len = next - pos;
+ else
+ len = os_strlen(pos);
+ if (os_memcmp(pos, func[i], len) != 0) {
+ if (maybe && next) {
+ pos = next + 1;
+ continue;
+ }
+ if (allow_skip) {
+ i++;
+ continue;
+ }
+ return 0;
+ }
+ if (!next) {
+ match = 1;
+ break;
+ }
+ pos = next + 1;
+ i++;
+ }
+ if (!match)
+ return 0;
+
+ wpa_trace_test_fail_after--;
+ if (wpa_trace_test_fail_after == 0) {
+ wpa_printf(MSG_INFO, "TESTING: fail at %s",
+ wpa_trace_test_fail_func);
+ for (i = 0; i < res; i++)
+ wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+ (int) i, func[i]);
+ return 1;
+ }
+
+ return 0;
+}
+
#else
static inline int testing_fail_alloc(void)
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 890abf4..dea27b9 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -216,18 +216,18 @@
}
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
{
- HANDLE hFile;
+ HANDLE h;
if (stream == NULL)
return -1;
- hFile = _get_osfhandle(_fileno(stream));
- if (hFile == INVALID_HANDLE_VALUE)
+ h = (HANDLE) _get_osfhandle(_fileno(stream));
+ if (h == INVALID_HANDLE_VALUE)
return -1;
- if (!FlushFileBuffers(hFile))
+ if (!FlushFileBuffers(h))
return -1;
return 0;
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 6f5ea93..2f1157b 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -275,7 +275,7 @@
pos++;
if (pos >= end)
return -1;
- if ((pos + pos[0]) < end)
+ if (pos[0] < end - pos)
end = pos + 1 + pos[0];
pos++;
wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
@@ -1385,7 +1385,7 @@
end = buf + len;
/* RES */
- if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+ if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) {
wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
return -1;
}
@@ -1395,7 +1395,7 @@
wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
/* CK */
- if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+ if (pos[0] != CK_LEN || CK_LEN > end - pos) {
wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
return -1;
}
@@ -1405,7 +1405,7 @@
wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
/* IK */
- if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+ if (pos[0] != IK_LEN || IK_LEN > end - pos) {
wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
return -1;
}
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index f8f815a..c9a5023 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -123,13 +123,13 @@
/* find payload start allowing for extended bitmap(s) */
- if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+ if (iterator->_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) {
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader + sizeof(uint32_t) >
(unsigned long)iterator->_max_length)
return -EINVAL;
while (get_unaligned_le32(iterator->_arg) &
- (1 << IEEE80211_RADIOTAP_EXT)) {
+ BIT(IEEE80211_RADIOTAP_EXT)) {
iterator->_arg += sizeof(uint32_t);
/*
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index b2c7e08..41511b9 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -14,6 +14,8 @@
#include "utils/ext_password.h"
#include "utils/trace.h"
#include "utils/base64.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
struct printf_test_data {
@@ -44,6 +46,7 @@
char buf[100];
u8 bin[100];
int errors = 0;
+ int array[10];
wpa_printf(MSG_INFO, "printf encode/decode tests");
@@ -92,9 +95,24 @@
if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
errors++;
+ if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q')
+ errors++;
+
if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
errors++;
+ array[0] = 10;
+ array[1] = 10;
+ array[2] = 5;
+ array[3] = 10;
+ array[4] = 5;
+ array[5] = 0;
+ if (int_array_len(array) != 5)
+ errors++;
+ int_array_sort_unique(array);
+ if (int_array_len(array) != 2)
+ errors++;
+
if (errors) {
wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
return -1;
@@ -336,7 +354,7 @@
static int common_tests(void)
{
- char buf[3];
+ char buf[3], longbuf[100];
u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
u8 bin[3];
int errors = 0;
@@ -409,6 +427,11 @@
errors++;
}
+ if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 ||
+ wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 ||
+ os_strcmp(longbuf, "01-0") != 0)
+ errors++;
+
if (errors) {
wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
return -1;
@@ -418,6 +441,403 @@
}
+static int os_tests(void)
+{
+ int errors = 0;
+ void *ptr;
+ os_time_t t;
+
+ wpa_printf(MSG_INFO, "os tests");
+
+ ptr = os_calloc((size_t) -1, (size_t) -1);
+ if (ptr) {
+ errors++;
+ os_free(ptr);
+ }
+ ptr = os_calloc((size_t) 2, (size_t) -1);
+ if (ptr) {
+ errors++;
+ os_free(ptr);
+ }
+ ptr = os_calloc((size_t) -1, (size_t) 2);
+ if (ptr) {
+ errors++;
+ os_free(ptr);
+ }
+
+ ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1);
+ if (ptr) {
+ errors++;
+ os_free(ptr);
+ }
+
+ os_sleep(1, 1);
+
+ if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 ||
+ os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 ||
+ os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 ||
+ os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 ||
+ os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 ||
+ os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 ||
+ os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0)
+ errors++;
+
+ if (os_setenv("hwsim_test_env", "test value", 0) != 0 ||
+ os_setenv("hwsim_test_env", "test value 2", 1) != 0 ||
+ os_unsetenv("hwsim_test_env") != 0)
+ errors++;
+
+ if (os_file_exists("/this-file-does-not-exists-hwsim") != 0)
+ errors++;
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d os test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpabuf_tests(void)
+{
+ int errors = 0;
+ void *ptr;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_INFO, "wpabuf tests");
+
+ ptr = os_malloc(100);
+ if (ptr) {
+ buf = wpabuf_alloc_ext_data(ptr, 100);
+ if (buf) {
+ if (wpabuf_resize(&buf, 100) < 0)
+ errors++;
+ else
+ wpabuf_put(buf, 100);
+ wpabuf_free(buf);
+ } else {
+ errors++;
+ os_free(ptr);
+ }
+ } else {
+ errors++;
+ }
+
+ buf = wpabuf_alloc(100);
+ if (buf) {
+ struct wpabuf *buf2;
+
+ wpabuf_put(buf, 100);
+ if (wpabuf_resize(&buf, 100) < 0)
+ errors++;
+ else
+ wpabuf_put(buf, 100);
+ buf2 = wpabuf_concat(buf, NULL);
+ if (buf2 != buf)
+ errors++;
+ wpabuf_free(buf2);
+ } else {
+ errors++;
+ }
+
+ buf = NULL;
+ buf = wpabuf_zeropad(buf, 10);
+ if (buf != NULL)
+ errors++;
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int ip_addr_tests(void)
+{
+ int errors = 0;
+ struct hostapd_ip_addr addr;
+ char buf[100];
+
+ wpa_printf(MSG_INFO, "ip_addr tests");
+
+ if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 ||
+ addr.af != AF_INET ||
+ hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL ||
+ hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+ hostapd_ip_txt(&addr, buf, 0) != NULL ||
+ hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+ errors++;
+
+ if (hostapd_parse_ip_addr("::", &addr) != 0 ||
+ addr.af != AF_INET6 ||
+ hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+ hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+ errors++;
+
+ if (errors) {
+ wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct test_eloop {
+ unsigned int magic;
+ int close_in_timeout;
+ int pipefd1[2];
+ int pipefd2[2];
+};
+
+
+static void eloop_tests_start(int close_in_timeout);
+
+
+static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct test_eloop *t = eloop_ctx;
+ ssize_t res;
+ char buf[10];
+
+ wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+ if (t->magic != 0x12345678) {
+ wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+ __func__, t->magic);
+ }
+
+ if (t->pipefd2[0] != sock) {
+ wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+ __func__, sock, t->pipefd2[0]);
+ }
+
+ res = read(sock, buf, sizeof(buf));
+ wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+ __func__, sock, (int) res);
+}
+
+
+static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct test_eloop *t = eloop_ctx;
+
+ wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+ if (t->magic != 0x12345678) {
+ wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+ __func__, t->magic);
+ }
+
+ if (t->pipefd2[0] != sock) {
+ wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+ __func__, sock, t->pipefd2[0]);
+ }
+
+ /*
+ * This is expected to block due to the original socket with data having
+ * been closed and no new data having been written to the new socket
+ * with the same fd. To avoid blocking the process during test, skip the
+ * read here.
+ */
+ wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function",
+ __func__);
+}
+
+
+static void reopen_pipefd2(struct test_eloop *t)
+{
+ if (t->pipefd2[0] < 0) {
+ wpa_printf(MSG_INFO, "pipefd2 had been closed");
+ } else {
+ int res;
+
+ wpa_printf(MSG_INFO, "close pipefd2");
+ eloop_unregister_read_sock(t->pipefd2[0]);
+ close(t->pipefd2[0]);
+ t->pipefd2[0] = -1;
+ close(t->pipefd2[1]);
+ t->pipefd2[1] = -1;
+
+ res = pipe(t->pipefd2);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+ t->pipefd2[0] = -1;
+ t->pipefd2[1] = -1;
+ return;
+ }
+
+ wpa_printf(MSG_INFO,
+ "re-register pipefd2 with new sockets %d,%d",
+ t->pipefd2[0], t->pipefd2[1]);
+ eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong,
+ t, NULL);
+ }
+}
+
+
+static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct test_eloop *t = eloop_ctx;
+ ssize_t res;
+ char buf[10];
+
+ wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+ if (t->magic != 0x12345678) {
+ wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+ __func__, t->magic);
+ }
+
+ if (t->pipefd1[0] != sock) {
+ wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+ __func__, sock, t->pipefd1[0]);
+ }
+
+ res = read(sock, buf, sizeof(buf));
+ wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+ __func__, sock, (int) res);
+
+ if (!t->close_in_timeout)
+ reopen_pipefd2(t);
+}
+
+
+static void eloop_test_cb(void *eloop_data, void *user_ctx)
+{
+ struct test_eloop *t = eloop_data;
+
+ wpa_printf(MSG_INFO, "%s", __func__);
+
+ if (t->magic != 0x12345678) {
+ wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+ __func__, t->magic);
+ }
+
+ if (t->close_in_timeout)
+ reopen_pipefd2(t);
+}
+
+
+static void eloop_test_timeout(void *eloop_data, void *user_ctx)
+{
+ struct test_eloop *t = eloop_data;
+ int next_run = 0;
+
+ wpa_printf(MSG_INFO, "%s", __func__);
+
+ if (t->magic != 0x12345678) {
+ wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+ __func__, t->magic);
+ }
+
+ if (t->pipefd1[0] >= 0) {
+ wpa_printf(MSG_INFO, "pipefd1 had not been closed");
+ eloop_unregister_read_sock(t->pipefd1[0]);
+ close(t->pipefd1[0]);
+ t->pipefd1[0] = -1;
+ close(t->pipefd1[1]);
+ t->pipefd1[1] = -1;
+ }
+
+ if (t->pipefd2[0] >= 0) {
+ wpa_printf(MSG_INFO, "pipefd2 had not been closed");
+ eloop_unregister_read_sock(t->pipefd2[0]);
+ close(t->pipefd2[0]);
+ t->pipefd2[0] = -1;
+ close(t->pipefd2[1]);
+ t->pipefd2[1] = -1;
+ }
+
+ next_run = t->close_in_timeout;
+ t->magic = 0;
+ wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t);
+ os_free(t);
+
+ if (next_run)
+ eloop_tests_start(0);
+}
+
+
+static void eloop_tests_start(int close_in_timeout)
+{
+ struct test_eloop *t;
+ int res;
+
+ t = os_zalloc(sizeof(*t));
+ if (!t)
+ return;
+ t->magic = 0x12345678;
+ t->close_in_timeout = close_in_timeout;
+
+ wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)",
+ t, close_in_timeout);
+
+ res = pipe(t->pipefd1);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+ os_free(t);
+ return;
+ }
+
+ res = pipe(t->pipefd2);
+ if (res < 0) {
+ wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+ close(t->pipefd1[0]);
+ close(t->pipefd1[1]);
+ os_free(t);
+ return;
+ }
+
+ wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d",
+ t->pipefd1[0], t->pipefd1[1],
+ t->pipefd2[0], t->pipefd2[1]);
+
+ eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL);
+ eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL);
+ eloop_register_timeout(0, 0, eloop_test_cb, t, NULL);
+ eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL);
+
+ if (write(t->pipefd1[1], "HELLO", 5) < 0)
+ wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+ if (write(t->pipefd2[1], "TEST", 4) < 0)
+ wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+ os_sleep(0, 50000);
+ wpa_printf(MSG_INFO, "waiting for eloop callbacks");
+}
+
+
+static void eloop_tests_run(void *eloop_data, void *user_ctx)
+{
+ eloop_tests_start(1);
+}
+
+
+static int eloop_tests(void)
+{
+ wpa_printf(MSG_INFO, "schedule eloop tests to be run");
+
+ /*
+ * Cannot return error from these without a significant design change,
+ * so for now, run the tests from a scheduled timeout and require
+ * separate verification of the results from the debug log.
+ */
+ eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL);
+
+ return 0;
+}
+
+
int utils_module_tests(void)
{
int ret = 0;
@@ -430,6 +850,10 @@
bitfield_tests() < 0 ||
base64_tests() < 0 ||
common_tests() < 0 ||
+ os_tests() < 0 ||
+ wpabuf_tests() < 0 ||
+ ip_addr_tests() < 0 ||
+ eloop_tests() < 0 ||
int_array_tests() < 0)
ret = -1;
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 3c26301..3275524 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -517,16 +517,18 @@
{
#ifdef CONFIG_DEBUG_FILE
int rv;
- if (last_path) {
- char *tmp = os_strdup(last_path);
- wpa_debug_close_file();
- rv = wpa_debug_open_file(tmp);
- os_free(tmp);
- } else {
- wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
- "re-open log file.");
- rv = -1;
- }
+ char *tmp;
+
+ if (!last_path)
+ return 0; /* logfile not used */
+
+ tmp = os_strdup(last_path);
+ if (!tmp)
+ return -1;
+
+ wpa_debug_close_file();
+ rv = wpa_debug_open_file(tmp);
+ os_free(tmp);
return rv;
#else /* CONFIG_DEBUG_FILE */
return 0;
@@ -819,3 +821,42 @@
bin_clear_free(buf, buflen);
}
#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+ switch (level) {
+ case MSG_EXCESSIVE:
+ return "EXCESSIVE";
+ case MSG_MSGDUMP:
+ return "MSGDUMP";
+ case MSG_DEBUG:
+ return "DEBUG";
+ case MSG_INFO:
+ return "INFO";
+ case MSG_WARNING:
+ return "WARNING";
+ case MSG_ERROR:
+ return "ERROR";
+ default:
+ return "?";
+ }
+}
+
+
+int str_to_debug_level(const char *s)
+{
+ if (os_strcasecmp(s, "EXCESSIVE") == 0)
+ return MSG_EXCESSIVE;
+ if (os_strcasecmp(s, "MSGDUMP") == 0)
+ return MSG_MSGDUMP;
+ if (os_strcasecmp(s, "DEBUG") == 0)
+ return MSG_DEBUG;
+ if (os_strcasecmp(s, "INFO") == 0)
+ return MSG_INFO;
+ if (os_strcasecmp(s, "WARNING") == 0)
+ return MSG_WARNING;
+ if (os_strcasecmp(s, "ERROR") == 0)
+ return MSG_ERROR;
+ return -1;
+}
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 87bd7fa..17d8f96 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -364,4 +364,7 @@
#define WPA_ASSERT(a) do { } while (0)
#endif
+const char * debug_level_str(int level);
+int str_to_debug_level(const char *s);
+
#endif /* WPA_DEBUG_H */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 7aafa0a..11e7323 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -17,7 +17,7 @@
struct wpabuf_trace {
unsigned int magic;
-};
+} __attribute__((aligned(8)));
static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
{
diff --git a/src/wps/http_client.c b/src/wps/http_client.c
index 0290013..cdf3a51 100644
--- a/src/wps/http_client.c
+++ b/src/wps/http_client.c
@@ -85,15 +85,16 @@
{
struct http_client *c = eloop_ctx;
int res;
+ size_t send_len;
+ send_len = wpabuf_len(c->req) - c->req_pos;
wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
"bytes remaining)",
inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
(unsigned long) wpabuf_len(c->req),
- (unsigned long) wpabuf_len(c->req) - c->req_pos);
+ (unsigned long) send_len);
- res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
- wpabuf_len(c->req) - c->req_pos, 0);
+ res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
strerror(errno));
@@ -102,12 +103,11 @@
return;
}
- if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
+ if ((size_t) res < send_len) {
wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
"remaining",
res, (unsigned long) wpabuf_len(c->req),
- (unsigned long) wpabuf_len(c->req) - c->req_pos -
- res);
+ (unsigned long) send_len - res);
c->req_pos += res;
return;
}
@@ -146,24 +146,20 @@
c->cb_ctx = cb_ctx;
c->sd = socket(AF_INET, SOCK_STREAM, 0);
- if (c->sd < 0) {
- http_client_free(c);
- return NULL;
- }
+ if (c->sd < 0)
+ goto fail;
if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
- http_client_free(c);
- return NULL;
+ goto fail;
}
if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
if (errno != EINPROGRESS) {
wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
strerror(errno));
- http_client_free(c);
- return NULL;
+ goto fail;
}
/*
@@ -173,20 +169,18 @@
}
if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
- c, NULL)) {
- http_client_free(c);
- return NULL;
- }
-
- if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
- http_client_timeout, c, NULL)) {
- http_client_free(c);
- return NULL;
- }
+ c, NULL) ||
+ eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+ http_client_timeout, c, NULL))
+ goto fail;
c->req = req;
return c;
+
+fail:
+ http_client_free(c);
+ return NULL;
}
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index ac088c4..507abe8 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -277,11 +277,9 @@
"%s", srv->port, strerror(errno));
goto fail;
}
- if (listen(srv->fd, 10 /* max backlog */) < 0)
- goto fail;
- if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
- goto fail;
- if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+ if (listen(srv->fd, 10 /* max backlog */) < 0 ||
+ fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
srv, NULL))
goto fail;
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index 180b572..7a2ba50 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -278,8 +278,6 @@
}
}
*uri = 0; /* null terminate */
- while (isgraph(*hbp))
- hbp++;
while (*hbp == ' ' || *hbp == '\t')
hbp++;
/* get version */
@@ -506,10 +504,13 @@
new_alloc_nbytes < (h->content_length + 1))
new_alloc_nbytes = h->content_length + 1;
if (new_alloc_nbytes < h->body_alloc_nbytes ||
- new_alloc_nbytes > h->max_bytes) {
+ new_alloc_nbytes > h->max_bytes +
+ HTTPREAD_BODYBUF_DELTA) {
wpa_printf(MSG_DEBUG,
- "httpread: Unacceptable body length %d",
- new_alloc_nbytes);
+ "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
+ new_alloc_nbytes,
+ h->body_alloc_nbytes,
+ h->max_bytes);
goto bad;
}
if ((new_body = os_realloc(h->body, new_alloc_nbytes))
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 8d1ce1e..bb3c055 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -45,9 +45,14 @@
return -1;
record->payload_length = *pos++;
} else {
+ u32 len;
+
if (size < 6)
return -1;
- record->payload_length = ntohl(*(u32 *)pos);
+ len = WPA_GET_BE32(pos);
+ if (len > size - 6 || len > 20000)
+ return -1;
+ record->payload_length = len;
pos += sizeof(u32);
}
@@ -68,7 +73,8 @@
pos += record->payload_length;
record->total_length = pos - data;
- if (record->total_length > size)
+ if (record->total_length > size ||
+ record->total_length < record->payload_length)
return -1;
return 0;
}
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 498f11f..fbaf85a 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -355,16 +355,16 @@
int wps_ap_priority_compar(const struct wpabuf *wps_a,
const struct wpabuf *wps_b)
{
- struct wps_parse_attr attr_a, attr_b;
+ struct wps_parse_attr attr;
int sel_a, sel_b;
- if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+ if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
return 1;
- if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
- return -1;
+ sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
- sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
- sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
+ if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
+ return -1;
+ sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
if (sel_a && !sel_b)
return -1;
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 11a967b..756d57e 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -83,10 +83,10 @@
const u8 *end = pos + len;
u8 id, elen;
- while (pos + 2 <= end) {
+ while (end - pos >= 2) {
id = *pos++;
elen = *pos++;
- if (pos + elen > end)
+ if (elen > end - pos)
break;
if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
return -1;
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 82c4739..8188fe9 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -59,43 +59,44 @@
/* variable length fields */
const u8 *manufacturer;
- size_t manufacturer_len;
const u8 *model_name;
- size_t model_name_len;
const u8 *model_number;
- size_t model_number_len;
const u8 *serial_number;
- size_t serial_number_len;
const u8 *dev_name;
- size_t dev_name_len;
const u8 *public_key;
- size_t public_key_len;
const u8 *encr_settings;
- size_t encr_settings_len;
const u8 *ssid; /* <= 32 octets */
- size_t ssid_len;
const u8 *network_key; /* <= 64 octets */
- size_t network_key_len;
const u8 *authorized_macs; /* <= 30 octets */
- size_t authorized_macs_len;
const u8 *sec_dev_type_list; /* <= 128 octets */
- size_t sec_dev_type_list_len;
const u8 *oob_dev_password; /* 38..54 octets */
- size_t oob_dev_password_len;
+ u16 manufacturer_len;
+ u16 model_name_len;
+ u16 model_number_len;
+ u16 serial_number_len;
+ u16 dev_name_len;
+ u16 public_key_len;
+ u16 encr_settings_len;
+ u16 ssid_len;
+ u16 network_key_len;
+ u16 authorized_macs_len;
+ u16 sec_dev_type_list_len;
+ u16 oob_dev_password_len;
/* attributes that can occur multiple times */
#define MAX_CRED_COUNT 10
- const u8 *cred[MAX_CRED_COUNT];
- size_t cred_len[MAX_CRED_COUNT];
- size_t num_cred;
-
#define MAX_REQ_DEV_TYPE_COUNT 10
- const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
- size_t num_req_dev_type;
+ unsigned int num_cred;
+ unsigned int num_req_dev_type;
+ unsigned int num_vendor_ext;
+
+ u16 cred_len[MAX_CRED_COUNT];
+ u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+
+ const u8 *cred[MAX_CRED_COUNT];
+ const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
- size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
- size_t num_vendor_ext;
};
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 16d466e..88f85fe 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -528,7 +528,7 @@
{
u16 methods = 0;
- if (str == NULL) {
+ if (str == NULL || str[0] == '\0') {
/* Default to enabling methods based on build configuration */
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
methods |= WPS_CONFIG_VIRT_DISPLAY;
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 89957b1..9321b72 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -759,7 +759,7 @@
static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
- size_t cred_len[], size_t num_cred, int wps2)
+ u16 cred_len[], unsigned int num_cred, int wps2)
{
size_t i;
int ok = 0;
@@ -799,6 +799,7 @@
struct wpabuf *attrs, int wps2)
{
struct wps_credential cred;
+ int ret = 0;
if (!wps->wps->ap)
return 0;
@@ -877,10 +878,10 @@
if (wps->wps->cred_cb) {
cred.cred_attr = wpabuf_head(attrs);
cred.cred_attr_len = wpabuf_len(attrs);
- wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+ ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
}
- return 0;
+ return ret;
}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 078ff72..b840acd 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1649,11 +1649,15 @@
case HTTP_CLIENT_OK:
wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
reply = http_client_get_body(c);
- if (reply == NULL)
+ if (reply)
+ msg = os_zalloc(wpabuf_len(reply) + 1);
+ if (msg == NULL) {
+ if (ap->wps) {
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
break;
- msg = os_zalloc(wpabuf_len(reply) + 1);
- if (msg == NULL)
- break;
+ }
os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
break;
case HTTP_CLIENT_FAILED:
@@ -1709,21 +1713,30 @@
url = http_client_url_parse(ap->control_url, &dst, &path);
if (url == NULL) {
wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
- return;
+ goto fail;
}
buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
&len_ptr, &body_ptr);
os_free(url);
if (buf == NULL)
- return;
+ goto fail;
wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
ap->http = http_client_addr(&dst, buf, 10000,
wps_er_http_put_message_cb, ap);
- if (ap->http == NULL)
+ if (ap->http == NULL) {
wpabuf_free(buf);
+ goto fail;
+ }
+ return;
+
+fail:
+ if (ap->wps) {
+ wps_deinit(ap->wps);
+ ap->wps = NULL;
+ }
}
diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c
index e381fec..280b2b3 100644
--- a/src/wps/wps_er_ssdp.c
+++ b/src/wps/wps_er_ssdp.c
@@ -78,9 +78,7 @@
if (os_strstr(start, "ssdp:byebye"))
byebye = 1;
} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
- start += 9;
- while (*start == ' ')
- start++;
+ start += 14;
pos2 = os_strstr(start, "max-age=");
if (pos2 == NULL)
continue;
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 8bcf2b3..4ca3a42 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -2605,13 +2605,16 @@
token = wps_get_nfc_pw_token(
&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
if (token && token->peer_pk_hash_known) {
+ size_t len;
+
wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
"Password Token");
dl_list_del(&token->list);
wps->nfc_pw_token = token;
addr[0] = attr->public_key;
- sha256_vector(1, addr, &attr->public_key_len, hash);
+ len = attr->public_key_len;
+ sha256_vector(1, addr, &len, hash);
if (os_memcmp_const(hash,
wps->nfc_pw_token->pubkey_hash,
WPS_OOB_PUBKEY_HASH_LEN) != 0) {
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 933d734..0c458c6 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -695,6 +695,7 @@
struct subscription *s;
time_t now = time(NULL);
time_t expire = now + UPNP_SUBSCRIBE_SEC;
+ char str[80];
/* Get rid of expired subscriptions so we have room */
subscription_list_age(sm, now);
@@ -743,8 +744,10 @@
subscription_destroy(s);
return NULL;
}
- wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
- s, callback_urls);
+ uuid_bin2str(s->uuid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Subscription %p (SID %s) started with %s",
+ s, str, callback_urls);
/* Schedule sending this */
event_send_all_later(sm);
return s;
@@ -1079,6 +1082,7 @@
void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
{
struct upnp_wps_device_interface *iface;
+ struct upnp_wps_peer *peer;
if (!sm)
return;
@@ -1099,8 +1103,13 @@
iface->wps->registrar);
dl_list_del(&iface->list);
- if (iface->peer.wps)
- wps_deinit(iface->peer.wps);
+ while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer,
+ list))) {
+ if (peer->wps)
+ wps_deinit(peer->wps);
+ dl_list_del(&peer->list);
+ os_free(peer);
+ }
os_free(iface->ctx->ap_pin);
os_free(iface->ctx);
os_free(iface);
@@ -1138,6 +1147,7 @@
}
wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
+ dl_list_init(&iface->peers);
iface->ctx = ctx;
iface->wps = wps;
iface->priv = priv;
diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h
index 87b7ab1..b6f6df5 100644
--- a/src/wps/wps_upnp.h
+++ b/src/wps/wps_upnp.h
@@ -11,11 +11,14 @@
#ifndef WPS_UPNP_H
#define WPS_UPNP_H
+#include "utils/list.h"
+
struct upnp_wps_device_sm;
struct wps_context;
struct wps_data;
struct upnp_wps_peer {
+ struct dl_list list;
struct wps_data *wps;
};
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index 2949f14..cca3905 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -34,10 +34,8 @@
wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
msg);
- if (wps_validate_upnp_set_selected_registrar(msg) < 0)
- return -1;
-
- if (wps_parse_msg(msg, &attr) < 0)
+ if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
+ wps_parse_msg(msg, &attr) < 0)
return -1;
s->reg = reg;
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index 2c8ed4f..94aae75 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -276,11 +276,9 @@
* Assume we are called ONLY with no current event and ONLY with
* nonempty event queue and ONLY with at least one address to send to.
*/
- if (dl_list_empty(&s->addr_list))
- return -1;
- if (s->current_event)
- return -1;
- if (dl_list_empty(&s->event_queue))
+ if (dl_list_empty(&s->addr_list) ||
+ s->current_event ||
+ dl_list_empty(&s->event_queue))
return -1;
s->current_event = e = event_dequeue(s);
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index f289fe6..6a7c627 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -109,8 +109,7 @@
struct wps_context *wps;
void *priv;
- /* FIX: maintain separate structures for each UPnP peer */
- struct upnp_wps_peer peer;
+ struct dl_list peers; /* active UPnP peer sessions */
};
/*
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 26a740d..968fc03 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -139,7 +139,7 @@
uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
msg = wpabuf_alloc(800); /* more than big enough */
if (msg == NULL)
- goto fail;
+ return NULL;
switch (a->type) {
case ADVERTISE_UP:
case ADVERTISE_DOWN:
@@ -213,10 +213,6 @@
*islast = 1;
return msg;
-
-fail:
- wpabuf_free(msg);
- return NULL;
}
@@ -744,11 +740,9 @@
int sd;
sd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sd < 0)
- goto fail;
- if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
- goto fail;
- if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+ if (sd < 0 ||
+ fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
+ setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
goto fail;
os_memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -760,9 +754,8 @@
mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
- (char *) &mcast_addr, sizeof(mcast_addr)))
- goto fail;
- if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+ (char *) &mcast_addr, sizeof(mcast_addr)) ||
+ setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof(ttl)))
goto fail;
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index b1cf571..7548e84 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -300,7 +300,8 @@
* would appear to be required (given that we will be closing it!).
*/
static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
- struct http_request *hreq, char *filename)
+ struct http_request *hreq,
+ const char *filename)
{
struct wpabuf *buf; /* output buffer, allocated */
char *put_length_here;
@@ -409,6 +410,15 @@
}
+static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
+{
+ dl_list_del(&peer->list);
+ if (peer->wps)
+ wps_deinit(peer->wps);
+ os_free(peer);
+}
+
+
static enum http_reply_code
web_process_get_device_info(struct upnp_wps_device_sm *sm,
struct wpabuf **reply, const char **replyname)
@@ -426,7 +436,9 @@
if (!iface || iface->ctx->ap_pin == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
- peer = &iface->peer;
+ peer = os_zalloc(sizeof(*peer));
+ if (!peer)
+ return HTTP_INTERNAL_SERVER_ERROR;
/*
* Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
@@ -436,9 +448,6 @@
* registration.
*/
- if (peer->wps)
- wps_deinit(peer->wps);
-
os_memset(&cfg, 0, sizeof(cfg));
cfg.wps = iface->wps;
cfg.pin = (u8 *) iface->ctx->ap_pin;
@@ -455,8 +464,22 @@
*reply = NULL;
if (*reply == NULL) {
wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+ os_free(peer);
return HTTP_INTERNAL_SERVER_ERROR;
}
+
+ if (dl_list_len(&iface->peers) > 3) {
+ struct upnp_wps_peer *old;
+
+ old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+ if (old) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
+ wps_upnp_peer_del(old);
+ }
+ }
+ dl_list_add_tail(&iface->peers, &peer->list);
+ /* TODO: Could schedule a timeout to free the entry */
+
*replyname = name;
return HTTP_OK;
}
@@ -472,6 +495,8 @@
enum wps_process_res res;
enum wsc_op_code op_code;
struct upnp_wps_device_interface *iface;
+ struct wps_parse_attr attr;
+ struct upnp_wps_peer *tmp, *peer;
iface = dl_list_first(&sm->interfaces,
struct upnp_wps_device_interface, list);
@@ -487,11 +512,56 @@
msg = xml_get_base64_item(data, "NewInMessage", &ret);
if (msg == NULL)
return ret;
- res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
- if (res == WPS_FAILURE)
+
+ if (wps_parse_msg(msg, &attr)) {
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Could not parse PutMessage - NewInMessage");
+ wpabuf_free(msg);
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* Find a matching active peer session */
+ peer = NULL;
+ dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
+ if (!tmp->wps)
+ continue;
+ if (attr.enrollee_nonce &&
+ os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
+ WPS_NONCE_LEN) != 0)
+ continue; /* Enrollee nonce mismatch */
+ if (attr.msg_type &&
+ *attr.msg_type != WPS_M2 &&
+ *attr.msg_type != WPS_M2D &&
+ attr.registrar_nonce &&
+ os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
+ WPS_NONCE_LEN) != 0)
+ continue; /* Registrar nonce mismatch */
+ peer = tmp;
+ break;
+ }
+ if (!peer) {
+ /*
+ Try to use the first entry in case message could work with
+ * it. The actual handler function will reject this, if needed.
+ * This maintains older behavior where only a single peer entry
+ * was supported.
+ */
+ peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+ }
+ if (!peer || !peer->wps) {
+ wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
+ wpabuf_free(msg);
+ return HTTP_BAD_REQUEST;
+ }
+
+ res = wps_process_msg(peer->wps, WSC_UPnP, msg);
+ if (res == WPS_FAILURE) {
*reply = NULL;
- else
- *reply = wps_get_msg(iface->peer.wps, &op_code);
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
+ wps_upnp_peer_del(peer);
+ } else {
+ *reply = wps_get_msg(peer->wps, &op_code);
+ }
wpabuf_free(msg);
if (*reply == NULL)
return HTTP_INTERNAL_SERVER_ERROR;
@@ -1003,6 +1073,8 @@
ret = HTTP_INTERNAL_SERVER_ERROR;
goto error;
}
+ if (len > 0 && callback_urls[len - 1] == '\r')
+ callback_urls[len - 1] = '\0';
continue;
}
/* SID is only for renewal */
@@ -1214,18 +1286,25 @@
}
if (got_uuid) {
+ char str[80];
+
+ uuid_bin2str(uuid, str, sizeof(str));
+
s = subscription_find(sm, uuid);
if (s) {
struct subscr_addr *sa;
sa = dl_list_first(&s->addr_list, struct subscr_addr,
list);
- wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
- s, (sa && sa->domain_and_port) ?
+ wpa_printf(MSG_DEBUG,
+ "WPS UPnP: Unsubscribing %p (SID %s) %s",
+ s, str, (sa && sa->domain_and_port) ?
sa->domain_and_port : "-null-");
dl_list_del(&s->list);
subscription_destroy(s);
} else {
- wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
+ wpa_printf(MSG_INFO,
+ "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
+ str);
ret = HTTP_PRECONDITION_FAILED;
goto send_msg;
}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index e3020ec..e748785 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -39,7 +39,7 @@
# Use Android specific directory for control interface sockets
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/misc/wifi/sockets\"
# Use Android specific directory for wpa_cli command completion history
L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\"
@@ -315,6 +315,22 @@
NEED_GAS=y
endif
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
include $(LOCAL_PATH)/src/drivers/drivers.mk
ifdef CONFIG_AP
@@ -369,7 +385,6 @@
else
L_CFLAGS += -DEAP_TLS
OBJS += src/eap_peer/eap_tls.c
-OBJS_h += src/eap_server/eap_server_tls.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -380,7 +395,6 @@
L_CFLAGS += -DEAP_UNAUTH_TLS
ifndef CONFIG_EAP_TLS
OBJS += src/eap_peer/eap_tls.c
-OBJS_h += src/eap_server/eap_server_tls.c
TLS_FUNCS=y
endif
CONFIG_IEEE8021X_EAPOL=y
@@ -395,7 +409,6 @@
L_CFLAGS += -DEAP_PEAP
OBJS += src/eap_peer/eap_peap.c
OBJS += src/eap_common/eap_peap_common.c
-OBJS_h += src/eap_server/eap_server_peap.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -409,11 +422,12 @@
else
L_CFLAGS += -DEAP_TTLS
OBJS += src/eap_peer/eap_ttls.c
-OBJS_h += src/eap_server/eap_server_ttls.c
endif
-MS_FUNCS=y
TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
CHAP=y
+endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -425,7 +439,6 @@
else
L_CFLAGS += -DEAP_MD5
OBJS += src/eap_peer/eap_md5.c
-OBJS_h += src/eap_server/eap_server_md5.c
endif
CHAP=y
CONFIG_IEEE8021X_EAPOL=y
@@ -448,7 +461,6 @@
L_CFLAGS += -DEAP_MSCHAPv2
OBJS += src/eap_peer/eap_mschapv2.c
OBJS += src/eap_peer/mschapv2.c
-OBJS_h += src/eap_server/eap_server_mschapv2.c
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -462,7 +474,6 @@
else
L_CFLAGS += -DEAP_GTC
OBJS += src/eap_peer/eap_gtc.c
-OBJS_h += src/eap_server/eap_server_gtc.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -487,7 +498,6 @@
else
L_CFLAGS += -DEAP_SIM
OBJS += src/eap_peer/eap_sim.c
-OBJS_h += src/eap_server/eap_server_sim.c
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
@@ -515,7 +525,6 @@
else
L_CFLAGS += -DEAP_PSK
OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
-OBJS_h += src/eap_server/eap_server_psk.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_AES=y
@@ -532,7 +541,6 @@
else
L_CFLAGS += -DEAP_AKA
OBJS += src/eap_peer/eap_aka.c
-OBJS_h += src/eap_server/eap_server_aka.c
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
@@ -558,7 +566,6 @@
ifdef CONFIG_EAP_SIM_COMMON
OBJS += src/eap_common/eap_sim_common.c
-OBJS_h += src/eap_server/eap_sim_db.c
NEED_AES=y
NEED_FIPS186_2_PRF=y
endif
@@ -573,7 +580,6 @@
L_CFLAGS += -DEAP_FAST
OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
OBJS += src/eap_common/eap_fast_common.c
-OBJS_h += src/eap_server/eap_server_fast.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -588,7 +594,6 @@
else
L_CFLAGS += -DEAP_PAX
OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
-OBJS_h += src/eap_server/eap_server_pax.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -601,7 +606,6 @@
else
L_CFLAGS += -DEAP_SAKE
OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
-OBJS_h += src/eap_server/eap_server_sake.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -614,7 +618,6 @@
else
L_CFLAGS += -DEAP_GPSK
OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
-OBJS_h += src/eap_server/eap_server_gpsk.c
endif
CONFIG_IEEE8021X_EAPOL=y
ifdef CONFIG_EAP_GPSK_SHA256
@@ -627,7 +630,6 @@
ifdef CONFIG_EAP_PWD
L_CFLAGS += -DEAP_PWD
OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
-OBJS_h += src/eap_server/eap_server_pwd.c
CONFIG_IEEE8021X_EAPOL=y
NEED_SHA256=y
endif
@@ -640,12 +642,12 @@
else
L_CFLAGS += -DEAP_EKE
OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c
-OBJS_h += src/eap_server/eap_server_eke.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_SHA256=y
+NEED_AES_CBC=y
endif
ifdef CONFIG_WPS
@@ -662,7 +664,6 @@
OBJS += src/wps/wps_dev_attr.c
OBJS += src/wps/wps_enrollee.c
OBJS += src/wps/wps_registrar.c
-OBJS_h += src/eap_server/eap_server_wsc.c
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_SHA256=y
@@ -725,8 +726,6 @@
L_CFLAGS += -DEAP_IKEV2
OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
-OBJS_h += src/eap_server/eap_server_ikev2.c
-OBJS_h += src/eap_server/ikev2.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
@@ -742,7 +741,6 @@
else
L_CFLAGS += -DEAP_VENDOR_TEST
OBJS += src/eap_peer/eap_vendor_test.c
-OBJS_h += src/eap_server/eap_server_vendor_test.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -752,8 +750,6 @@
L_CFLAGS += -DEAP_TNC
OBJS += src/eap_peer/eap_tnc.c
OBJS += src/eap_peer/tncc.c
-OBJS_h += src/eap_server/eap_server_tnc.c
-OBJS_h += src/eap_server/tncs.c
NEED_BASE64=y
ifndef CONFIG_NATIVE_WINDOWS
ifndef CONFIG_DRIVER_BSD
@@ -860,36 +856,6 @@
endif
endif
-ifdef CONFIG_EAP_SERVER
-L_CFLAGS += -DEAP_SERVER
-OBJS_h += src/eap_server/eap_server.c
-OBJS_h += src/eap_server/eap_server_identity.c
-OBJS_h += src/eap_server/eap_server_methods.c
-endif
-
-ifdef CONFIG_RADIUS_CLIENT
-OBJS_h += src/utils/ip_addr.c
-OBJS_h += src/radius/radius.c
-OBJS_h += src/radius/radius_client.c
-endif
-
-ifdef CONFIG_AUTHENTICATOR
-OBJS_h += src/eapol_auth/eapol_auth_sm.c
-OBJS_h += src/ap/ieee802_1x.c
-endif
-
-ifdef CONFIG_WPA_AUTHENTICATOR
-OBJS_h += src/ap/wpa_auth.c
-OBJS_h += src/ap/wpa_auth_ie.c
-OBJS_h += src/ap/pmksa_cache_auth.c
-ifdef CONFIG_IEEE80211R
-OBJS_h += src/ap/wpa_auth_ft.c
-endif
-ifdef CONFIG_PEERKEY
-OBJS_h += src/ap/peerkey_auth.c
-endif
-endif
-
ifdef CONFIG_PCSC
# PC/SC interface for smartcards (USIM, GSM SIM)
L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
@@ -941,7 +907,6 @@
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
OBJS += src/eap_peer/eap_tls_common.c
-OBJS_h += src/eap_server/eap_server_tls_common.c
ifndef CONFIG_FIPS
NEED_TLS_PRF=y
NEED_SHA1=y
@@ -966,6 +931,7 @@
ifdef TLS_FUNCS
L_CFLAGS += -DEAP_TLS_OPENSSL
OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
LIBS += -lssl
endif
OBJS += src/crypto/crypto_openssl.c
@@ -973,6 +939,8 @@
ifdef NEED_FIPS186_2_PRF
OBJS += src/crypto/fips_prf_openssl.c
endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_p += -lcrypto
ifdef CONFIG_TLS_ADD_DL
@@ -1065,6 +1033,8 @@
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
@@ -1117,6 +1087,20 @@
endif
ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += src/crypto/aes-unwrap.c
endif
ifdef NEED_AES_EAX
@@ -1139,7 +1123,7 @@
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += src/crypto/aes-wrap.c
endif
endif
@@ -1215,11 +1199,17 @@
endif
endif
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
OBJS += src/crypto/rc4.c
endif
endif
+endif
SHA256OBJS = # none by default
ifdef NEED_SHA256
@@ -1231,16 +1221,26 @@
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += src/crypto/sha256-internal.c
endif
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += src/crypto/sha384-internal.c
+endif
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += src/crypto/sha512-internal.c
+endif
ifdef NEED_TLS_PRF_SHA256
SHA256OBJS += src/crypto/sha256-tlsprf.c
endif
ifdef NEED_HMAC_SHA256_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
SHA256OBJS += src/crypto/sha256-kdf.c
endif
OBJS += $(SHA256OBJS)
endif
ifdef NEED_SHA384
L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
endif
ifdef NEED_DH_GROUPS
@@ -1473,12 +1473,6 @@
OBJS += src/drivers/driver_common.c
-OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c
-OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c
-ifdef CONFIG_AUTHENTICATOR
-OBJS_wpa += tests/link_test.c
-endif
-OBJS_wpa += $(OBJS_l2)
OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
OBJS_t += src/radius/radius_client.c
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 1ac79b4..facd90e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,68 @@
ChangeLog for wpa_supplicant
+2015-09-27 - v2.5
+ * fixed P2P validation of SSID element length before copying it
+ [http://w1.fi/security/2015-1/] (CVE-2015-1863)
+ * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+ [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+ * fixed WMM Action frame parser (AP mode)
+ [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+ * fixed EAP-pwd peer missing payload length validation
+ [http://w1.fi/security/2015-4/]
+ (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146)
+ * fixed validation of WPS and P2P NFC NDEF record payload length
+ [http://w1.fi/security/2015-5/]
+ * nl80211:
+ - added VHT configuration for IBSS
+ - fixed vendor command handling to check OUI properly
+ - allow driver-based roaming to change ESS
+ * added AVG_BEACON_RSSI to SIGNAL_POLL output
+ * wpa_cli: added tab completion for number of commands
+ * removed unmaintained and not yet completed SChannel/CryptoAPI support
+ * modified Extended Capabilities element use in Probe Request frames to
+ include all cases if any of the values are non-zero
+ * added support for dynamically creating/removing a virtual interface
+ with interface_add/interface_remove
+ * added support for hashed password (NtHash) in EAP-pwd peer
+ * added support for memory-only PSK/passphrase (mem_only_psk=1 and
+ CTRL-REQ/RSP-PSK_PASSPHRASE)
+ * P2P
+ - optimize scan frequencies list when re-joining a persistent group
+ - fixed number of sequences with nl80211 P2P Device interface
+ - added operating class 125 for P2P use cases (this allows 5 GHz
+ channels 161 and 169 to be used if they are enabled in the current
+ regulatory domain)
+ - number of fixes to P2PS functionality
+ - do not allow 40 MHz co-ex PRI/SEC switch to force MCC
+ - extended support for preferred channel listing
+ * D-Bus:
+ - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface
+ - fixed PresenceRequest to use group interface
+ - added new signals: FindStopped, WPS pbc-overlap,
+ GroupFormationFailure, WPS timeout, InvitationReceived
+ - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient
+ - added manufacturer info
+ * added EAP-EKE peer support for deriving Session-Id
+ * added wps_priority configuration parameter to set the default priority
+ for all network profiles added by WPS
+ * added support to request a scan with specific SSIDs with the SCAN
+ command (optional "ssid <hexdump>" arguments)
+ * removed support for WEP40/WEP104 as a group cipher with WPA/WPA2
+ * fixed SAE group selection in an error case
+ * modified SAE routines to be more robust and PWE generation to be
+ stronger against timing attacks
+ * added support for Brainpool Elliptic Curves with SAE
+ * added support for CCMP-256 and GCMP-256 as group ciphers with FT
+ * fixed BSS selection based on estimated throughput
+ * added option to disable TLSv1.0 with OpenSSL
+ (phase1="tls_disable_tlsv1_0=1")
+ * added Fast Session Transfer (FST) module
+ * fixed OpenSSL PKCS#12 extra certificate handling
+ * fixed key derivation for Suite B 192-bit AKM (this breaks
+ compatibility with the earlier version)
+ * added RSN IE to Mesh Peering Open/Confirm frames
+ * number of small fixes
+
2015-03-15 - v2.4
* allow OpenSSL cipher configuration to be set for internal EAP server
(openssl_ciphers parameter)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 05d8e0a..e3d3acf 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -17,6 +17,16 @@
-include .config
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
ifdef CONFIG_TESTING_OPTIONS
CFLAGS += -DCONFIG_TESTING_OPTIONS
CONFIG_WPS_TESTING=y
@@ -131,12 +141,15 @@
OBJS += ../src/utils/$(CONFIG_ELOOP).o
OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+ifndef CONFIG_OSX
ifeq ($(CONFIG_ELOOP), eloop)
# Using glibc < 2.17 requires -lrt for clock_gettime()
+# OS X has an alternate implementation
LIBS += -lrt
LIBS_c += -lrt
LIBS_p += -lrt
endif
+endif
ifdef CONFIG_ELOOP_POLL
CFLAGS += -DCONFIG_ELOOP_POLL
@@ -275,6 +288,10 @@
NEED_RC4=y
else
CFLAGS += -DCONFIG_NO_WPA
+ifeq ($(CONFIG_TLS), internal)
+NEED_SHA1=y
+NEED_MD5=y
+endif
endif
ifdef CONFIG_IBSS_RSN
@@ -383,7 +400,6 @@
else
CFLAGS += -DEAP_TLS
OBJS += ../src/eap_peer/eap_tls.o
-OBJS_h += ../src/eap_server/eap_server_tls.o
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -394,7 +410,6 @@
CFLAGS += -DEAP_UNAUTH_TLS
ifndef CONFIG_EAP_TLS
OBJS += ../src/eap_peer/eap_tls.o
-OBJS_h += ../src/eap_server/eap_server_tls.o
TLS_FUNCS=y
endif
CONFIG_IEEE8021X_EAPOL=y
@@ -409,7 +424,6 @@
CFLAGS += -DEAP_PEAP
OBJS += ../src/eap_peer/eap_peap.o
OBJS += ../src/eap_common/eap_peap_common.o
-OBJS_h += ../src/eap_server/eap_server_peap.o
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -423,11 +437,12 @@
else
CFLAGS += -DEAP_TTLS
OBJS += ../src/eap_peer/eap_ttls.o
-OBJS_h += ../src/eap_server/eap_server_ttls.o
endif
-MS_FUNCS=y
TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
CHAP=y
+endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -439,7 +454,6 @@
else
CFLAGS += -DEAP_MD5
OBJS += ../src/eap_peer/eap_md5.o
-OBJS_h += ../src/eap_server/eap_server_md5.o
endif
CHAP=y
CONFIG_IEEE8021X_EAPOL=y
@@ -462,7 +476,6 @@
CFLAGS += -DEAP_MSCHAPv2
OBJS += ../src/eap_peer/eap_mschapv2.o
OBJS += ../src/eap_peer/mschapv2.o
-OBJS_h += ../src/eap_server/eap_server_mschapv2.o
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -476,7 +489,6 @@
else
CFLAGS += -DEAP_GTC
OBJS += ../src/eap_peer/eap_gtc.o
-OBJS_h += ../src/eap_server/eap_server_gtc.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -501,7 +513,6 @@
else
CFLAGS += -DEAP_SIM
OBJS += ../src/eap_peer/eap_sim.o
-OBJS_h += ../src/eap_server/eap_server_sim.o
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
@@ -529,7 +540,6 @@
else
CFLAGS += -DEAP_PSK
OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o
-OBJS_h += ../src/eap_server/eap_server_psk.o
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_AES=y
@@ -546,7 +556,6 @@
else
CFLAGS += -DEAP_AKA
OBJS += ../src/eap_peer/eap_aka.o
-OBJS_h += ../src/eap_server/eap_server_aka.o
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
@@ -572,7 +581,6 @@
ifdef CONFIG_EAP_SIM_COMMON
OBJS += ../src/eap_common/eap_sim_common.o
-OBJS_h += ../src/eap_server/eap_sim_db.o
NEED_AES=y
NEED_FIPS186_2_PRF=y
endif
@@ -587,7 +595,6 @@
CFLAGS += -DEAP_FAST
OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o
OBJS += ../src/eap_common/eap_fast_common.o
-OBJS_h += ../src/eap_server/eap_server_fast.o
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
@@ -602,7 +609,6 @@
else
CFLAGS += -DEAP_PAX
OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o
-OBJS_h += ../src/eap_server/eap_server_pax.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -615,7 +621,6 @@
else
CFLAGS += -DEAP_SAKE
OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o
-OBJS_h += ../src/eap_server/eap_server_sake.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -628,7 +633,6 @@
else
CFLAGS += -DEAP_GPSK
OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o
-OBJS_h += ../src/eap_server/eap_server_gpsk.o
endif
CONFIG_IEEE8021X_EAPOL=y
ifdef CONFIG_EAP_GPSK_SHA256
@@ -641,7 +645,6 @@
ifdef CONFIG_EAP_PWD
CFLAGS += -DEAP_PWD
OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
-OBJS_h += ../src/eap_server/eap_server_pwd.o
CONFIG_IEEE8021X_EAPOL=y
NEED_SHA256=y
endif
@@ -654,12 +657,12 @@
else
CFLAGS += -DEAP_EKE
OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o
-OBJS_h += ../src/eap_server/eap_server_eke.o
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_SHA256=y
+NEED_AES_CBC=y
endif
ifdef CONFIG_WPS
@@ -676,7 +679,6 @@
OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o
-OBJS_h += ../src/eap_server/eap_server_wsc.o
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_SHA256=y
@@ -739,8 +741,6 @@
CFLAGS += -DEAP_IKEV2
OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o
OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
-OBJS_h += ../src/eap_server/eap_server_ikev2.o
-OBJS_h += ../src/eap_server/ikev2.o
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
@@ -756,7 +756,6 @@
else
CFLAGS += -DEAP_VENDOR_TEST
OBJS += ../src/eap_peer/eap_vendor_test.o
-OBJS_h += ../src/eap_server/eap_server_vendor_test.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
@@ -766,8 +765,6 @@
CFLAGS += -DEAP_TNC
OBJS += ../src/eap_peer/eap_tnc.o
OBJS += ../src/eap_peer/tncc.o
-OBJS_h += ../src/eap_server/eap_server_tnc.o
-OBJS_h += ../src/eap_server/tncs.o
NEED_BASE64=y
ifndef CONFIG_NATIVE_WINDOWS
ifndef CONFIG_DRIVER_BSD
@@ -887,36 +884,6 @@
endif
endif
-ifdef CONFIG_EAP_SERVER
-CFLAGS += -DEAP_SERVER
-OBJS_h += ../src/eap_server/eap_server.o
-OBJS_h += ../src/eap_server/eap_server_identity.o
-OBJS_h += ../src/eap_server/eap_server_methods.o
-endif
-
-ifdef CONFIG_RADIUS_CLIENT
-OBJS_h += ../src/utils/ip_addr.o
-OBJS_h += ../src/radius/radius.o
-OBJS_h += ../src/radius/radius_client.o
-endif
-
-ifdef CONFIG_AUTHENTICATOR
-OBJS_h += ../src/eapol_auth/eapol_auth_sm.o
-OBJS_h += ../src/ap/ieee802_1x.o
-endif
-
-ifdef CONFIG_WPA_AUTHENTICATOR
-OBJS_h += ../src/ap/wpa_auth.o
-OBJS_h += ../src/ap/wpa_auth_ie.o
-OBJS_h += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
-OBJS_h += ../src/ap/wpa_auth_ft.o
-endif
-ifdef CONFIG_PEERKEY
-OBJS_h += ../src/ap/peerkey_auth.o
-endif
-endif
-
ifdef CONFIG_PCSC
# PC/SC interface for smartcards (USIM, GSM SIM)
CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
@@ -968,7 +935,6 @@
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
OBJS += ../src/eap_peer/eap_tls_common.o
-OBJS_h += ../src/eap_server/eap_server_tls_common.o
ifndef CONFIG_FIPS
NEED_TLS_PRF=y
NEED_SHA1=y
@@ -993,6 +959,7 @@
ifdef TLS_FUNCS
CFLAGS += -DEAP_TLS_OPENSSL
OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl
endif
OBJS += ../src/crypto/crypto_openssl.o
@@ -1001,6 +968,8 @@
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_openssl.o
endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_p += -lcrypto
ifdef CONFIG_TLS_ADD_DL
@@ -1094,6 +1063,8 @@
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
@@ -1146,6 +1117,20 @@
endif
ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += ../src/crypto/aes-unwrap.o
endif
ifdef NEED_AES_EAX
@@ -1171,7 +1156,7 @@
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += ../src/crypto/aes-wrap.o
endif
endif
@@ -1243,11 +1228,17 @@
endif
endif
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
OBJS += ../src/crypto/rc4.o
endif
endif
+endif
SHA256OBJS = # none by default
ifdef NEED_SHA256
@@ -1259,16 +1250,26 @@
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += ../src/crypto/sha256-internal.o
endif
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += ../src/crypto/sha384-internal.o
+endif
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += ../src/crypto/sha512-internal.o
+endif
ifdef NEED_TLS_PRF_SHA256
SHA256OBJS += ../src/crypto/sha256-tlsprf.o
endif
ifdef NEED_HMAC_SHA256_KDF
+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
OBJS += ../src/crypto/sha256-kdf.o
endif
OBJS += $(SHA256OBJS)
endif
ifdef NEED_SHA384
CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
endif
ifdef NEED_DH_GROUPS
@@ -1383,7 +1384,7 @@
ifdef CONFIG_READLINE
OBJS_c += ../src/utils/edit_readline.o
-LIBS_c += -lncurses -lreadline
+LIBS_c += -lreadline -lncurses
else
ifdef CONFIG_WPA_CLI_EDIT
OBJS_c += ../src/utils/edit.o
@@ -1414,6 +1415,10 @@
CFLAGS += -DCONFIG_IPV6
endif
+ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
+CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR
+endif
+
ifdef NEED_BASE64
OBJS += ../src/utils/base64.o
endif
@@ -1540,12 +1545,6 @@
OBJS += ../src/drivers/driver_common.o
OBJS_priv += ../src/drivers/driver_common.o
-OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
-OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
-ifdef CONFIG_AUTHENTICATOR
-OBJS_wpa += tests/link_test.o
-endif
-OBJS_wpa += $(OBJS_l2)
OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
OBJS_t += ../src/radius/radius_client.o
@@ -1591,6 +1590,24 @@
endif
endif
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+FST_OBJS += ../src/fst/fst.o
+FST_OBJS += ../src/fst/fst_session.o
+FST_OBJS += ../src/fst/fst_iface.o
+FST_OBJS += ../src/fst/fst_group.o
+FST_OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_CTRL_IFACE
+FST_OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+OBJS += $(FST_OBJS)
+OBJS_t += $(FST_OBJS)
+OBJS_t2 += $(FST_OBJS)
+endif
+
ifndef LDO
LDO=$(CC)
endif
@@ -1644,9 +1661,11 @@
LIBCTRL += ../src/common/wpa_ctrl.o
LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/common.o
LIBCTRL += ../src/utils/wpa_debug.o
LIBCTRLSO += ../src/common/wpa_ctrl.c
LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
+LIBCTRLSO += ../src/utils/common.c
LIBCTRLSO += ../src/utils/wpa_debug.c
libwpa_client.a: $(LIBCTRL)
@@ -1658,12 +1677,12 @@
@$(E) " CC $@ ($^)"
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
-link_test: $(OBJS) $(OBJS_h) tests/link_test.o
- $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+libwpa_test1: libwpa_test.o libwpa_client.a
+ $(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c)
@$(E) " LD " $@
-test_wpa: $(OBJS_wpa) $(OBJS_h)
- $(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+libwpa_test2: libwpa_test.o libwpa_client.so
+ $(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c)
@$(E) " LD " $@
nfc_pw_token: $(OBJS_nfc)
@@ -1748,17 +1767,6 @@
wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
$(MAKE) -C wpa_gui-qt4
-TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \
- ../src/utils/common.o ../src/utils/os_unix.o \
- ../src/utils/wpa_debug.o $(AESOBJS) \
- tests/test_eap_sim_common.o
-test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS)
- $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS)
- ./test-eap_sim_common
- rm test-eap_sim_common
-
-tests: test-eap_sim_common
-
FIPSDIR=/usr/local/ssl/fips-2.0
FIPSLD=$(FIPSDIR)/bin/fipsld
fips:
@@ -1779,5 +1787,6 @@
rm -rf lcov-html
rm -f libwpa_client.a
rm -f libwpa_client.so
+ rm -f libwpa_test1 libwpa_test2
-include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8d27bb2..e9c0a01 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -32,6 +32,9 @@
#CONFIG_DRIVER_NL80211=y
CONFIG_LIBNL20=y
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
#CONFIG_DRIVER_BSD=y
#CFLAGS += -I/usr/local/include
@@ -476,4 +479,7 @@
# External password backend for testing purposes (developer use)
#CONFIG_EXT_PASSWORD_TEST=y
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index f3960c5..27fa2a9 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -56,12 +56,32 @@
if (!conf->secondary_channel)
goto no_vht;
- center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ switch (conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_80MHZ:
+ case VHT_CHANWIDTH_80P80MHZ:
+ center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+ break;
+ default:
+ /*
+ * conf->vht_oper_chwidth might not be set for non-P2P GO cases,
+ * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
+ * not supported.
+ */
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+ center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+ if (!center_chan) {
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+ center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
+ channel);
+ }
+ break;
+ }
if (!center_chan)
goto no_vht;
- /* Use 80 MHz channel */
- conf->vht_oper_chwidth = 1;
conf->vht_oper_centr_freq_seg0_idx = center_chan;
return;
@@ -76,10 +96,19 @@
#endif /* CONFIG_IEEE80211N */
-void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid,
- struct hostapd_config *conf)
+int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct hostapd_config *conf)
{
+ conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+ &conf->channel);
+
+ if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+ ssid->frequency);
+ return -1;
+ }
+
/* TODO: enable HT40 if driver supports it;
* drop to 11b if driver does not support 11g */
@@ -142,7 +171,32 @@
}
}
}
+
+ if (conf->secondary_channel) {
+ struct wpa_supplicant *iface;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+ {
+ if (iface == wpa_s ||
+ iface->wpa_state < WPA_AUTHENTICATING ||
+ (int) iface->assoc_freq != ssid->frequency)
+ continue;
+
+ /*
+ * Do not allow 40 MHz co-ex PRI/SEC switch to force us
+ * to change our PRI channel since we have an existing,
+ * concurrent connection on that channel and doing
+ * multi-channel concurrency is likely to cause more
+ * harm than using different PRI/SEC selection in
+ * environment with multiple BSSes on these two channels
+ * with mixed 20 MHz or PRI channel selection.
+ */
+ conf->no_pri_sec_switch = 1;
+ }
+ }
#endif /* CONFIG_IEEE80211N */
+
+ return 0;
}
@@ -156,15 +210,8 @@
os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
- conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
- &conf->channel);
- if (conf->hw_mode == NUM_HOSTAPD_MODES) {
- wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
- ssid->frequency);
+ if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf))
return -1;
- }
-
- wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
conf->ieee80211h = 1;
@@ -274,13 +321,17 @@
conf->beacon_int = wpa_s->conf->beacon_int;
#ifdef CONFIG_P2P
- if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
- wpa_printf(MSG_INFO,
- "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
- wpa_s->conf->p2p_go_ctwindow, conf->beacon_int);
- conf->p2p_go_ctwindow = 0;
- } else {
- conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+ if (ssid->mode == WPAS_MODE_P2P_GO ||
+ ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+ if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
+ wpa_printf(MSG_INFO,
+ "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
+ wpa_s->conf->p2p_go_ctwindow,
+ conf->beacon_int);
+ conf->p2p_go_ctwindow = 0;
+ } else {
+ conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+ }
}
#endif /* CONFIG_P2P */
@@ -604,6 +655,13 @@
return -1;
}
+ /* Use the maximum oper channel width if it's given. */
+ if (ssid->max_oper_chwidth)
+ conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+ ieee80211_freq_to_chan(ssid->vht_center_freq2,
+ &conf->vht_oper_centr_freq_seg1_idx);
+
os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
wpa_s->conf->wmm_ac_params,
sizeof(wpa_s->conf->wmm_ac_params));
@@ -1182,7 +1240,10 @@
return;
wpa_s->assoc_freq = freq;
- hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
+ if (wpa_s->current_ssid)
+ wpa_s->current_ssid->frequency = freq;
+ hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht,
+ offset, width, cf1, cf2);
}
@@ -1346,3 +1407,10 @@
radar->chan_width, radar->cf1, radar->cf2);
}
#endif /* NEED_AP_MLME */
+
+
+void ap_periodic(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->ap_iface)
+ hostapd_periodic_iface(wpa_s->ap_iface);
+}
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 3f4151d..2b8a1d4 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -76,9 +76,9 @@
const struct wpabuf *pw, const u8 *pubkey_hash);
struct hostapd_config;
-void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
- struct wpa_ssid *ssid,
- struct hostapd_config *conf);
+int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid,
+ struct hostapd_config *conf);
int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
@@ -93,4 +93,6 @@
void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
+void ap_periodic(struct wpa_supplicant *wpa_s);
+
#endif /* AP_H */
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index a2cf7a5..d12eb21 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -1,6 +1,7 @@
/*
* WPA Supplicant - auto scan
* Copyright (c) 2012, Intel Corporation. All rights reserved.
+ * Copyright 2015 Intel Deutschland GmbH
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -50,6 +51,11 @@
size_t nlen;
int i;
const struct autoscan_ops *ops = NULL;
+ struct sched_scan_plan *scan_plans;
+
+ /* Give preference to scheduled scan plans if supported/configured */
+ if (wpa_s->sched_scan_plans)
+ return 0;
if (wpa_s->autoscan && wpa_s->autoscan_priv)
return 0;
@@ -79,11 +85,23 @@
return -1;
}
+ scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans));
+ if (!scan_plans)
+ return -1;
+
wpa_s->autoscan_params = NULL;
wpa_s->autoscan_priv = ops->init(wpa_s, params);
- if (wpa_s->autoscan_priv == NULL)
+ if (!wpa_s->autoscan_priv) {
+ os_free(scan_plans);
return -1;
+ }
+
+ scan_plans[0].interval = 5;
+ scan_plans[0].iterations = 0;
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = scan_plans;
+ wpa_s->sched_scan_plans_num = 1;
wpa_s->autoscan = ops;
wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
@@ -116,7 +134,10 @@
wpa_s->autoscan_priv = NULL;
wpa_s->scan_interval = 5;
- wpa_s->sched_scan_interval = 0;
+
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = NULL;
+ wpa_s->sched_scan_plans_num = 0;
}
}
@@ -134,7 +155,7 @@
return -1;
wpa_s->scan_interval = interval;
- wpa_s->sched_scan_interval = interval;
+ wpa_s->sched_scan_plans[0].interval = interval;
request_scan(wpa_s);
}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 8134562..24cc986 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -19,11 +19,6 @@
#include "bss.h"
-/**
- * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
- */
-#define WPA_BSS_EXPIRATION_PERIOD 10
-
#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0)
#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1)
#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2)
@@ -65,6 +60,9 @@
anqp = os_zalloc(sizeof(*anqp));
if (anqp == NULL)
return NULL;
+#ifdef CONFIG_INTERWORKING
+ dl_list_init(&anqp->anqp_elems);
+#endif /* CONFIG_INTERWORKING */
anqp->users = 1;
return anqp;
}
@@ -85,6 +83,7 @@
#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
#ifdef CONFIG_INTERWORKING
+ dl_list_init(&n->anqp_elems);
ANQP_DUP(capability_list);
ANQP_DUP(venue_name);
ANQP_DUP(network_auth_type);
@@ -146,6 +145,10 @@
*/
static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
{
+#ifdef CONFIG_INTERWORKING
+ struct wpa_bss_anqp_elem *elem;
+#endif /* CONFIG_INTERWORKING */
+
if (anqp == NULL)
return;
@@ -164,6 +167,13 @@
wpabuf_free(anqp->nai_realm);
wpabuf_free(anqp->anqp_3gpp);
wpabuf_free(anqp->domain_name);
+
+ while ((elem = dl_list_first(&anqp->anqp_elems,
+ struct wpa_bss_anqp_elem, list))) {
+ dl_list_del(&elem->list);
+ wpabuf_free(elem->payload);
+ os_free(elem);
+ }
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
wpabuf_free(anqp->hs20_capability_list);
@@ -311,10 +321,18 @@
static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
{
- return bss == wpa_s->current_bss ||
- (!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));
+ if (bss == wpa_s->current_bss)
+ return 1;
+
+ if (wpa_s->current_bss &&
+ (bss->ssid_len != wpa_s->current_bss->ssid_len ||
+ os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
+ bss->ssid_len) != 0))
+ return 0; /* SSID has changed */
+
+ return !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);
}
@@ -390,8 +408,9 @@
dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
wpa_s->num_bss++;
wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
- " SSID '%s'",
- bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+ " SSID '%s' freq %d",
+ bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
+ bss->freq);
wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
return bss;
}
@@ -534,6 +553,9 @@
u32 changes;
changes = wpa_bss_compare_res(bss, res);
+ if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+ wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
+ MAC2STR(bss->bssid), bss->freq, res->freq);
bss->scan_miss_count = 0;
bss->last_update_idx = wpa_s->bss_update_idx;
wpa_bss_copy_res(bss, res, fetch_time);
@@ -777,7 +799,7 @@
struct wpa_bss *bss, *n;
os_get_reltime(&wpa_s->last_scan);
- if (!new_scan)
+ if ((info && info->aborted) || !new_scan)
return; /* do not expire entries without new scan */
dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
@@ -828,16 +850,6 @@
}
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
-{
- struct wpa_supplicant *wpa_s = eloop_ctx;
-
- wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
- eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
- wpa_bss_timeout, wpa_s, NULL);
-}
-
-
/**
* wpa_bss_init - Initialize BSS table
* @wpa_s: Pointer to wpa_supplicant data
@@ -850,8 +862,6 @@
{
dl_list_init(&wpa_s->bss);
dl_list_init(&wpa_s->bss_id);
- eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
- wpa_bss_timeout, wpa_s, NULL);
return 0;
}
@@ -883,7 +893,6 @@
*/
void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
{
- eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
wpa_bss_flush(wpa_s);
}
@@ -1015,8 +1024,8 @@
pos = (const u8 *) (bss + 1);
end = pos + bss->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
@@ -1043,8 +1052,8 @@
pos = (const u8 *) (bss + 1);
end = pos + bss->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1080,8 +1089,8 @@
pos += bss->ie_len;
end = pos + bss->beacon_ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1116,8 +1125,8 @@
pos = (const u8 *) (bss + 1);
end = pos + bss->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1161,8 +1170,8 @@
pos += bss->ie_len;
end = pos + bss->beacon_ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b215380..4a782af 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,6 +19,12 @@
#define WPA_BSS_ASSOCIATED BIT(5)
#define WPA_BSS_ANQP_FETCH_TRIED BIT(6)
+struct wpa_bss_anqp_elem {
+ struct dl_list list;
+ u16 infoid;
+ struct wpabuf *payload;
+};
+
/**
* struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
*/
@@ -34,6 +40,7 @@
struct wpabuf *nai_realm;
struct wpabuf *anqp_3gpp;
struct wpabuf *domain_name;
+ struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
struct wpabuf *hs20_capability_list;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 4d801cc..f2ae4fd 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -15,6 +15,7 @@
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
#include "p2p/p2p.h"
+#include "fst/fst.h"
#include "config.h"
@@ -898,6 +899,9 @@
static int wpa_config_parse_cipher(int line, const char *value)
{
+#ifdef CONFIG_NO_WPA
+ return -1;
+#else /* CONFIG_NO_WPA */
int val = wpa_parse_cipher(value);
if (val < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
@@ -910,12 +914,16 @@
return -1;
}
return val;
+#endif /* CONFIG_NO_WPA */
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_cipher(int cipher)
{
+#ifdef CONFIG_NO_WPA
+ return NULL;
+#else /* CONFIG_NO_WPA */
char *buf = os_zalloc(50);
if (buf == NULL)
return NULL;
@@ -926,6 +934,7 @@
}
return buf;
+#endif /* CONFIG_NO_WPA */
}
#endif /* NO_CONFIG_WRITE */
@@ -1836,6 +1845,8 @@
{ FUNC(auth_alg) },
{ FUNC(scan_freq) },
{ FUNC(freq_list) },
+ { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
+ VHT_CHANWIDTH_80P80MHZ) },
#ifdef IEEE8021X_EAPOL
{ FUNC(eap) },
{ STR_LENe(identity) },
@@ -2269,6 +2280,9 @@
os_free(config->osu_dir);
os_free(config->bgscan);
os_free(config->wowlan_triggers);
+ os_free(config->fst_group_id);
+ os_free(config->sched_scan_plans);
+
os_free(config);
}
@@ -3515,9 +3529,12 @@
config->user_mpm = DEFAULT_USER_MPM;
config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
+ config->dot11RSNASAERetransPeriod =
+ DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
config->fast_reauth = DEFAULT_FAST_REAUTH;
config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+ config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
@@ -3535,6 +3552,7 @@
config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
config->cert_in_cb = DEFAULT_CERT_IN_CB;
+ config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
@@ -4128,6 +4146,7 @@
{ INT(user_mpm), 0 },
{ INT_RANGE(max_peer_links, 0, 255), 0 },
{ INT(mesh_max_inactivity), 0 },
+ { INT(dot11RSNASAERetransPeriod), 0 },
#endif /* CONFIG_MESH */
{ INT(disable_scan_offload), 0 },
{ INT(fast_reauth), 0 },
@@ -4162,8 +4181,8 @@
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
- { INT(p2p_listen_reg_class), 0 },
- { INT(p2p_listen_channel), 0 },
+ { INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+ { INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
{ INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
{ INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
@@ -4171,6 +4190,7 @@
{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
{ INT(p2p_group_idle), 0 },
+ { INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
{ INT_RANGE(p2p_passphrase_len, 8, 63),
CFG_CHANGED_P2P_PASSPHRASE_LEN },
{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
@@ -4234,6 +4254,13 @@
{ INT(passive_scan), 0 },
{ INT(reassoc_same_bss_optim), 0 },
{ INT(wps_priority), 0},
+#ifdef CONFIG_FST
+ { STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
+ { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
+ { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
+#endif /* CONFIG_FST */
+ { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+ { STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
};
#undef FUNC
@@ -4292,6 +4319,23 @@
}
+int wpa_config_get_num_global_field_names(void)
+{
+ return NUM_GLOBAL_FIELDS;
+}
+
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
+{
+ if (i >= NUM_GLOBAL_FIELDS)
+ return NULL;
+
+ if (no_var)
+ *no_var = !global_fields[i].param1;
+ return global_fields[i].name;
+}
+
+
int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
{
size_t i;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index d8ca054..86f940d 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -18,6 +18,11 @@
#define DEFAULT_USER_MPM 1
#define DEFAULT_MAX_PEER_LINKS 99
#define DEFAULT_MESH_MAX_INACTIVITY 300
+/*
+ * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
+ * but use 1000 ms in practice to avoid issues on low power CPUs.
+ */
+#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
#define DEFAULT_FAST_REAUTH 1
#define DEFAULT_P2P_GO_INTENT 7
#define DEFAULT_P2P_INTRA_BSS 1
@@ -34,6 +39,7 @@
#define DEFAULT_KEY_MGMT_OFFLOAD 1
#define DEFAULT_CERT_IN_CB 1
#define DEFAULT_P2P_GO_CTWINDOW 0
+#define DEFAULT_WPA_RSC_RELAXATION 1
#include "config_ssid.h"
#include "wps/wps.h"
@@ -326,6 +332,7 @@
#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
+#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
/**
* struct wpa_config - wpa_supplicant configuration data
@@ -401,6 +408,11 @@
* one by one until the driver reports successful association; each
* network block should have explicit security policy (i.e., only one
* option in the lists) for key_mgmt, pairwise, group, proto variables.
+ *
+ * Note: ap_scan=2 should not be used with the nl80211 driver interface
+ * (the current Linux interface). ap_scan=1 is optimized work working
+ * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
+ * the network block can be used with nl80211.
*/
int ap_scan;
@@ -734,6 +746,39 @@
int p2p_group_idle;
/**
+ * p2p_go_freq_change_policy - The GO frequency change policy
+ *
+ * This controls the behavior of the GO when there is a change in the
+ * map of the currently used frequencies in case more than one channel
+ * is supported.
+ *
+ * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
+ * possible. In case the GO is the only interface using its frequency
+ * and there are other station interfaces on other frequencies, the GO
+ * will migrate to one of these frequencies.
+ *
+ * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
+ * but a transition is possible only in case one of the other used
+ * frequencies is one of the frequencies in the intersection of the
+ * frequency list of the local device and the peer device.
+ *
+ * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
+ *
+ * @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as
+ * P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only
+ * if all the group members advertise eCSA support.
+ */
+ enum {
+ P2P_GO_FREQ_MOVE_SCM = 0,
+ P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
+ P2P_GO_FREQ_MOVE_STAY = 2,
+ P2P_GO_FREQ_MOVE_SCM_ECSA = 3,
+ P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA,
+ } p2p_go_freq_change_policy;
+
+#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
+
+ /**
* p2p_passphrase_len - Passphrase length (8..63) for P2P GO
*
* This parameter controls the length of the random passphrase that is
@@ -1161,6 +1206,15 @@
int mesh_max_inactivity;
/**
+ * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
+ *
+ * This timeout value is used in mesh STA to retransmit
+ * SAE Authentication frame.
+ * By default: 1000 milliseconds.
+ */
+ int dot11RSNASAERetransPeriod;
+
+ /**
* passive_scan - Whether to force passive scan for network connection
*
* This parameter can be used to force only passive scanning to be used
@@ -1184,6 +1238,43 @@
* by executing the WPS protocol.
*/
int wps_priority;
+
+ /**
+ * fst_group_id - FST group ID
+ */
+ char *fst_group_id;
+
+ /**
+ * fst_priority - priority of the interface within the FST group
+ */
+ int fst_priority;
+
+ /**
+ * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
+ * interface.
+ */
+ int fst_llt;
+
+ /**
+ * wpa_rsc_relaxation - RSC relaxation on GTK installation
+ *
+ * Values:
+ * 0 - use the EAPOL-Key RSC value on GTK installation
+ * 1 - use the null RSC if a bogus RSC value is detected in message 3
+ * of 4-Way Handshake or message 1 of Group Key Handshake.
+ */
+ int wpa_rsc_relaxation;
+
+ /**
+ * sched_scan_plans - Scan plans for scheduled scan
+ *
+ * Each scan plan specifies the interval between scans and the number of
+ * iterations. The last scan plan only specifies the scan interval and
+ * will be run infinitely.
+ *
+ * format: <interval:iterations> <interval2:iterations2> ... <interval>
+ */
+ char *sched_scan_plans;
};
@@ -1242,6 +1333,9 @@
/* Prototypes for common functions from config.c */
int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
+int wpa_config_get_num_global_field_names(void);
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var);
/* Prototypes for backend specific functions from the selected config_*.c */
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index aeea70c..80e3e56 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -751,6 +751,7 @@
INT(disabled);
INT(peerkey);
INT(mixed_cell);
+ INT(max_oper_chwidth);
#ifdef CONFIG_IEEE80211W
write_int(f, "ieee80211w", ssid->ieee80211w,
MGMT_FRAME_PROTECTION_DEFAULT);
@@ -1133,6 +1134,9 @@
config->p2p_ignore_shared_freq);
if (config->p2p_cli_probe)
fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
+ if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
+ fprintf(f, "p2p_go_freq_change_policy=%u\n",
+ config->p2p_go_freq_change_policy);
#endif /* CONFIG_P2P */
if (config->country[0] && config->country[1]) {
fprintf(f, "country=%c%c\n",
@@ -1282,6 +1286,11 @@
fprintf(f, "mesh_max_inactivity=%d\n",
config->mesh_max_inactivity);
+ if (config->dot11RSNASAERetransPeriod !=
+ DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
+ fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
+ config->dot11RSNASAERetransPeriod);
+
if (config->passive_scan)
fprintf(f, "passive_scan=%d\n", config->passive_scan);
@@ -1291,6 +1300,13 @@
if (config->wps_priority)
fprintf(f, "wps_priority=%d\n", config->wps_priority);
+
+ if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
+ fprintf(f, "wpa_rsc_relaxation=%d\n",
+ config->wpa_rsc_relaxation);
+
+ if (config->sched_scan_plans)
+ fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1353,7 +1369,7 @@
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
- os_fsync(f);
+ os_fdatasync(f);
fclose(f);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 1c63e51..de8157a 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -225,7 +225,9 @@
*
* scan_ssid can be used to scan for APs using hidden SSIDs.
* Note: Many drivers do not support this. ap_mode=2 can be used with
- * such drivers to use hidden SSIDs.
+ * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
+ * do support scan_ssid=1 and that should be used with them instead of
+ * ap_scan=2.
*/
int scan_ssid;
@@ -447,6 +449,10 @@
int vht;
+ u8 max_oper_chwidth;
+
+ unsigned int vht_center_freq2;
+
/**
* wpa_ptk_rekey - Maximum lifetime for PTK in seconds
*
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index bab10ad..7b45a3a 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -28,6 +28,8 @@
#include "rsn_supp/pmksa_cache.h"
#include "l2_packet/l2_packet.h"
#include "wps/wps.h"
+#include "fst/fst.h"
+#include "fst/fst_ctrl_iface.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
@@ -465,6 +467,8 @@
wpa_s->extra_roc_dur = atoi(value);
} else if (os_strcasecmp(cmd, "test_failure") == 0) {
wpa_s->test_failure = atoi(value);
+ } else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
+ wpa_s->p2p_go_csa_on_inv = !!atoi(value);
#endif /* CONFIG_TESTING_OPTIONS */
#ifndef CONFIG_NO_CONFIG_BLOBS
} else if (os_strcmp(cmd, "blob") == 0) {
@@ -2134,45 +2138,6 @@
}
-static const char * debug_level_str(int level)
-{
- switch (level) {
- case MSG_EXCESSIVE:
- return "EXCESSIVE";
- case MSG_MSGDUMP:
- return "MSGDUMP";
- case MSG_DEBUG:
- return "DEBUG";
- case MSG_INFO:
- return "INFO";
- case MSG_WARNING:
- return "WARNING";
- case MSG_ERROR:
- return "ERROR";
- default:
- return "?";
- }
-}
-
-
-static int str_to_debug_level(const char *s)
-{
- if (os_strcasecmp(s, "EXCESSIVE") == 0)
- return MSG_EXCESSIVE;
- if (os_strcasecmp(s, "MSGDUMP") == 0)
- return MSG_MSGDUMP;
- if (os_strcasecmp(s, "DEBUG") == 0)
- return MSG_DEBUG;
- if (os_strcasecmp(s, "INFO") == 0)
- return MSG_INFO;
- if (os_strcasecmp(s, "WARNING") == 0)
- return MSG_WARNING;
- if (os_strcasecmp(s, "ERROR") == 0)
- return MSG_ERROR;
- return -1;
-}
-
-
static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
char *cmd, char *buf,
size_t buflen)
@@ -2205,7 +2170,7 @@
}
}
- if (cmd && os_strlen(cmd)) {
+ if (os_strlen(cmd)) {
int level = str_to_debug_level(cmd);
if (level < 0)
return -1;
@@ -2582,6 +2547,14 @@
pos += ret;
}
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
+ ret = os_snprintf(pos, end - pos, "[FST]");
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+#endif /* CONFIG_FST */
ret = os_snprintf(pos, end - pos, "\t%s",
wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -2773,6 +2746,8 @@
}
}
+ wpa_s->scan_min_time.sec = 0;
+ wpa_s->scan_min_time.usec = 0;
wpa_supplicant_select_network(wpa_s, ssid);
return 0;
@@ -2810,6 +2785,8 @@
return 0;
}
}
+ wpa_s->scan_min_time.sec = 0;
+ wpa_s->scan_min_time.usec = 0;
wpa_supplicant_enable_network(wpa_s, ssid);
return 0;
@@ -3064,19 +3041,19 @@
*name++ = '\0';
id = atoi(cmd);
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
id, name);
ssid = wpa_config_get_network(wpa_s->conf, id);
if (ssid == NULL) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
"id=%d", id);
return -1;
}
value = wpa_config_get_no_key(ssid, name);
if (value == NULL) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+ wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
"variable '%s'", name);
return -1;
}
@@ -3094,7 +3071,8 @@
static int wpa_supplicant_ctrl_iface_dup_network(
- struct wpa_supplicant *wpa_s, char *cmd)
+ struct wpa_supplicant *wpa_s, char *cmd,
+ struct wpa_supplicant *dst_wpa_s)
{
struct wpa_ssid *ssid_s, *ssid_d;
char *name, *id, *value;
@@ -3113,8 +3091,10 @@
id_s = atoi(cmd);
id_d = atoi(id);
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
- id_s, id_d, name);
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
+ wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
if (ssid_s == NULL) {
@@ -3123,7 +3103,7 @@
return -1;
}
- ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+ ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
if (ssid_d == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
"network id=%d", id_d);
@@ -3137,7 +3117,7 @@
return -1;
}
- ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+ ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
value);
os_free(value);
@@ -3948,6 +3928,15 @@
}
#endif /* CONFIG_EPR */
+#ifdef CONFIG_FIPS
+ if (os_strcmp(field, "fips") == 0) {
+ res = os_snprintf(buf, buflen, "FIPS");
+ if (os_snprintf_error(buflen, res))
+ return -1;
+ return res;
+ }
+#endif /* CONFIG_FIPS */
+
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
@@ -4197,9 +4186,10 @@
if (mask & WPA_BSS_MASK_WPS_SCAN) {
ie = (const u8 *) (bss + 1);
ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
- if (ret < 0 || ret >= end - pos)
+ if (ret >= end - pos)
return 0;
- pos += ret;
+ if (ret > 0)
+ pos += ret;
}
#endif /* CONFIG_WPS */
@@ -4243,6 +4233,8 @@
#ifdef CONFIG_INTERWORKING
if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
struct wpa_bss_anqp *anqp = bss->anqp;
+ struct wpa_bss_anqp_elem *elem;
+
pos = anqp_add_hex(pos, end, "anqp_capability_list",
anqp->capability_list);
pos = anqp_add_hex(pos, end, "anqp_venue_name",
@@ -4272,6 +4264,15 @@
pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
anqp->hs20_osu_providers_list);
#endif /* CONFIG_HS20 */
+
+ dl_list_for_each(elem, &anqp->anqp_elems,
+ struct wpa_bss_anqp_elem, list) {
+ char title[20];
+
+ os_snprintf(title, sizeof(title), "anqp[%u]",
+ elem->infoid);
+ pos = anqp_add_hex(pos, end, title, elem->payload);
+ }
}
#endif /* CONFIG_INTERWORKING */
@@ -4300,6 +4301,15 @@
pos += ret;
}
+#ifdef CONFIG_FST
+ if (mask & WPA_BSS_MASK_FST) {
+ ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
+ if (ret < 0 || ret >= end - pos)
+ return 0;
+ pos += ret;
+ }
+#endif /* CONFIG_FST */
+
if (mask & WPA_BSS_MASK_DELIM) {
ret = os_snprintf(pos, end - pos, "====\n");
if (os_snprintf_error(end - pos, ret))
@@ -4647,6 +4657,48 @@
}
+static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
+{
+ const char *last = NULL;
+ const char *token;
+ long int token_len;
+ unsigned int i;
+
+ /* Expected predefined CPT names delimited by ':' */
+ for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
+ if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
+ wpa_printf(MSG_ERROR,
+ "P2PS: CPT name list is too long, expected up to %d names",
+ P2PS_FEATURE_CAPAB_CPT_MAX);
+ cpt[0] = 0;
+ return -1;
+ }
+
+ token_len = last - token;
+
+ if (token_len == 3 &&
+ os_memcmp(token, "UDP", token_len) == 0) {
+ cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ } else if (token_len == 3 &&
+ os_memcmp(token, "MAC", token_len) == 0) {
+ cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "P2PS: Unsupported CPT name '%s'", token);
+ cpt[0] = 0;
+ return -1;
+ }
+
+ if (isblank(*last)) {
+ i++;
+ break;
+ }
+ }
+ cpt[i] = 0;
+ return 0;
+}
+
+
static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
{
struct p2ps_provision *p2ps_prov;
@@ -4655,6 +4707,7 @@
char *info = NULL;
u8 role = P2PS_SETUP_NONE;
long long unsigned val;
+ int i;
pos = os_strstr(cmd, "info=");
if (pos) {
@@ -4713,6 +4766,18 @@
if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
goto invalid_args;
+ pos = os_strstr(cmd, "cpt=");
+ if (pos) {
+ if (p2ps_ctrl_parse_cpt_priority(pos + 4,
+ p2ps_prov->cpt_priority))
+ goto invalid_args;
+ } else {
+ p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ }
+
+ for (i = 0; p2ps_prov->cpt_priority[i]; i++)
+ p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
+
/* force conncap with tstCap (no sanity checks) */
pos = os_strstr(cmd, "tstCap=");
if (pos) {
@@ -4789,11 +4854,37 @@
if (!p2ps_prov)
return -1;
+ p2ps_prov->pd_seeker = 1;
+
return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
p2ps_prov);
}
+static int parse_freq(int chwidth, int freq2)
+{
+ if (freq2 < 0)
+ return -1;
+ if (freq2)
+ return VHT_CHANWIDTH_80P80MHZ;
+
+ switch (chwidth) {
+ case 0:
+ case 20:
+ case 40:
+ return VHT_CHANWIDTH_USE_HT;
+ case 80:
+ return VHT_CHANWIDTH_80MHZ;
+ case 160:
+ return VHT_CHANWIDTH_160MHZ;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
+ chwidth);
+ return -1;
+ }
+}
+
+
static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
@@ -4810,7 +4901,7 @@
int go_intent = -1;
int freq = 0;
int pd;
- int ht40, vht;
+ int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
if (!wpa_s->global->p2p_init_wpa_s)
return -1;
@@ -4871,6 +4962,18 @@
return -1;
}
+ pos2 = os_strstr(pos, " freq2=");
+ if (pos2)
+ freq2 = atoi(pos2 + 7);
+
+ pos2 = os_strstr(pos, " max_oper_chwidth=");
+ if (pos2)
+ chwidth = atoi(pos2 + 18);
+
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
if (os_strncmp(pos, "pin", 3) == 0) {
/* Request random PIN (to be displayed) and enable the PIN */
wps_method = WPS_PIN_DISPLAY;
@@ -4895,8 +4998,8 @@
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, automatic, join,
- auth, go_intent, freq, persistent_id, pd,
- ht40, vht);
+ auth, go_intent, freq, freq2, persistent_id,
+ pd, ht40, vht, max_oper_chwidth);
if (new_pin == -2) {
os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
return 25;
@@ -5208,6 +5311,8 @@
char *adv_str;
u32 auto_accept, adv_id, svc_state, config_methods;
char *svc_info = NULL;
+ char *cpt_prio_str;
+ u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
pos = os_strchr(cmd, ' ');
if (pos == NULL)
@@ -5280,6 +5385,19 @@
if (pos != NULL)
*pos++ = '\0';
+ cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
+ if (cpt_prio_str) {
+ pos = os_strchr(pos, ' ');
+ if (pos != NULL)
+ *pos++ = '\0';
+
+ if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
+ return -1;
+ } else {
+ cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+ cpt_prio[1] = 0;
+ }
+
/* Service and Response Information are optional */
if (pos && pos[0]) {
size_t len;
@@ -5297,7 +5415,7 @@
return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
(u8) svc_state, (u16) config_methods,
- svc_info);
+ svc_info, cpt_prio);
}
@@ -5436,7 +5554,7 @@
struct wpa_ssid *ssid;
u8 *_peer = NULL, peer[ETH_ALEN];
int freq = 0, pref_freq = 0;
- int ht40, vht;
+ int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
id = atoi(cmd);
pos = os_strstr(cmd, " peer=");
@@ -5474,8 +5592,20 @@
ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
vht;
- return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
- pref_freq);
+ pos = os_strstr(cmd, "freq2=");
+ if (pos)
+ freq2 = atoi(pos + 6);
+
+ pos = os_strstr(cmd, " max_oper_chwidth=");
+ if (pos)
+ chwidth = atoi(pos + 18);
+
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
+ return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
+ max_oper_chwidth, pref_freq);
}
@@ -5522,7 +5652,8 @@
static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
- int id, int freq, int ht40, int vht)
+ int id, int freq, int vht_center_freq2,
+ int ht40, int vht, int vht_chwidth)
{
struct wpa_ssid *ssid;
@@ -5534,8 +5665,9 @@
return -1;
}
- return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
- NULL, 0);
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
+ vht_center_freq2, 0, ht40, vht,
+ vht_chwidth, NULL, 0, 0);
}
@@ -5544,11 +5676,14 @@
int freq = 0, persistent = 0, group_id = -1;
int vht = wpa_s->conf->p2p_go_vht;
int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+ int max_oper_chwidth, chwidth = 0, freq2 = 0;
char *token, *context = NULL;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "freq=%d", &freq) == 1 ||
- sscanf(token, "persistent=%d", &group_id) == 1) {
+ sscanf(token, "freq2=%d", &freq2) == 1 ||
+ sscanf(token, "persistent=%d", &group_id) == 1 ||
+ sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
continue;
} else if (os_strcmp(token, "ht40") == 0) {
ht40 = 1;
@@ -5565,11 +5700,17 @@
}
}
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
if (group_id >= 0)
return p2p_ctrl_group_add_persistent(wpa_s, group_id,
- freq, ht40, vht);
+ freq, freq2, ht40, vht,
+ max_oper_chwidth);
- return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+ return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
+ max_oper_chwidth);
}
@@ -5699,7 +5840,7 @@
freq->min, freq->max);
}
- wpas_p2p_update_channel_list(wpa_s);
+ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
return 0;
}
@@ -5916,6 +6057,7 @@
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
wpa_s->force_long_sd = 0;
wpas_p2p_stop_find(wpa_s);
+ wpa_s->parent->p2ps_method_config_any = 0;
if (wpa_s->global->p2p)
p2p_flush(wpa_s->global->p2p);
}
@@ -6562,6 +6704,53 @@
}
+static int wpas_ctrl_iface_get_pref_freq_list(
+ struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+ unsigned int freq_list[100], num = 100, i;
+ int ret;
+ enum wpa_driver_if_type iface_type;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ /* buf: "<interface_type>" */
+ if (os_strcmp(cmd, "STATION") == 0)
+ iface_type = WPA_IF_STATION;
+ else if (os_strcmp(cmd, "AP") == 0)
+ iface_type = WPA_IF_AP_BSS;
+ else if (os_strcmp(cmd, "P2P_GO") == 0)
+ iface_type = WPA_IF_P2P_GO;
+ else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+ iface_type = WPA_IF_P2P_CLIENT;
+ else if (os_strcmp(cmd, "IBSS") == 0)
+ iface_type = WPA_IF_IBSS;
+ else if (os_strcmp(cmd, "TDLS") == 0)
+ iface_type = WPA_IF_TDLS;
+ else
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+ iface_type, buf);
+
+ ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+ if (ret)
+ return -1;
+
+ for (i = 0; i < num; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i > 0 ? "," : "", freq_list[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen)
{
@@ -6673,6 +6862,8 @@
wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+ wpas_abort_ongoing_scan(wpa_s);
+
#ifdef CONFIG_P2P
wpas_p2p_cancel(p2p_wpa_s);
p2p_ctrl_flush(p2p_wpa_s);
@@ -6684,7 +6875,9 @@
p2p_wpa_s->p2p_disable_ip_addr_req = 0;
os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+ p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
p2p_wpa_s->global->pending_p2ps_group = 0;
+ p2p_wpa_s->global->pending_p2ps_group_freq = 0;
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS_TESTING
@@ -6748,7 +6941,9 @@
wpa_s->next_ssid = NULL;
#ifdef CONFIG_INTERWORKING
+#ifdef CONFIG_HS20
hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_HS20 */
#endif /* CONFIG_INTERWORKING */
wpa_s->ext_mgmt_frame_handling = 0;
@@ -6756,6 +6951,7 @@
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->extra_roc_dur = 0;
wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+ wpa_s->p2p_go_csa_on_inv = 0;
#endif /* CONFIG_TESTING_OPTIONS */
wpa_s->disconnected = 0;
@@ -6773,6 +6969,7 @@
}
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+ wpa_s->wnmsleep_used = 0;
}
@@ -7381,7 +7578,7 @@
{
struct wpa_supplicant *wpa_s = ctx;
const struct ether_header *eth;
- const struct iphdr *ip;
+ struct iphdr ip;
const u8 *pos;
unsigned int i;
@@ -7389,14 +7586,14 @@
return;
eth = (const struct ether_header *) buf;
- ip = (const struct iphdr *) (eth + 1);
- pos = (const u8 *) (ip + 1);
+ os_memcpy(&ip, eth + 1, sizeof(ip));
+ pos = &buf[sizeof(*eth) + sizeof(ip)];
- if (ip->ihl != 5 || ip->version != 4 ||
- ntohs(ip->tot_len) != HWSIM_IP_LEN)
+ 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++) {
+ for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
if (*pos != (u8) i)
return;
pos++;
@@ -7443,7 +7640,7 @@
int used;
long int val;
u8 tos;
- u8 buf[HWSIM_PACKETLEN];
+ u8 buf[2 + HWSIM_PACKETLEN];
struct ether_header *eth;
struct iphdr *ip;
u8 *dpos;
@@ -7471,7 +7668,7 @@
return -1;
tos = val;
- eth = (struct ether_header *) buf;
+ eth = (struct ether_header *) &buf[2];
os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
os_memcpy(eth->ether_shost, src, ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_IP);
@@ -7483,14 +7680,14 @@
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->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+ ip->daddr = htonl(192U << 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,
+ if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
HWSIM_PACKETLEN) < 0)
return -1;
@@ -7580,6 +7777,44 @@
}
+static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+ char *pos;
+
+ wpa_trace_test_fail_after = atoi(cmd);
+ pos = os_strchr(cmd, ':');
+ if (pos) {
+ pos++;
+ os_strlcpy(wpa_trace_test_fail_func, pos,
+ sizeof(wpa_trace_test_fail_func));
+ } else {
+ wpa_trace_test_fail_after = 0;
+ }
+ return 0;
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
+ char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+ extern char wpa_trace_test_fail_func[256];
+ extern unsigned int wpa_trace_test_fail_after;
+
+ return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+ wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+ return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -7655,6 +7890,7 @@
case VENDOR_ELEM_P2P_INV_REQ:
case VENDOR_ELEM_P2P_INV_RESP:
case VENDOR_ELEM_P2P_ASSOC_REQ:
+ case VENDOR_ELEM_P2P_ASSOC_RESP:
return wpa_s->parent;
#endif /* CONFIG_P2P */
default:
@@ -7983,6 +8219,19 @@
}
+static int wpas_ctrl_cmd_debug_level(const char *cmd)
+{
+ if (os_strcmp(cmd, "PING") == 0 ||
+ os_strncmp(cmd, "BSS ", 4) == 0 ||
+ os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
+ os_strncmp(cmd, "STATUS", 6) == 0 ||
+ os_strncmp(cmd, "STA ", 4) == 0 ||
+ os_strncmp(cmd, "STA-", 4) == 0)
+ return MSG_EXCESSIVE;
+ return MSG_DEBUG;
+}
+
+
char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
char *buf, size_t *resp_len)
{
@@ -8006,9 +8255,7 @@
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf));
} else {
- int level = MSG_DEBUG;
- if (os_strcmp(buf, "PING") == 0)
- level = MSG_EXCESSIVE;
+ int level = wpas_ctrl_cmd_debug_level(buf);
wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
}
@@ -8425,6 +8672,9 @@
} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_scan_results(
wpa_s, reply, reply_size);
+ } else if (os_strcmp(buf, "ABORT_SCAN") == 0) {
+ if (wpas_abort_ongoing_scan(wpa_s) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
reply_len = -1;
@@ -8447,7 +8697,8 @@
reply_len = wpa_supplicant_ctrl_iface_get_network(
wpa_s, buf + 12, reply, reply_size);
} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
- if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+ if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
+ wpa_s))
reply_len = -1;
} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
reply_len = wpa_supplicant_ctrl_iface_list_creds(
@@ -8623,6 +8874,11 @@
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+ } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+ if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "GET_FAIL") == 0) {
+ reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
reply_len = -1;
@@ -8644,6 +8900,9 @@
} 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 if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+ reply_len = wpas_ctrl_iface_get_pref_freq_list(
+ wpa_s, buf + 19, reply, reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -8667,10 +8926,11 @@
struct wpa_supplicant *wpa_s;
unsigned int create_iface = 0;
u8 mac_addr[ETH_ALEN];
+ enum wpa_driver_if_type type = WPA_IF_STATION;
/*
* <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
- * TAB<bridge_ifname>[TAB<create>]
+ * TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]]
*/
wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
@@ -8738,9 +8998,22 @@
if (!extra[0])
break;
- if (os_strcmp(extra, "create") == 0)
+ if (os_strcmp(extra, "create") == 0) {
create_iface = 1;
- else {
+ if (!pos)
+ break;
+
+ if (os_strcmp(pos, "sta") == 0) {
+ type = WPA_IF_STATION;
+ } else if (os_strcmp(pos, "ap") == 0) {
+ type = WPA_IF_AP_BSS;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "INTERFACE_ADD unsupported interface type: '%s'",
+ pos);
+ return -1;
+ }
+ } else {
wpa_printf(MSG_DEBUG,
"INTERFACE_ADD unsupported extra parameter: '%s'",
extra);
@@ -8753,7 +9026,7 @@
iface.ifname);
if (!global->ifaces)
return -1;
- if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+ if (wpa_drv_if_add(global->ifaces, type, iface.ifname,
NULL, NULL, NULL, mac_addr, NULL) < 0) {
wpa_printf(MSG_ERROR,
"CTRL_IFACE interface creation failed");
@@ -9045,6 +9318,41 @@
}
+static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
+ char *cmd)
+{
+ struct wpa_supplicant *wpa_s[2]; /* src, dst */
+ char *p;
+ unsigned int i;
+
+ /* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
+ * <variable name> */
+
+ for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
+ p = os_strchr(cmd, ' ');
+ if (p == NULL)
+ return -1;
+ *p = '\0';
+
+ wpa_s[i] = global->ifaces;
+ for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
+ if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
+ break;
+ }
+
+ if (!wpa_s[i]) {
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: Could not find iface=%s", cmd);
+ return -1;
+ }
+
+ cmd = p + 1;
+ }
+
+ return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
+}
+
+
#ifndef CONFIG_NO_CONFIG_WRITE
static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
{
@@ -9126,6 +9434,59 @@
}
+#ifdef CONFIG_FST
+
+static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
+ char *cmd, char *buf,
+ size_t reply_size)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct fst_iface_cfg cfg;
+ struct wpa_supplicant *wpa_s;
+ struct fst_wpa_obj iface_obj;
+
+ if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+ wpa_s = wpa_supplicant_get_iface(global, ifname);
+ if (wpa_s) {
+ if (wpa_s->fst) {
+ wpa_printf(MSG_INFO, "FST: Already attached");
+ return -1;
+ }
+ fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+ wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
+ &iface_obj, &cfg);
+ if (wpa_s->fst)
+ return os_snprintf(buf, reply_size, "OK\n");
+ }
+ }
+
+ return -1;
+}
+
+
+static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
+ char *cmd, char *buf,
+ size_t reply_size)
+{
+ char ifname[IFNAMSIZ + 1];
+ struct wpa_supplicant *wpa_s;
+
+ if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+ wpa_s = wpa_supplicant_get_iface(global, ifname);
+ if (wpa_s) {
+ if (!fst_iface_detach(ifname)) {
+ wpa_s->fst = NULL;
+ return os_snprintf(buf, reply_size, "OK\n");
+ }
+ }
+ }
+
+ return -1;
+}
+
+#endif /* CONFIG_FST */
+
+
char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
char *buf, size_t *resp_len)
{
@@ -9177,6 +9538,18 @@
} else if (os_strcmp(buf, "INTERFACES") == 0) {
reply_len = wpa_supplicant_global_iface_interfaces(
global, reply, reply_size);
+#ifdef CONFIG_FST
+ } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+ reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+ reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
+ reply,
+ reply_size);
+ } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+ reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
} else if (os_strcmp(buf, "TERMINATE") == 0) {
wpa_supplicant_terminate_proc(global);
} else if (os_strcmp(buf, "SUSPEND") == 0) {
@@ -9197,6 +9570,9 @@
#endif /* CONFIG_P2P */
reply_len = -1;
}
+ } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+ if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
+ reply_len = -1;
#ifndef CONFIG_NO_CONFIG_WRITE
} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
if (wpas_global_ctrl_iface_save_config(global))
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 160a6f0..2c71b2d 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -95,7 +95,7 @@
#ifdef __linux__
socklen_t optlen;
int sndbuf, outq;
- int level = MSG_DEBUG;
+ int level = MSG_MSGDUMP;
if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
level = MSG_EXCESSIVE;
@@ -774,6 +774,53 @@
if (wpa_s->conf->ctrl_interface == NULL)
return priv;
+#ifdef ANDROID
+ if (wpa_s->global->params.ctrl_interface) {
+ int same = 0;
+
+ if (wpa_s->global->params.ctrl_interface[0] == '/') {
+ if (os_strcmp(wpa_s->global->params.ctrl_interface,
+ wpa_s->conf->ctrl_interface) == 0)
+ same = 1;
+ } else if (os_strncmp(wpa_s->global->params.ctrl_interface,
+ "@android:", 9) == 0 ||
+ os_strncmp(wpa_s->global->params.ctrl_interface,
+ "@abstract:", 10) == 0) {
+ char *pos;
+
+ /*
+ * Currently, Android uses @android:wpa_* as the naming
+ * convention for the global ctrl interface. This logic
+ * needs to be revisited if the above naming convention
+ * is modified.
+ */
+ pos = os_strchr(wpa_s->global->params.ctrl_interface,
+ '_');
+ if (pos &&
+ os_strcmp(pos + 1,
+ wpa_s->conf->ctrl_interface) == 0)
+ same = 1;
+ }
+
+ if (same) {
+ /*
+ * The invalid configuration combination might be
+ * possible to hit in an Android OTA upgrade case, so
+ * instead of refusing to start the wpa_supplicant
+ * process, do not open the per-interface ctrl_iface
+ * and continue with the global control interface that
+ * was set from the command line since the Wi-Fi
+ * framework will use it for operations.
+ */
+ wpa_printf(MSG_ERROR,
+ "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
+ wpa_s->global->params.ctrl_interface,
+ wpa_s->conf->ctrl_interface);
+ return priv;
+ }
+ }
+#endif /* ANDROID */
+
if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
os_free(priv);
return NULL;
@@ -869,8 +916,10 @@
free_dst:
dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
- list)
+ list) {
+ dl_list_del(&dst->list);
os_free(dst);
+ }
dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
struct ctrl_iface_msg, list) {
dl_list_del(&msg->list);
@@ -958,7 +1007,8 @@
msg.msg_namelen = dst->addrlen;
wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
- wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
+ wpa_printf(MSG_MSGDUMP,
+ "CTRL_IFACE monitor sent successfully to %s",
addr_txt);
dst->errors = 0;
continue;
@@ -1374,8 +1424,10 @@
if (priv->global->params.ctrl_interface)
unlink(priv->global->params.ctrl_interface);
dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
- list)
+ list) {
+ dl_list_del(&dst->list);
os_free(dst);
+ }
dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
struct ctrl_iface_msg, list) {
dl_list_del(&msg->list);
diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h
index a551ccd..95eb4bc 100644
--- a/wpa_supplicant/dbus/dbus_common_i.h
+++ b/wpa_supplicant/dbus/dbus_common_i.h
@@ -13,6 +13,8 @@
#include <dbus/dbus.h>
+struct wpa_dbus_property_desc;
+
struct wpas_dbus_priv {
DBusConnection *con;
int should_dispatch;
@@ -20,9 +22,13 @@
u32 next_objid;
int dbus_new_initialized;
-#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+ struct wpa_dbus_property_desc *all_interface_properties;
+ int globals_start;
+#if defined(CONFIG_AP)
int dbus_noc_refcnt;
-#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
+#endif /* CONFIG_AP */
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
};
#endif /* DBUS_COMMON_I_H */
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index a0c44eb..e4e9b8d 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -205,24 +205,6 @@
/**
- * Add a byte entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- * wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The byte value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
- const char *key, const char value)
-{
- return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE,
- &value);
-}
-
-
-/**
* Add a boolean entry to the dict.
*
* @param iter_dict A valid DBusMessageIter returned from
@@ -317,62 +299,6 @@
/**
- * Add a 64-bit integer entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- * wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The 64-bit integer value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
- const char *key,
- const dbus_int64_t value)
-{
- return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64,
- &value);
-}
-
-
-/**
- * Add a 64-bit unsigned integer entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- * wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The 64-bit unsigned integer value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
- const char *key,
- const dbus_uint64_t value)
-{
- return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
- &value);
-}
-
-
-/**
- * Add a double-precision floating point entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- * wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The double-precision floating point value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
- const char *key, const double value)
-{
- return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE,
- &value);
-}
-
-
-/**
* Add a DBus object path entry to the dict.
*
* @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index b068431..94a0efd 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -26,9 +26,6 @@
dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
const char *key, const char *value);
-dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
- const char *key, const char value);
-
dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
const char *key,
const dbus_bool_t value);
@@ -49,18 +46,6 @@
const char *key,
const dbus_uint32_t value);
-dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
- const char *key,
- const dbus_int64_t value);
-
-dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
- const char *key,
- const dbus_uint64_t value);
-
-dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
- const char *key,
- const double value);
-
dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
const char *key,
const char *value);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 1959ea7..440b8cf 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -633,6 +633,10 @@
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
!wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "config_error",
+ fail->config_error) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "error_indication",
+ fail->error_indication) ||
!wpa_dbus_dict_close_write(&iter, &dict_iter))
wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
else
@@ -1857,6 +1861,99 @@
dbus_message_unref(msg);
}
+
+/**
+ * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @reason: indicates the reason code for group formation failure
+ *
+ * Sends Event dbus signal and string reason code when available.
+ */
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason)
+{
+ DBusMessage *msg;
+ struct wpas_dbus_priv *iface;
+
+ 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,
+ "GroupFormationFailure");
+ if (msg == NULL)
+ return;
+
+ if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID))
+ dbus_connection_send(iface->con, msg, NULL);
+ else
+ wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+ dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: Source address of the Invitation Request
+ * @dev_add: GO Device Address
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @id: Persistent group id or %0 if not persistent group
+ * @op_freq: Operating frequency for the group
+ */
+
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *dev_addr,
+ const u8 *bssid, int id,
+ int op_freq)
+{
+ DBusMessage *msg;
+ DBusMessageIter iter, dict_iter;
+ struct wpas_dbus_priv *iface;
+
+ 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,
+ "InvitationReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+ if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+ (sa &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "sa",
+ (const char *) sa, ETH_ALEN)) ||
+ (dev_addr &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr",
+ (const char *) dev_addr,
+ ETH_ALEN)) ||
+ (bssid &&
+ !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+ (const char *) bssid,
+ ETH_ALEN)) ||
+ (id &&
+ !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) ||
+ !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) ||
+ !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+ dbus_message_unref(msg);
+ return;
+ }
+
+ dbus_connection_send(iface->con, msg, NULL);
+ dbus_message_unref(msg);
+}
+
+
#endif /* CONFIG_P2P */
@@ -2076,41 +2173,54 @@
END_ARGS
}
},
+ { "ExpectDisconnect", WPAS_DBUS_NEW_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_expect_disconnect,
+ {
+ END_ARGS
+ }
+ },
{ NULL, NULL, NULL, { END_ARGS } }
};
static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
{ "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
wpas_dbus_getter_debug_level,
- wpas_dbus_setter_debug_level
+ wpas_dbus_setter_debug_level,
+ NULL
},
{ "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
wpas_dbus_getter_debug_timestamp,
- wpas_dbus_setter_debug_timestamp
+ wpas_dbus_setter_debug_timestamp,
+ NULL
},
{ "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
wpas_dbus_getter_debug_show_keys,
- wpas_dbus_setter_debug_show_keys
+ wpas_dbus_setter_debug_show_keys,
+ NULL
},
{ "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
wpas_dbus_getter_interfaces,
+ NULL,
NULL
},
{ "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
wpas_dbus_getter_eap_methods,
+ NULL,
NULL
},
{ "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
wpas_dbus_getter_global_capabilities,
+ NULL,
NULL
},
#ifdef CONFIG_WIFI_DISPLAY
{ "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay",
wpas_dbus_getter_global_wfd_ies,
- wpas_dbus_setter_global_wfd_ies
+ wpas_dbus_setter_global_wfd_ies,
+ NULL
},
#endif /* CONFIG_WIFI_DISPLAY */
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
@@ -2138,12 +2248,50 @@
};
+static char * uscore_to_dbus(const char *uscore)
+{
+ const char *p = uscore;
+ char *str, *s;
+ dbus_bool_t last_was_uscore = TRUE;
+
+ s = str = os_zalloc(os_strlen(uscore) + 1);
+ if (!str)
+ return NULL;
+ while (p && *p) {
+ if (*p == '_') {
+ last_was_uscore = TRUE;
+ } else {
+ *s++ = last_was_uscore ? toupper(*p) : *p;
+ last_was_uscore = FALSE;
+ }
+ p++;
+ }
+
+ return str;
+}
+
+
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv);
+
+
+static void wpa_dbus_ctrl_iface_props_deinit(struct wpas_dbus_priv *priv)
+{
+ int idx = priv->globals_start;
+
+ /* Free all allocated property values */
+ while (priv->all_interface_properties[idx].dbus_property)
+ os_free((char *)
+ priv->all_interface_properties[idx++].dbus_property);
+ os_free((char *) priv->all_interface_properties);
+}
+
+
/**
* wpas_dbus_ctrl_iface_init - Initialize dbus control interface
* @global: Pointer to global data from wpa_supplicant_init()
* Returns: 0 on success or -1 on failure
*
- * Initialize the dbus control interface for wpa_supplicantand and start
+ * Initialize the dbus control interface for wpa_supplicant and start
* receiving commands from external programs over the bus.
*/
int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
@@ -2151,11 +2299,18 @@
struct wpa_dbus_object_desc *obj_desc;
int ret;
+ ret = wpa_dbus_ctrl_iface_props_init(priv);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "dbus: Not enough memory to init interface properties");
+ return -1;
+ }
+
obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
if (!obj_desc) {
wpa_printf(MSG_ERROR,
"Not enough memory to create object description");
- return -1;
+ goto error;
}
wpas_dbus_register(obj_desc, priv->global, NULL,
@@ -2168,31 +2323,36 @@
ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
WPAS_DBUS_NEW_SERVICE,
obj_desc);
- if (ret < 0)
+ if (ret < 0) {
free_dbus_object_desc(obj_desc);
- else
- priv->dbus_new_initialized = 1;
+ goto error;
+ }
- return ret;
+ priv->dbus_new_initialized = 1;
+ return 0;
+
+error:
+ wpa_dbus_ctrl_iface_props_deinit(priv);
+ return -1;
}
/**
* wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
* wpa_supplicant
- * @iface: Pointer to dbus private data from wpas_dbus_init()
+ * @priv: Pointer to dbus private data from wpas_dbus_init()
*
* Deinitialize the dbus control interface that was initialized with
* wpas_dbus_ctrl_iface_init().
*/
-void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *priv)
{
- if (!iface->dbus_new_initialized)
+ if (!priv->dbus_new_initialized)
return;
wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
WPAS_DBUS_NEW_PATH);
- dbus_connection_unregister_object_path(iface->con,
- WPAS_DBUS_NEW_PATH);
+ dbus_connection_unregister_object_path(priv->con, WPAS_DBUS_NEW_PATH);
+ wpa_dbus_ctrl_iface_props_deinit(priv);
}
@@ -2205,13 +2365,15 @@
static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
{ "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
wpas_dbus_getter_network_properties,
- wpas_dbus_setter_network_properties
+ wpas_dbus_setter_network_properties,
+ NULL
},
{ "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
wpas_dbus_getter_enabled,
- wpas_dbus_setter_enabled
+ wpas_dbus_setter_enabled,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -2350,53 +2512,65 @@
static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
{ "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
wpas_dbus_getter_bss_ssid,
+ NULL,
NULL
},
{ "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
wpas_dbus_getter_bss_bssid,
+ NULL,
NULL
},
{ "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
wpas_dbus_getter_bss_privacy,
+ NULL,
NULL
},
{ "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
wpas_dbus_getter_bss_mode,
+ NULL,
NULL
},
{ "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
wpas_dbus_getter_bss_signal,
+ NULL,
NULL
},
{ "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
wpas_dbus_getter_bss_frequency,
+ NULL,
NULL
},
{ "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
wpas_dbus_getter_bss_rates,
+ NULL,
NULL
},
{ "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
wpas_dbus_getter_bss_wpa,
+ NULL,
NULL
},
{ "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
wpas_dbus_getter_bss_rsn,
+ NULL,
NULL
},
{ "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
wpas_dbus_getter_bss_wps,
+ NULL,
NULL
},
{ "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
wpas_dbus_getter_bss_ies,
+ NULL,
NULL
},
{ "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
wpas_dbus_getter_bss_age,
+ NULL,
NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
@@ -2896,131 +3070,168 @@
}
},
#endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_CONFIG_WRITE
+ { "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE,
+ (WPADBusMethodHandler) wpas_dbus_handler_save_config,
+ {
+ END_ARGS
+ }
+ },
+#endif /* CONFIG_NO_CONFIG_WRITE */
{ NULL, NULL, NULL, { END_ARGS } }
};
static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
{ "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
wpas_dbus_getter_capabilities,
+ NULL,
NULL
},
{ "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_state,
+ NULL,
NULL
},
{ "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
wpas_dbus_getter_scanning,
+ NULL,
NULL
},
{ "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
wpas_dbus_getter_ap_scan,
- wpas_dbus_setter_ap_scan
+ wpas_dbus_setter_ap_scan,
+ NULL
},
{ "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
wpas_dbus_getter_bss_expire_age,
- wpas_dbus_setter_bss_expire_age
+ wpas_dbus_setter_bss_expire_age,
+ NULL
},
{ "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
wpas_dbus_getter_bss_expire_count,
- wpas_dbus_setter_bss_expire_count
+ wpas_dbus_setter_bss_expire_count,
+ NULL
},
{ "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_country,
- wpas_dbus_setter_country
+ wpas_dbus_setter_country,
+ NULL
},
{ "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_ifname,
+ NULL,
NULL
},
{ "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_driver,
+ NULL,
NULL
},
{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_bridge_ifname,
+ NULL,
NULL
},
{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
wpas_dbus_getter_current_bss,
+ NULL,
NULL
},
{ "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
wpas_dbus_getter_current_network,
+ NULL,
NULL
},
{ "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_current_auth_mode,
+ NULL,
NULL
},
{ "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
wpas_dbus_getter_blobs,
+ NULL,
NULL
},
{ "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
wpas_dbus_getter_bsss,
+ NULL,
NULL
},
{ "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
wpas_dbus_getter_networks,
+ NULL,
NULL
},
{ "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
wpas_dbus_getter_fast_reauth,
- wpas_dbus_setter_fast_reauth
+ wpas_dbus_setter_fast_reauth,
+ NULL
},
{ "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
wpas_dbus_getter_scan_interval,
- wpas_dbus_setter_scan_interval
+ wpas_dbus_setter_scan_interval,
+ NULL
},
{ "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_pkcs11_engine_path,
+ NULL,
NULL
},
{ "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
wpas_dbus_getter_pkcs11_module_path,
+ NULL,
NULL
},
#ifdef CONFIG_WPS
{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
wpas_dbus_getter_process_credentials,
- wpas_dbus_setter_process_credentials
+ wpas_dbus_setter_process_credentials,
+ NULL
},
{ "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s",
wpas_dbus_getter_config_methods,
- wpas_dbus_setter_config_methods
+ wpas_dbus_setter_config_methods,
+ NULL
},
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
wpas_dbus_getter_p2p_device_config,
- wpas_dbus_setter_p2p_device_config
+ wpas_dbus_setter_p2p_device_config,
+ NULL
},
{ "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
wpas_dbus_getter_p2p_peers,
+ NULL,
NULL
},
{ "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
wpas_dbus_getter_p2p_role,
+ NULL,
NULL
},
{ "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
wpas_dbus_getter_p2p_group,
+ NULL,
NULL
},
{ "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
wpas_dbus_getter_p2p_peergo,
+ NULL,
NULL
},
{ "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
wpas_dbus_getter_persistent_groups,
+ NULL,
NULL
},
#endif /* CONFIG_P2P */
{ "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
wpas_dbus_getter_disconnect_reason,
+ NULL,
NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
@@ -3172,6 +3383,12 @@
END_ARGS
}
},
+ { "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "reason", "s", ARG_OUT },
+ END_ARGS
+ }
+ },
{ "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
{ "properties", "a{sv}", ARG_OUT },
@@ -3187,7 +3404,7 @@
{ "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
{
{ "path", "o", ARG_OUT },
- { "dev_passwd_id", "i", ARG_OUT },
+ { "dev_passwd_id", "q", ARG_OUT },
{ "device_go_intent", "y", ARG_OUT },
END_ARGS
}
@@ -3236,6 +3453,12 @@
END_ARGS
}
},
+ { "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+ {
+ { "properties", "a{sv}", ARG_OUT },
+ END_ARGS
+ }
+ },
#endif /* CONFIG_P2P */
#ifdef CONFIG_AP
{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
@@ -3282,6 +3505,77 @@
};
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv)
+{
+ size_t all_size;
+ unsigned int i, j, count, num_const, num_globals;
+ const char *global_name;
+ static const char * const ignored_globals[] = {
+ "bss_expiration_age", "bss_expiration_scan_count",
+ "ap_scan", "country", "fast_reauth",
+ "pkcs11_engine_path", "pkcs11_module_path"
+ };
+
+ /* wpas_dbus_interface_properties terminates with a NULL element */
+ num_const = ARRAY_SIZE(wpas_dbus_interface_properties) - 1;
+
+ num_globals = wpa_config_get_num_global_field_names();
+ priv->globals_start = num_const;
+
+ /* allocate enough for all properties + terminating NULL element */
+ all_size = (num_globals + num_const + 1) *
+ sizeof(wpas_dbus_interface_properties[0]);
+ priv->all_interface_properties = os_zalloc(all_size);
+ if (!priv->all_interface_properties) {
+ wpa_printf(MSG_ERROR,
+ "dbus: Not enough memory for interface properties");
+ return -1;
+ }
+
+ /* Copy constant interface properties to the start of the array */
+ os_memcpy(priv->all_interface_properties,
+ wpas_dbus_interface_properties,
+ sizeof(wpas_dbus_interface_properties));
+
+ /* Dynamically construct interface global properties */
+ for (i = 0, count = num_const; i < num_globals; i++) {
+ struct wpa_dbus_property_desc *desc;
+ int no_var = 0;
+
+ /* ignore globals that are actually just methods */
+ global_name = wpa_config_get_global_field_name(i, &no_var);
+ if (no_var)
+ continue;
+ /* Ignore fields already explicitly exposed */
+ for (j = 0; j < ARRAY_SIZE(ignored_globals); j++) {
+ if (os_strcmp(global_name, ignored_globals[j]) == 0)
+ break;
+ }
+ if (j < ARRAY_SIZE(ignored_globals))
+ continue;
+
+ desc = &priv->all_interface_properties[count++];
+ desc->dbus_property = uscore_to_dbus(global_name);
+ if (!desc->dbus_property) {
+ wpa_printf(MSG_ERROR,
+ "dbus: Not enough memory for D-Bus property name");
+ goto error;
+ }
+ desc->dbus_interface = WPAS_DBUS_NEW_IFACE_INTERFACE;
+ desc->type = "s";
+ desc->getter = wpas_dbus_getter_iface_global;
+ desc->setter = wpas_dbus_setter_iface_global;
+ desc->data = global_name;
+ }
+
+ return 0;
+
+error:
+ wpa_dbus_ctrl_iface_props_deinit(priv);
+ return -1;
+}
+
+
/**
* wpas_dbus_register_interface - Register an interface with D-Bus
* @wpa_s: wpa_supplicant interface structure
@@ -3289,7 +3583,6 @@
*/
int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
{
-
struct wpa_dbus_object_desc *obj_desc = NULL;
struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
int next;
@@ -3315,7 +3608,7 @@
}
wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
- wpas_dbus_interface_properties,
+ ctrl_iface->all_interface_properties,
wpas_dbus_interface_signals);
wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
@@ -3381,65 +3674,80 @@
static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
{ "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
wpas_dbus_getter_p2p_peer_device_name,
+ NULL,
NULL
},
{ "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
wpas_dbus_getter_p2p_peer_manufacturer,
+ NULL,
NULL
},
{ "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
wpas_dbus_getter_p2p_peer_modelname,
+ NULL,
NULL
},
{ "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
wpas_dbus_getter_p2p_peer_modelnumber,
+ NULL,
NULL
},
{ "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
wpas_dbus_getter_p2p_peer_serialnumber,
+ NULL,
NULL
},
{ "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
wpas_dbus_getter_p2p_peer_primary_device_type,
+ NULL,
NULL
},
{ "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
wpas_dbus_getter_p2p_peer_config_method,
+ NULL,
NULL
},
{ "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
wpas_dbus_getter_p2p_peer_level,
+ NULL,
NULL
},
{ "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
wpas_dbus_getter_p2p_peer_device_capability,
+ NULL,
NULL
},
{ "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
wpas_dbus_getter_p2p_peer_group_capability,
+ NULL,
NULL
},
{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
wpas_dbus_getter_p2p_peer_secondary_device_types,
+ NULL,
NULL
},
{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
wpas_dbus_getter_p2p_peer_vendor_extension,
+ NULL,
NULL
},
{ "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
wpas_dbus_getter_p2p_peer_ies,
+ NULL,
NULL
},
{ "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
wpas_dbus_getter_p2p_peer_device_address,
+ NULL,
NULL
},
{ "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
wpas_dbus_getter_p2p_peer_groups,
+ NULL,
NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
@@ -3697,41 +4005,50 @@
static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
{ "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
wpas_dbus_getter_p2p_group_members,
+ NULL,
NULL
},
{ "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
wpas_dbus_getter_p2p_group,
+ NULL,
NULL
},
{ "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
wpas_dbus_getter_p2p_role,
+ NULL,
NULL
},
{ "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
wpas_dbus_getter_p2p_group_ssid,
+ NULL,
NULL
},
{ "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
wpas_dbus_getter_p2p_group_bssid,
+ NULL,
NULL
},
{ "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
wpas_dbus_getter_p2p_group_frequency,
+ NULL,
NULL
},
{ "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
wpas_dbus_getter_p2p_group_passphrase,
+ NULL,
NULL
},
{ "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
wpas_dbus_getter_p2p_group_psk,
+ NULL,
NULL
},
{ "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
wpas_dbus_getter_p2p_group_vendor_ext,
- wpas_dbus_setter_p2p_group_vendor_ext
+ wpas_dbus_setter_p2p_group_vendor_ext,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
@@ -3858,9 +4175,10 @@
wpas_dbus_persistent_group_properties[] = {
{ "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
wpas_dbus_getter_persistent_group_properties,
- wpas_dbus_setter_persistent_group_properties
+ wpas_dbus_setter_persistent_group_properties,
+ NULL
},
- { NULL, NULL, NULL, NULL, NULL }
+ { NULL, NULL, NULL, NULL, NULL, NULL }
};
/* No signals intended for persistent group objects */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 7503348..6d240ff 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -191,6 +191,8 @@
void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
int client, int network_id);
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason);
void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
@@ -231,6 +233,10 @@
const u8 *sta);
void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
const u8 *sta);
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *dev_addr,
+ const u8 *bssid, int id,
+ int op_freq);
#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
@@ -400,6 +406,12 @@
}
static inline void
+wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason)
+{
+}
+
+static inline void
wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
@@ -532,6 +544,14 @@
{
}
+static inline
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *dev_addr,
+ const u8 *bssid, int id,
+ int op_freq)
+{
+}
+
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 083dca6..fbfcdc9 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -435,7 +435,8 @@
for (i = 0; i < array_len; i++) {
if (!dbus_message_iter_append_basic(&array_iter, type,
- (char *)array + i * element_size)) {
+ (const char *) array +
+ i * element_size)) {
dbus_set_error(error, DBUS_ERROR_FAILED,
"%s: failed to construct message 2.5",
__func__);
@@ -711,9 +712,9 @@
*
* Getter for "DebugLevel" property.
*/
-dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_level(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
const char *str;
int idx = wpa_debug_level;
@@ -737,9 +738,9 @@
*
* Getter for "DebugTimestamp" property.
*/
-dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
&wpa_debug_timestamp, error);
@@ -756,9 +757,9 @@
*
* Getter for "DebugShowKeys" property.
*/
-dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
&wpa_debug_show_keys, error);
@@ -774,8 +775,9 @@
*
* Setter for "DebugLevel" property.
*/
-dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_level(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
const char *str = NULL;
@@ -812,9 +814,9 @@
*
* Setter for "DebugTimestamp" property.
*/
-dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
dbus_bool_t val;
@@ -838,9 +840,9 @@
*
* Setter for "DebugShowKeys" property.
*/
-dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
dbus_bool_t val;
@@ -867,9 +869,9 @@
* by dbus clients to return list of registered interfaces objects
* paths
*/
-dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_interfaces(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
struct wpa_supplicant *wpa_s;
@@ -912,8 +914,9 @@
* Getter for "EapMethods" property. Handles requests
* by dbus clients to return list of strings with supported EAP methods
*/
-dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_eap_methods(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
char **eap_methods;
size_t num_items = 0;
@@ -948,9 +951,9 @@
* return a list of strings with supported capabilities like AP, RSN IBSS,
* and P2P that are determined at compile time.
*/
-dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_global_capabilities(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
size_t num_items = 0;
@@ -1341,6 +1344,7 @@
}
if (params.freqs && params.freqs[0]) {
+ wpa_s->last_scan_req = MANUAL_SCAN_REQ;
if (wpa_supplicant_trigger_scan(wpa_s,
¶ms)) {
reply = wpas_dbus_error_scan_error(
@@ -1367,6 +1371,7 @@
wpa_supplicant_cancel_sched_scan(wpa_s);
}
+ wpa_s->last_scan_req = MANUAL_SCAN_REQ;
if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) {
reply = wpas_dbus_error_scan_error(
message, "Scan request rejected");
@@ -1578,6 +1583,27 @@
/**
+ * wpas_dbus_handler_expect_disconnect - ExpectDisconnect
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: NULL
+ *
+ * Handler function for notifying system there will be a expected disconnect.
+ * This will prevent wpa_supplicant from adding blacklists upon next disconnect..
+ */
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+ struct wpa_global *global)
+{
+ struct wpa_supplicant *wpa_s = global->ifaces;
+
+ for (; wpa_s; wpa_s = wpa_s->next)
+ if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+ wpa_s->own_disconnect_req = 1;
+ return NULL;
+}
+
+
+/**
* wpas_dbus_handler_reattach - Reattach to current AP
* @message: Pointer to incoming dbus message
* @wpa_s: wpa_supplicant structure for a network interface
@@ -1852,7 +1878,7 @@
os_free(iface);
return reply;
#else /* IEEE8021X_EAPOL */
- wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+ wpa_printf(MSG_DEBUG, "dbus: 802.1X not included");
return wpas_dbus_error_unknown_error(message, "802.1X not included");
#endif /* IEEE8021X_EAPOL */
}
@@ -2269,6 +2295,35 @@
#endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_CONFIG_WRITE
+/**
+ * wpas_dbus_handler_save_config - Save configuration to configuration file
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on Success, Otherwise errror message
+ *
+ * Handler function for "SaveConfig" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+ struct wpa_supplicant *wpa_s)
+{
+ int ret;
+
+ if (!wpa_s->conf->update_config) {
+ return wpas_dbus_error_unknown_error(
+ message,
+ "Not allowed to update configuration (update_config=0)");
+ }
+
+ ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+ if (ret)
+ return wpas_dbus_error_unknown_error(
+ message, "Failed to update configuration");
+ return NULL;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
/**
* wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
* @message: Pointer to incoming dbus message
@@ -2336,8 +2391,9 @@
*
* Getter for "Capabilities" property of an interface.
*/
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_capabilities(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct wpa_driver_capa capa;
@@ -2627,8 +2683,9 @@
*
* Getter for "State" property.
*/
-dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_state(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *str_state;
@@ -2667,8 +2724,9 @@
*
* Getter for "scanning" property.
*/
-dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_scanning(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
@@ -2687,8 +2745,9 @@
*
* Getter function for "ApScan" property.
*/
-dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_ap_scan(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
@@ -2707,8 +2766,9 @@
*
* Setter function for "ApScan" property.
*/
-dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_ap_scan(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t ap_scan;
@@ -2736,9 +2796,9 @@
*
* Getter function for "FastReauth" property.
*/
-dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_fast_reauth(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
@@ -2758,9 +2818,9 @@
*
* Setter function for "FastReauth" property.
*/
-dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_fast_reauth(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t fast_reauth;
@@ -2784,9 +2844,9 @@
* Getter for "DisconnectReason" property. The reason is negative if it is
* locally generated.
*/
-dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_disconnect_reason(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_int32_t reason = wpa_s->disconnect_reason;
@@ -2805,9 +2865,9 @@
*
* Getter function for "BSSExpireAge" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_age(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
@@ -2826,9 +2886,9 @@
*
* Setter function for "BSSExpireAge" property.
*/
-dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_age(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t expire_age;
@@ -2855,9 +2915,9 @@
*
* Getter function for "BSSExpireCount" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_count(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
@@ -2876,9 +2936,9 @@
*
* Setter function for "BSSExpireCount" property.
*/
-dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_count(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_uint32_t expire_count;
@@ -2905,8 +2965,9 @@
*
* Getter function for "Country" property.
*/
-dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_country(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char country[3];
@@ -2930,8 +2991,9 @@
*
* Setter function for "Country" property.
*/
-dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_country(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *country;
@@ -2968,9 +3030,9 @@
*
* Getter function for "ScanInterval" property.
*/
-dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_scan_interval(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_int32_t scan_interval = wpa_s->scan_interval;
@@ -2989,9 +3051,9 @@
*
* Setter function for "ScanInterval" property.
*/
-dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_scan_interval(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_int32_t scan_interval;
@@ -3018,8 +3080,9 @@
*
* Getter for "Ifname" property.
*/
-dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_ifname(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *ifname = wpa_s->ifname;
@@ -3038,8 +3101,9 @@
*
* Getter for "Driver" property.
*/
-dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_driver(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *driver;
@@ -3067,9 +3131,9 @@
*
* Getter for "CurrentBSS" property.
*/
-dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_current_bss(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
@@ -3095,9 +3159,9 @@
*
* Getter for "CurrentNetwork" property.
*/
-dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_current_network(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
@@ -3123,9 +3187,9 @@
*
* Getter for "CurrentAuthMode" property.
*/
-dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_current_auth_mode(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *eap_mode;
@@ -3160,9 +3224,9 @@
*
* Getter for "BridgeIfname" property.
*/
-dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bridge_ifname(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *bridge_ifname = wpa_s->bridge_ifname;
@@ -3181,8 +3245,9 @@
*
* Getter for "BSSs" property.
*/
-dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bsss(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct wpa_bss *bss;
@@ -3238,8 +3303,9 @@
*
* Getter for "Networks" property.
*/
-dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_networks(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct wpa_ssid *ssid;
@@ -3301,9 +3367,9 @@
*
* Getter for "PKCS11EnginePath" property.
*/
-dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *pkcs11_engine_path;
@@ -3326,9 +3392,9 @@
*
* Getter for "PKCS11ModulePath" property.
*/
-dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
const char *pkcs11_module_path;
@@ -3351,8 +3417,9 @@
*
* Getter for "Blobs" property.
*/
-dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_blobs(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
@@ -3404,6 +3471,79 @@
}
+dbus_bool_t wpas_dbus_getter_iface_global(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ int ret;
+ char buf[250];
+ char *p = buf;
+
+ if (!property_desc->data) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Unhandled interface property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ ret = wpa_config_get_value(property_desc->data, wpa_s->conf, buf,
+ sizeof(buf));
+ if (ret < 0)
+ *p = '\0';
+
+ return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &p,
+ error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_iface_global(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+ struct wpa_supplicant *wpa_s = user_data;
+ const char *new_value = NULL;
+ char buf[250];
+ size_t combined_len;
+ int ret;
+
+ if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+ &new_value))
+ return FALSE;
+
+ combined_len = os_strlen(property_desc->data) + os_strlen(new_value) +
+ 3;
+ if (combined_len >= sizeof(buf)) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Interface property %s value too large",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ if (!new_value[0])
+ new_value = "NULL";
+
+ ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
+ new_value);
+ if (os_snprintf_error(combined_len, ret)) {
+ dbus_set_error(error, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+ "Failed to construct new interface property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ if (wpa_config_process_global(wpa_s->conf, buf, -1)) {
+ dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+ "Failed to set interface property %s",
+ property_desc->dbus_property);
+ return FALSE;
+ }
+
+ wpa_supplicant_update_config(wpa_s);
+ return TRUE;
+}
+
+
static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
DBusError *error, const char *func_name)
{
@@ -3430,8 +3570,9 @@
*
* Getter for "BSSID" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_bssid(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3455,8 +3596,9 @@
*
* Getter for "SSID" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ssid(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3480,8 +3622,9 @@
*
* Getter for "Privacy" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_privacy(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3506,8 +3649,9 @@
*
* Getter for "Mode" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_mode(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3547,8 +3691,9 @@
*
* Getter for "Level" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_signal(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3573,8 +3718,9 @@
*
* Getter for "Frequency" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_frequency(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3605,8 +3751,9 @@
*
* Getter for "Rates" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rates(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3645,9 +3792,9 @@
}
-static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
- struct wpa_ie_data *ie_data,
- DBusError *error)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, struct wpa_ie_data *ie_data, DBusError *error)
{
DBusMessageIter iter_dict, variant_iter;
const char *group;
@@ -3778,8 +3925,9 @@
*
* Getter for "WPA" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wpa(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3798,7 +3946,7 @@
return FALSE;
}
- return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+ return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
}
@@ -3811,8 +3959,9 @@
*
* Getter for "RSN" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rsn(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3831,7 +3980,7 @@
return FALSE;
}
- return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+ return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
}
@@ -3844,8 +3993,9 @@
*
* Getter for "WPS" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wps(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3900,8 +4050,9 @@
*
* Getter for "IEs" property.
*/
-dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ies(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3925,8 +4076,9 @@
*
* Getter for BSS age
*/
-dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_age(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct bss_handler_args *args = user_data;
struct wpa_bss *res;
@@ -3954,8 +4106,9 @@
*
* Getter for "enabled" property of a configured network.
*/
-dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_enabled(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
@@ -3974,8 +4127,9 @@
*
* Setter for "Enabled" property of a configured network.
*/
-dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_enabled(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
struct wpa_supplicant *wpa_s;
@@ -4007,9 +4161,9 @@
*
* Getter for "Properties" property of a configured network.
*/
-dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_network_properties(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
DBusMessageIter variant_iter, dict_iter;
@@ -4069,9 +4223,9 @@
*
* Setter for "Properties" property of a configured network.
*/
-dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_network_properties(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
struct wpa_ssid *ssid = net->ssid;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 50f72ec..86c8359 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -10,6 +10,8 @@
#ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
#define CTRL_IFACE_DBUS_NEW_HANDLERS_H
+#include "dbus_new_helpers.h"
+
struct network_handler_args {
struct wpa_supplicant *wpa_s;
struct wpa_ssid *ssid;
@@ -50,39 +52,20 @@
DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
struct wpa_global *global);
-dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+ struct wpa_global *global);
-dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_getter_interfaces);
+DECLARE_ACCESSOR(wpas_dbus_getter_eap_methods);
+DECLARE_ACCESSOR(wpas_dbus_getter_global_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_iface_global);
+DECLARE_ACCESSOR(wpas_dbus_setter_iface_global);
DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -146,150 +129,50 @@
DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_state);
+DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
+DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_getter_country);
+DECLARE_ACCESSOR(wpas_dbus_setter_country);
+DECLARE_ACCESSOR(wpas_dbus_getter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_setter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_driver);
+DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bsss);
+DECLARE_ACCESSOR(wpas_dbus_getter_networks);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_blobs);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_signal);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rates);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wpa);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_network_properties);
DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -297,20 +180,10 @@
DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
struct wpa_supplicant *wpa_s);
-dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_getter_config_methods);
+DECLARE_ACCESSOR(wpas_dbus_setter_config_methods);
DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
struct wpa_supplicant *wpa_s);
@@ -321,6 +194,9 @@
DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+ struct wpa_supplicant *wpa_s);
+
DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
const char *arg);
DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index e9d60df..a0c5ff7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -364,13 +364,14 @@
goto inv_args;
if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
- NULL, 0)) {
+ 0, 0, NULL, 0, 0)) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
goto out;
}
- } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
+ 0))
goto inv_args;
out:
@@ -582,7 +583,7 @@
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, 0, join, authorize_only,
- go_intent, freq, -1, 0, 0, 0);
+ go_intent, freq, 0, -1, 0, 0, 0, 0);
if (new_pin >= 0) {
char npin[9];
@@ -733,8 +734,8 @@
if (ssid == NULL || ssid->disabled != 2)
goto err;
- if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
- 0) {
+ if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
+ 0) < 0) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
@@ -807,9 +808,9 @@
* P2P Device property accessor methods.
*/
-dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_device_config(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
DBusMessageIter variant_iter, dict_iter;
@@ -916,9 +917,9 @@
}
-dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_device_config(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
DBusMessageIter variant_iter, iter_dict;
@@ -1087,8 +1088,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peers(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct p2p_data *p2p = wpa_s->global->p2p;
@@ -1201,8 +1203,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_role(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char *str;
@@ -1224,8 +1227,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
@@ -1243,8 +1247,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peergo(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
@@ -1271,9 +1276,9 @@
* Peer object properties accessor methods
*/
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1309,9 +1314,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1346,9 +1351,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1383,9 +1388,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1420,9 +1425,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1458,6 +1463,7 @@
dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+ const struct wpa_dbus_property_desc *property_desc,
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
@@ -1483,9 +1489,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1508,9 +1514,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1533,9 +1539,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1558,9 +1564,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1584,6 +1590,7 @@
dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+ const struct wpa_dbus_property_desc *property_desc,
DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
@@ -1649,9 +1656,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
unsigned int i, num = 0;
@@ -1684,8 +1691,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1709,9 +1717,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1774,9 +1782,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct peer_handler_args *peer_args = user_data;
const struct p2p_peer_info *info;
@@ -1842,9 +1850,9 @@
*
* Getter for "PersistentGroups" property.
*/
-dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_groups(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct wpa_ssid *ssid;
@@ -1904,21 +1912,21 @@
*
* Getter for "Properties" property of a persistent group.
*/
-dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
/* Leveraging the fact that persistent group object is still
* represented in same manner as network within.
*/
- return wpas_dbus_getter_network_properties(iter, error, net);
+ return wpas_dbus_getter_network_properties(property_desc, iter, error, net);
}
/**
- * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
+ * wpas_dbus_setter_persistent_group_properties - Set options for a persistent
* group
* @iter: Pointer to incoming dbus message iter
* @error: Location to store error on failure
@@ -1927,9 +1935,9 @@
*
* Setter for "Properties" property of a persistent group.
*/
-dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct network_handler_args *net = user_data;
struct wpa_ssid *ssid = net->ssid;
@@ -2142,9 +2150,9 @@
* Group object properties accessor methods
*/
-dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_members(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct wpa_ssid *ssid;
@@ -2211,8 +2219,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
@@ -2224,9 +2233,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
u8 role = wpas_get_p2p_role(wpa_s);
@@ -2248,9 +2257,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
u16 op_freq;
@@ -2271,9 +2280,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char *p_pass;
@@ -2292,8 +2301,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
u8 *p_psk = NULL;
@@ -2313,9 +2323,9 @@
}
-dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
struct hostapd_data *hapd;
@@ -2348,9 +2358,9 @@
}
-dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
DBusMessageIter variant_iter, iter_dict, array_iter, sub;
@@ -2876,8 +2886,9 @@
#ifdef CONFIG_WIFI_DISPLAY
-dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
struct wpabuf *ie;
@@ -2898,8 +2909,9 @@
}
-dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
- DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_global *global = user_data;
DBusMessageIter variant, array;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index 2aecbbe..c4c0261 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -89,139 +89,50 @@
/*
* P2P Device property accessor methods.
*/
-dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo);
/*
* P2P Peer properties.
*/
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
- DBusMessageIter *iter, DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
- DBusMessageIter *iter, DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelname);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_serialnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_primary_device_type);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_config_method);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_group_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_secondary_device_types);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups);
/*
* P2P Group properties
*/
-
-dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_members);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext);
/*
* P2P Persistent Groups and properties
*/
-
-dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
- DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_groups);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_group_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_persistent_group_properties);
DBusMessage * wpas_dbus_handler_add_persistent_group(
DBusMessage *message, struct wpa_supplicant *wpa_s);
@@ -233,15 +144,8 @@
DBusMessage *message, struct wpa_supplicant *wpa_s);
#ifdef CONFIG_WIFI_DISPLAY
-
-dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
-dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
-
+DECLARE_ACCESSOR(wpas_dbus_getter_global_wfd_ies);
+DECLARE_ACCESSOR(wpas_dbus_setter_global_wfd_ies);
#endif /* CONFIG_WIFI_DISPLAY */
#endif /* DBUS_NEW_HANDLERS_P2P_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index b2251ba..1d5dd1c 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -349,9 +349,9 @@
* true if wps_cred_processing configuration field is not equal to 1 or false
* if otherwise.
*/
-dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_process_credentials(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
@@ -371,9 +371,9 @@
* Setter for "ProcessCredentials" property. Sets credentials_processed on 2
* if boolean argument is true or on 1 if otherwise.
*/
-dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_process_credentials(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
dbus_bool_t process_credentials, old_pc;
@@ -407,9 +407,9 @@
* Getter for "ConfigMethods" property. Returned boolean will be true if
* providing the relevant string worked, or false otherwise.
*/
-dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_getter_config_methods(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char *methods = wpa_s->conf->config_methods;
@@ -431,9 +431,9 @@
* Setter for "ConfigMethods" property. Sets the methods string, apply such
* change and returns true on success. Returns false otherwise.
*/
-dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
- DBusError *error,
- void *user_data)
+dbus_bool_t wpas_dbus_setter_config_methods(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data)
{
struct wpa_supplicant *wpa_s = user_data;
char *methods, *new_methods;
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 45623f3..0115e32 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -46,7 +46,7 @@
goto error;
/* An error getting a property fails the request entirely */
- if (!dsc->getter(&entry_iter, error, user_data)) {
+ if (!dsc->getter(dsc, &entry_iter, error, user_data)) {
wpa_printf(MSG_INFO,
"dbus: %s dbus_interface=%s dbus_property=%s getter failed",
__func__, dsc->dbus_interface,
@@ -176,7 +176,7 @@
dbus_message_iter_init_append(reply, &iter);
dbus_error_init(&error);
- if (dsc->getter(&iter, &error, user_data) == FALSE) {
+ if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) {
dbus_message_unref(reply);
reply = wpas_dbus_reply_new_from_error(
message, &error, DBUS_ERROR_FAILED,
@@ -213,7 +213,7 @@
/* Iter will now point to the property's new value */
dbus_error_init(&error);
- if (dsc->setter(&iter, &error, user_data) == TRUE) {
+ if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) {
/* Success */
reply = dbus_message_new_method_return(message);
} else {
@@ -627,7 +627,8 @@
return FALSE;
dbus_error_init(&error);
- if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+ if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data))
+ {
if (dbus_error_is_set(&error)) {
wpa_printf(MSG_ERROR,
"dbus: %s: Cannot get new value of property %s: (%s) %s",
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 6e2c1f1..7b63b28 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -16,9 +16,13 @@
void *user_data);
typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
-typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
- DBusError *error,
- void *user_data);
+struct wpa_dbus_property_desc;
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(
+ const struct wpa_dbus_property_desc *property_desc,
+ DBusMessageIter *iter, DBusError *error, void *user_data);
+#define DECLARE_ACCESSOR(f) \
+dbus_bool_t f(const struct wpa_dbus_property_desc *property_desc, \
+ DBusMessageIter *iter, DBusError *error, void *user_data)
struct wpa_dbus_object_desc {
DBusConnection *connection;
@@ -89,6 +93,8 @@
WPADBusPropertyAccessor getter;
/* property setter function */
WPADBusPropertyAccessor setter;
+ /* other data */
+ const char *data;
};
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index 6209c67..aee105b 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
if (!iface)
return NULL;
iface->dbus_interface = os_strdup(dbus_interface);
- iface->xml = wpabuf_alloc(6000);
+ iface->xml = wpabuf_alloc(15000);
if (iface->dbus_interface == NULL || iface->xml == NULL) {
os_free(iface->dbus_interface);
wpabuf_free(iface->xml);
@@ -257,7 +257,7 @@
DBusMessage *reply;
struct wpabuf *xml;
- xml = wpabuf_alloc(10000);
+ xml = wpabuf_alloc(20000);
if (xml == NULL)
return NULL;
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index 462c713..e8f62ef 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -809,10 +809,10 @@
}
-static const char const *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
+ "bssid", "scan_freq", "freq_list", NULL
};
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 7f627fd..e2e4bdc 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -31,6 +31,9 @@
# Driver interface for Linux drivers using the nl80211 kernel interface
CONFIG_DRIVER_NL80211=y
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
# driver_nl80211.c requires libnl. If you are compiling it yourself
# you may need to point hostapd to your version of libnl.
#
@@ -276,6 +279,12 @@
# none = Empty template
#CONFIG_L2_PACKET=linux
+# Disable Linux packet socket workaround applicable for station interface
+# in a bridge for EAPOL frames. This should be uncommented only if the kernel
+# is known to not have the regression issue in packet socket behavior with
+# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
+#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
+
# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
CONFIG_PEERKEY=y
@@ -495,3 +504,12 @@
#
# External password backend for testing purposes (developer use)
#CONFIG_EXT_PASSWORD_TEST=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
+# OS X builds. This is only for building eapol_test.
+#CONFIG_OSX=y
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d1f9f8b..699fd4f 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -100,12 +100,10 @@
}
static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params,
- u32 interval)
+ struct wpa_driver_scan_params *params)
{
if (wpa_s->driver->sched_scan)
- return wpa_s->driver->sched_scan(wpa_s->drv_priv,
- params, interval);
+ return wpa_s->driver->sched_scan(wpa_s->drv_priv, params);
return -1;
}
@@ -292,7 +290,7 @@
if (wpa_s->driver->send_mlme)
return wpa_s->driver->send_mlme(wpa_s->drv_priv,
data, data_len, noack,
- freq);
+ freq, NULL, 0);
return -1;
}
@@ -401,7 +399,7 @@
if (wpa_s->driver->if_add)
return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
addr, bss_ctx, NULL, force_ifname,
- if_addr, bridge, 0);
+ if_addr, bridge, 0, 0);
return -1;
}
@@ -893,4 +891,30 @@
return wpa_s->driver->set_band(wpa_s->drv_priv, band);
}
+static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list)
+{
+ if (!wpa_s->driver->get_pref_freq_list)
+ return -1;
+ return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+ num, freq_list);
+}
+
+static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ if (!wpa_s->driver->set_prob_oper_freq)
+ return 0;
+ return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
+}
+
+static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->driver->abort_scan)
+ return -1;
+ return wpa_s->driver->abort_scan(wpa_s->drv_priv);
+}
+
#endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index bde7508..7c7d54a 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -14,6 +14,7 @@
#include "common.h"
#include "utils/ext_password.h"
+#include "common/version.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
@@ -76,6 +77,9 @@
const char *pcsc_reader;
const char *pcsc_pin;
+
+ unsigned int ctrl_iface:1;
+ unsigned int id_req_sent:1;
};
static struct eapol_test_data eapol_test;
@@ -329,7 +333,11 @@
static void eapol_test_eapol_done_cb(void *ctx)
{
+ struct eapol_test_data *e = ctx;
+
printf("WPA: EAPOL processing complete\n");
+ wpa_supplicant_cancel_auth_timeout(e->wpa_s);
+ wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED);
}
@@ -407,6 +415,9 @@
{
struct eapol_test_data *e = ctx;
printf("eapol_sm_cb: result=%d\n", result);
+ e->id_req_sent = 0;
+ if (e->ctrl_iface)
+ return;
e->eapol_test_num_reauths--;
if (e->eapol_test_num_reauths < 0)
eloop_terminate();
@@ -552,11 +563,21 @@
}
+static enum wpa_states eapol_test_get_state(void *ctx)
+{
+ struct eapol_test_data *e = ctx;
+ struct wpa_supplicant *wpa_s = e->wpa_s;
+
+ return wpa_s->wpa_state;
+}
+
+
static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct eapol_config eapol_conf;
struct eapol_ctx *ctx;
+ struct wpa_sm_ctx *wctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
@@ -590,6 +611,25 @@
return -1;
}
+ wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ wctx = os_zalloc(sizeof(*wctx));
+ if (wctx == NULL) {
+ os_free(ctx);
+ return -1;
+ }
+ wctx->ctx = e;
+ wctx->msg_ctx = wpa_s;
+ wctx->get_state = eapol_test_get_state;
+ wpa_s->wpa = wpa_sm_init(wctx);
+ if (!wpa_s->wpa) {
+ os_free(ctx);
+ os_free(wctx);
+ return -1;
+ }
+
+ if (!ssid)
+ return 0;
+
wpa_s->current_ssid = ssid;
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
eapol_conf.accept_802_1x_keys = 1;
@@ -614,6 +654,8 @@
{
struct extra_radius_attr *p, *prev;
+ wpa_sm_deinit(wpa_s->wpa);
+ wpa_s->wpa = NULL;
radius_client_deinit(e->radius);
wpabuf_free(e->last_eap_radius);
radius_msg_free(e->last_recv_radius);
@@ -757,6 +799,8 @@
break;
case EAP_CODE_FAILURE:
os_strlcpy(buf, "EAP Failure", sizeof(buf));
+ if (e->ctrl_iface)
+ break;
eloop_terminate();
break;
default:
@@ -901,25 +945,66 @@
if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
e->eapol_test_num_reauths < 0) ||
hdr->code == RADIUS_CODE_ACCESS_REJECT) {
- eloop_terminate();
+ if (!e->ctrl_iface)
+ eloop_terminate();
}
return RADIUS_RX_QUEUED;
}
+static int driver_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+
+static int driver_get_bssid(void *priv, u8 *bssid)
+{
+ struct eapol_test_data *e = priv;
+
+ if (e->ctrl_iface && !e->id_req_sent) {
+ eloop_register_timeout(0, 0, send_eap_request_identity,
+ e->wpa_s, NULL);
+ e->id_req_sent = 1;
+ }
+
+ os_memset(bssid, 0, ETH_ALEN);
+ bssid[5] = 1;
+ return 0;
+}
+
+
+static int driver_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+ os_memset(capa, 0, sizeof(*capa));
+ capa->flags = WPA_DRIVER_FLAGS_WIRED;
+ return 0;
+}
+
+
+struct wpa_driver_ops eapol_test_drv_ops = {
+ .name = "test",
+ .get_ssid = driver_get_ssid,
+ .get_bssid = driver_get_bssid,
+ .get_capa = driver_get_capa,
+};
+
static void wpa_init_conf(struct eapol_test_data *e,
struct wpa_supplicant *wpa_s, const char *authsrv,
int port, const char *secret,
- const char *cli_addr)
+ const char *cli_addr, const char *ifname)
{
struct hostapd_radius_server *as;
int res;
+ wpa_s->driver = &eapol_test_drv_ops;
+ wpa_s->drv_priv = e;
wpa_s->bssid[5] = 1;
os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
- os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname));
+ os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
assert(e->radius_conf != NULL);
@@ -1155,13 +1240,13 @@
static void usage(void)
{
printf("usage:\n"
- "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+ "eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
"[-s<AS secret>]\\\n"
" [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
" [-M<client MAC address>] [-o<server cert file] \\\n"
" [-N<attr spec>] [-R<PC/SC reader>] "
"[-P<PC/SC PIN>] \\\n"
- " [-A<client IP>]\n"
+ " [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
"eapol_test scard\n"
"eapol_test sim <PIN> <num triplets> [debug]\n"
"\n");
@@ -1180,6 +1265,7 @@
" -W = wait for a control interface monitor before starting\n"
" -S = save configuration after authentication\n"
" -n = no MPPE keys expected\n"
+ " -v = show version\n"
" -t<timeout> = sets timeout in seconds (default: 30 s)\n"
" -C<Connect-Info> = RADIUS Connect-Info (default: "
"CONNECT 11Mbps 802.11b)\n"
@@ -1216,6 +1302,8 @@
int timeout = 30;
char *pos;
struct extra_radius_attr *p = NULL, *p1;
+ const char *ifname = "test";
+ const char *ctrl_iface = NULL;
if (os_program_init())
return -1;
@@ -1231,7 +1319,7 @@
wpa_debug_show_keys = 1;
for (;;) {
- c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
+ c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
if (c < 0)
break;
switch (c) {
@@ -1250,6 +1338,9 @@
case 'e':
eapol_test.req_eap_key_name = 1;
break;
+ case 'i':
+ ifname = optarg;
+ break;
case 'M':
if (hwaddr_aton(optarg, eapol_test.own_addr)) {
usage();
@@ -1290,6 +1381,13 @@
case 't':
timeout = atoi(optarg);
break;
+ case 'T':
+ ctrl_iface = optarg;
+ eapol_test.ctrl_iface = 1;
+ break;
+ case 'v':
+ printf("eapol_test v" VERSION_STR "\n");
+ return 0;
case 'W':
wait_for_monitor++;
break;
@@ -1336,7 +1434,7 @@
&argv[optind + 1]);
}
- if (conf == NULL) {
+ if (conf == NULL && !ctrl_iface) {
usage();
printf("Configuration file is required.\n");
return -1;
@@ -1358,12 +1456,15 @@
eapol_test.wpa_s = &wpa_s;
dl_list_init(&wpa_s.bss);
dl_list_init(&wpa_s.bss_id);
- wpa_s.conf = wpa_config_read(conf, NULL);
+ if (conf)
+ wpa_s.conf = wpa_config_read(conf, NULL);
+ else
+ wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", conf);
return -1;
}
- if (wpa_s.conf->ssid == NULL) {
+ if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
printf("No networks defined.\n");
return -1;
}
@@ -1374,7 +1475,7 @@
}
wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
- cli_addr);
+ cli_addr, ifname);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {
printf("Failed to initialize control interface '%s'.\n"
@@ -1387,7 +1488,8 @@
wpa_s.conf->ctrl_interface);
return -1;
}
- if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+ if (wpa_s.conf->ssid &&
+ wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
return -1;
if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
@@ -1399,9 +1501,12 @@
if (wait_for_monitor)
wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
- eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test,
- NULL);
- eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
+ if (!ctrl_iface) {
+ eloop_register_timeout(timeout, 0, eapol_test_timeout,
+ &eapol_test, NULL);
+ eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s,
+ NULL);
+ }
eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
eloop_run();
diff --git a/wpa_supplicant/eapol_test.py b/wpa_supplicant/eapol_test.py
new file mode 100644
index 0000000..80e7dfc
--- /dev/null
+++ b/wpa_supplicant/eapol_test.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python2
+#
+# eapol_test controller
+# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import argparse
+import logging
+import os
+import Queue
+import sys
+import threading
+
+logger = logging.getLogger()
+dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(dir, '..', 'wpaspy'))
+import wpaspy
+wpas_ctrl = '/tmp/eapol_test'
+
+class eapol_test:
+ def __init__(self, ifname):
+ self.ifname = ifname
+ self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+ if "PONG" not in self.ctrl.request("PING"):
+ raise Exception("Failed to connect to eapol_test (%s)" % ifname)
+ self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+ self.mon.attach()
+
+ def add_network(self):
+ id = self.request("ADD_NETWORK")
+ if "FAIL" in id:
+ raise Exception("ADD_NETWORK failed")
+ return int(id)
+
+ def remove_network(self, id):
+ id = self.request("REMOVE_NETWORK " + str(id))
+ if "FAIL" in id:
+ raise Exception("REMOVE_NETWORK failed")
+ return None
+
+ def set_network(self, id, field, value):
+ res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
+ if "FAIL" in res:
+ raise Exception("SET_NETWORK failed")
+ return None
+
+ def set_network_quoted(self, id, field, value):
+ res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+ if "FAIL" in res:
+ raise Exception("SET_NETWORK failed")
+ return None
+
+ def request(self, cmd, timeout=10):
+ return self.ctrl.request(cmd, timeout=timeout)
+
+ def wait_event(self, events, timeout=10):
+ start = os.times()[4]
+ while True:
+ while self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.ifname + ": " + ev)
+ for event in events:
+ if event in ev:
+ return ev
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not self.mon.pending(timeout=remaining):
+ break
+ return None
+
+def run(ifname, count, no_fast_reauth, res):
+ et = eapol_test(ifname)
+
+ et.request("AP_SCAN 0")
+ if no_fast_reauth:
+ et.request("SET fast_reauth 0")
+ else:
+ et.request("SET fast_reauth 1")
+ id = et.add_network()
+ et.set_network(id, "key_mgmt", "IEEE8021X")
+ et.set_network(id, "eapol_flags", "0")
+ et.set_network(id, "eap", "TLS")
+ et.set_network_quoted(id, "identity", "user")
+ et.set_network_quoted(id, "ca_cert", 'ca.pem')
+ et.set_network_quoted(id, "client_cert", 'client.pem')
+ et.set_network_quoted(id, "private_key", 'client.key')
+ et.set_network_quoted(id, "private_key_passwd", 'whatever')
+ et.set_network(id, "disabled", "0")
+
+ fail = False
+ for i in range(count):
+ et.request("REASSOCIATE")
+ ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
+ if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
+ fail = True
+ break
+
+ et.remove_network(id)
+
+ if fail:
+ res.put("FAIL (%d OK)" % i)
+ else:
+ res.put("PASS %d" % (i + 1))
+
+def main():
+ parser = argparse.ArgumentParser(description='eapol_test controller')
+ parser.add_argument('--ctrl', help='control interface directory')
+ parser.add_argument('--num', help='number of processes')
+ parser.add_argument('--iter', help='number of iterations')
+ parser.add_argument('--no-fast-reauth', action='store_true',
+ dest='no_fast_reauth',
+ help='disable TLS session resumption')
+ args = parser.parse_args()
+
+ num = int(args.num)
+ iter = int(args.iter)
+ if args.ctrl:
+ global wpas_ctrl
+ wpas_ctrl = args.ctrl
+
+ t = {}
+ res = {}
+ for i in range(num):
+ res[i] = Queue.Queue()
+ t[i] = threading.Thread(target=run, args=(str(i), iter,
+ args.no_fast_reauth, res[i]))
+ for i in range(num):
+ t[i].start()
+ for i in range(num):
+ t[i].join()
+ try:
+ results = res[i].get(False)
+ except:
+ results = "N/A"
+ print "%d: %s" % (i, results)
+
+if __name__ == "__main__":
+ main()
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6b0c685..6eb3e88 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -23,6 +23,7 @@
#include "eap_peer/eap.h"
#include "ap/hostapd.h"
#include "p2p/p2p.h"
+#include "fst/fst.h"
#include "wnm_sta.h"
#include "notify.h"
#include "common/ieee802_11_defs.h"
@@ -71,6 +72,7 @@
}
+#ifndef CONFIG_NO_SCAN_PROCESSING
/**
* wpas_reenabled_network_time - Time until first network is re-enabled
* @wpa_s: Pointer to wpa_supplicant data
@@ -106,6 +108,7 @@
return res;
}
+#endif /* CONFIG_NO_SCAN_PROCESSING */
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
@@ -286,9 +289,6 @@
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
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;
@@ -305,6 +305,7 @@
wpa_s->key_mgmt = 0;
wpas_rrm_reset(wpa_s);
+ wpa_s->wnmsleep_used = 0;
}
@@ -566,6 +567,13 @@
break;
}
#endif /* CONFIG_IEEE80211W */
+ if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
+ wpas_get_ssid_pmf(wpa_s, ssid) ==
+ NO_MGMT_FRAME_PROTECTION) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip RSN IE - no mgmt frame protection enabled but AP requires it");
+ break;
+ }
wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE");
return 1;
@@ -830,9 +838,9 @@
osen = ie != NULL;
wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
- "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
+ "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
- wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+ wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
@@ -1038,6 +1046,19 @@
*/
#endif /* CONFIG_P2P */
+ if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
+ {
+ struct os_reltime diff;
+
+ os_reltime_sub(&wpa_s->scan_min_time,
+ &bss->last_update, &diff);
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ " skip - scan result not recent enough (%u.%06u seconds too old)",
+ (unsigned int) diff.sec,
+ (unsigned int) diff.usec);
+ continue;
+ }
+
/* Matching configuration found */
return ssid;
}
@@ -1190,6 +1211,7 @@
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
+ wpas_wps_pbc_overlap(wpa_s);
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
return -1;
@@ -1405,6 +1427,8 @@
return -1;
if (!own_request)
return -1;
+ if (data && data->scan_info.external_scan)
+ return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
@@ -1429,7 +1453,7 @@
#endif /* CONFIG_NO_RANDOM_POOL */
if (own_request && wpa_s->scan_res_handler &&
- (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+ !(data && data->scan_info.external_scan)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
@@ -1450,9 +1474,11 @@
}
wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
- wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+ wpa_s->own_scan_running,
+ data ? data->scan_info.external_scan : 0);
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
- wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
+ own_request && !(data && data->scan_info.external_scan)) {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
wpa_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0;
@@ -1463,7 +1489,7 @@
wpas_notify_scan_done(wpa_s, 1);
- if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+ if (data && data->scan_info.external_scan) {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res);
return 0;
@@ -1494,7 +1520,7 @@
wpa_scan_results_free(scan_res);
- if (wpa_s->scan_work) {
+ if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
@@ -1504,7 +1530,7 @@
scan_work_done:
wpa_scan_results_free(scan_res);
- if (wpa_s->scan_work) {
+ if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
@@ -1818,6 +1844,50 @@
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_FST
+static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
+ const u8 *ie, size_t ie_len)
+{
+ struct mb_ies_info mb_ies;
+
+ if (!ie || !ie_len || !wpa_s->fst)
+ return -ENOENT;
+
+ os_memset(&mb_ies, 0, sizeof(mb_ies));
+
+ while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+ size_t len;
+
+ len = 2 + ie[1];
+ if (len > ie_len) {
+ wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found",
+ ie, ie_len);
+ break;
+ }
+
+ if (ie[0] == WLAN_EID_MULTI_BAND) {
+ wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
+ (unsigned int) len);
+ mb_ies.ies[mb_ies.nof_ies].ie = ie + 2;
+ mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2;
+ mb_ies.nof_ies++;
+ }
+
+ ie_len -= len;
+ ie += len;
+ }
+
+ if (mb_ies.nof_ies > 0) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+#endif /* CONFIG_FST */
+
+
static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
@@ -2038,6 +2108,45 @@
}
+static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+#ifdef CONFIG_FST
+ struct assoc_info *ai = data ? &data->assoc_info : NULL;
+ struct wpa_bss *bss = wpa_s->current_bss;
+ const u8 *ieprb, *iebcn;
+
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = NULL;
+
+ if (ai &&
+ !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) {
+ wpa_printf(MSG_DEBUG,
+ "FST: MB IEs updated from Association Response frame");
+ return;
+ }
+
+ if (ai &&
+ !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) {
+ wpa_printf(MSG_DEBUG,
+ "FST: MB IEs updated from association event Beacon IEs");
+ return;
+ }
+
+ if (!bss)
+ return;
+
+ ieprb = (const u8 *) (bss + 1);
+ iebcn = ieprb + bss->ie_len;
+
+ if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len))
+ wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE");
+ else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len))
+ wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE");
+#endif /* CONFIG_FST */
+}
+
+
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
@@ -2102,6 +2211,8 @@
"WPA/RSN IEs not updated");
}
+ wpas_fst_update_mb_assoc(wpa_s, data);
+
#ifdef CONFIG_SME
os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
wpa_s->sme.prev_bssid_set = 1;
@@ -2343,7 +2454,8 @@
if (!wpa_s->disconnected &&
(!wpa_s->auto_reconnect_disabled ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
- wpas_wps_searching(wpa_s))) {
+ wpas_wps_searching(wpa_s) ||
+ wpas_wps_reenable_networks_pending(wpa_s))) {
wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
"reconnect (wps=%d/%d wpa_state=%d)",
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
@@ -2372,7 +2484,8 @@
"try to re-connect");
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
- wpa_supplicant_cancel_sched_scan(wpa_s);
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
}
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
@@ -2993,25 +3106,13 @@
if (wpa_s->drv_priv == NULL)
return; /* Ignore event during drv initialization */
- free_hw_features(wpa_s);
- wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
- wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
- wpas_p2p_update_channel_list(wpa_s);
-
- /*
- * Check other interfaces to see if they share the same radio. If
- * so, they get updated with this same hw mode info.
- */
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
- if (ifs != wpa_s) {
- wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
- ifs->ifname);
- free_hw_features(ifs);
- ifs->hw.modes = wpa_drv_get_hw_feature_data(
- ifs, &ifs->hw.num_modes, &ifs->hw.flags);
- }
+ wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+ ifs->ifname);
+ free_hw_features(ifs);
+ ifs->hw.modes = wpa_drv_get_hw_feature_data(
+ ifs, &ifs->hw.num_modes, &ifs->hw.flags);
}
/* Restart sched_scan with updated channel list */
@@ -3021,6 +3122,8 @@
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
+
+ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
}
@@ -3121,6 +3224,13 @@
return;
}
+#ifdef CONFIG_FST
+ if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
+ fst_rx_action(wpa_s->fst, mgmt, len);
+ return;
+ }
+#endif /* CONFIG_FST */
+
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
category, payload, plen, freq);
if (wpa_s->ifmsh)
@@ -3131,9 +3241,6 @@
static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
union wpa_event_data *event)
{
-#ifdef CONFIG_P2P
- struct wpa_supplicant *ifs;
-#endif /* CONFIG_P2P */
struct wpa_freq_range_list *list;
char *str = NULL;
@@ -3150,29 +3257,13 @@
__func__);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
- wpas_p2p_update_channel_list(wpa_s);
- }
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- int freq;
- if (!ifs->current_ssid ||
- !ifs->current_ssid->p2p_group ||
- (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
- ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
- continue;
-
- freq = ifs->current_ssid->frequency;
- if (!freq_range_list_includes(list, freq)) {
- wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
- freq);
- continue;
- }
-
- wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
- freq);
- /* TODO: Consider using CSA or removing the group within
- * wpa_supplicant */
- wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+ /*
+ * The update channel flow will also take care of moving a GO
+ * from the unsafe frequency if needed.
+ */
+ wpas_p2p_update_channel_list(wpa_s,
+ WPAS_P2P_CHANNEL_UPDATE_AVOID);
}
#endif /* CONFIG_P2P */
@@ -3237,12 +3328,20 @@
switch (event) {
case EVENT_AUTH:
+#ifdef CONFIG_FST
+ wpas_fst_update_mbie(wpa_s, data->auth.ies, data->auth.ies_len);
+#endif /* CONFIG_FST */
sme_event_auth(wpa_s, data);
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);
+ if (data) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
+ data->assoc_info.subnet_status);
+ }
break;
case EVENT_DISASSOC:
wpas_event_disassoc(wpa_s,
@@ -3257,10 +3356,11 @@
break;
#ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_STARTED:
- os_get_reltime(&wpa_s->scan_start_time);
- if (wpa_s->own_scan_requested) {
+ if (wpa_s->own_scan_requested ||
+ (data && !data->scan_info.external_scan)) {
struct os_reltime diff;
+ os_get_reltime(&wpa_s->scan_start_time);
os_reltime_sub(&wpa_s->scan_start_time,
&wpa_s->scan_trigger_time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
@@ -3283,7 +3383,8 @@
}
break;
case EVENT_SCAN_RESULTS:
- if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+ if (!(data && data->scan_info.external_scan) &&
+ os_reltime_initialized(&wpa_s->scan_start_time)) {
struct os_reltime now, diff;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
@@ -3294,8 +3395,10 @@
}
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
- wpa_s->own_scan_running = 0;
- wpa_s->radio->external_scan_running = 0;
+ if (!(data && data->scan_info.external_scan))
+ wpa_s->own_scan_running = 0;
+ if (data && data->scan_info.nl_scan_event)
+ wpa_s->radio->external_scan_running = 0;
radio_work_check_next(wpa_s);
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -3451,20 +3554,25 @@
data->rx_from_unknown.wds);
break;
case EVENT_CH_SWITCH:
- if (!data)
+ if (!data || !wpa_s->current_ssid)
break;
- if (!wpa_s->ap_iface) {
- wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
- "event in non-AP mode");
- break;
+
+ wpa_s->assoc_freq = data->ch_switch.freq;
+ wpa_s->current_ssid->frequency = data->ch_switch.freq;
+
+ if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
+ wpa_s->current_ssid->mode ==
+ WPAS_MODE_P2P_GROUP_FORMATION) {
+ wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+ data->ch_switch.ht_enabled,
+ data->ch_switch.ch_offset,
+ data->ch_switch.ch_width,
+ data->ch_switch.cf1,
+ data->ch_switch.cf2);
}
- wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
- data->ch_switch.ht_enabled,
- data->ch_switch.ch_offset,
- data->ch_switch.ch_width,
- data->ch_switch.cf1,
- data->ch_switch.cf2);
+ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
break;
#ifdef NEED_AP_MLME
case EVENT_DFS_RADAR_DETECTED:
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index a1afc85..e6c564b 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -617,7 +617,7 @@
prov->osu_ssid_len = osu_ssid_len;
/* OSU Friendly Name Length */
- if (pos + 2 > end) {
+ if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
"Friendly Name Length");
return;
@@ -633,9 +633,9 @@
pos += len2;
/* OSU Friendly Name Duples */
- while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+ while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) {
struct osu_lang_string *f;
- if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+ if (1 + pos2[0] > pos - pos2 || pos2[0] < 3) {
wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
break;
}
@@ -646,7 +646,7 @@
}
/* OSU Server URI */
- if (pos + 1 > end) {
+ if (end - pos < 1) {
wpa_printf(MSG_DEBUG,
"HS 2.0: Not enough room for OSU Server URI length");
return;
@@ -661,7 +661,7 @@
pos += uri_len;
/* OSU Method list */
- if (pos + 1 > end) {
+ if (end - pos < 1) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
"list length");
return;
@@ -681,7 +681,7 @@
}
/* Icons Available Length */
- if (pos + 2 > end) {
+ if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
"Available Length");
return;
@@ -701,7 +701,7 @@
struct osu_icon *icon = &prov->icon[prov->icon_count];
u8 flen;
- if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+ if (2 + 2 + 3 + 1 + 1 > pos - pos2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
break;
}
@@ -713,46 +713,46 @@
os_memcpy(icon->lang, pos2, 3);
pos2 += 3;
- flen = pos2[0];
- if (flen > pos - pos2 - 1) {
+ flen = *pos2++;
+ if (flen > pos - pos2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
break;
}
- os_memcpy(icon->icon_type, pos2 + 1, flen);
- pos2 += 1 + flen;
+ os_memcpy(icon->icon_type, pos2, flen);
+ pos2 += flen;
- if (pos2 + 1 > pos) {
+ if (pos - pos2 < 1) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
"Filename length");
break;
}
- flen = pos2[0];
- if (flen > pos - pos2 - 1) {
+ flen = *pos2++;
+ if (flen > pos - pos2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
"Filename");
break;
}
- os_memcpy(icon->filename, pos2 + 1, flen);
- pos2 += 1 + flen;
+ os_memcpy(icon->filename, pos2, flen);
+ pos2 += flen;
prov->icon_count++;
}
/* OSU_NAI */
- if (pos + 1 > end) {
+ if (end - pos < 1) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
return;
}
- osu_nai_len = pos[0];
- if (osu_nai_len > end - pos - 1) {
+ osu_nai_len = *pos++;
+ if (osu_nai_len > end - pos) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
return;
}
- os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
- pos += 1 + osu_nai_len;
+ os_memcpy(prov->osu_nai, pos, osu_nai_len);
+ pos += osu_nai_len;
/* OSU Service Description Length */
- if (pos + 2 > end) {
+ if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
"Service Description Length");
return;
@@ -768,20 +768,20 @@
pos += len2;
/* OSU Service Description Duples */
- while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+ while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) {
struct osu_lang_string *f;
u8 descr_len;
- descr_len = pos2[0];
- if (descr_len > pos - pos2 - 1 || descr_len < 3) {
+ descr_len = *pos2++;
+ if (descr_len > pos - pos2 || descr_len < 3) {
wpa_printf(MSG_DEBUG, "Invalid OSU Service "
"Description");
break;
}
f = &prov->serv_desc[prov->serv_desc_count++];
- os_memcpy(f->lang, pos2 + 1, 3);
- os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
- pos2 += 1 + descr_len;
+ os_memcpy(f->lang, pos2, 3);
+ os_memcpy(f->text, pos2 + 3, descr_len - 3);
+ pos2 += descr_len;
}
wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
@@ -816,9 +816,9 @@
end = pos + wpabuf_len(prov_anqp);
/* OSU SSID */
- if (pos + 1 > end)
+ if (end - pos < 1)
continue;
- if (pos + 1 + pos[0] > end) {
+ if (1 + pos[0] > end - pos) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
"OSU SSID");
continue;
@@ -832,7 +832,7 @@
osu_ssid = pos;
pos += osu_ssid_len;
- if (pos + 1 > end) {
+ if (end - pos < 1) {
wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
"Number of OSU Providers");
continue;
@@ -842,7 +842,7 @@
num_providers);
/* OSU Providers */
- while (pos + 2 < end && num_providers > 0) {
+ while (end - pos > 2 && num_providers > 0) {
num_providers--;
len = WPA_GET_LE16(pos);
pos += 2;
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 6299191..d9d0ae7 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -697,7 +697,8 @@
ibss_rsn_free(prev);
}
- wpa_deinit(ibss_rsn->auth_group);
+ if (ibss_rsn->auth_group)
+ wpa_deinit(ibss_rsn->auth_group);
os_free(ibss_rsn);
}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index fd47c17..6a54d1e 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -362,13 +362,13 @@
u8 elen, auth_count, a;
const u8 *e_end;
- if (pos + 3 > end) {
+ if (end - pos < 3) {
wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
return NULL;
}
elen = *pos++;
- if (pos + elen > end || elen < 2) {
+ if (elen > end - pos || elen < 2) {
wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
return NULL;
}
@@ -381,14 +381,19 @@
for (a = 0; a < auth_count; a++) {
u8 id, len;
- if (pos + 2 > end || pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "No room for Authentication "
- "Parameter subfield");
+ if (end - pos < 2) {
+ wpa_printf(MSG_DEBUG,
+ "No room for Authentication Parameter subfield header");
return NULL;
}
id = *pos++;
len = *pos++;
+ if (len > end - pos) {
+ wpa_printf(MSG_DEBUG,
+ "No room for Authentication Parameter subfield");
+ return NULL;
+ }
switch (id) {
case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
@@ -463,7 +468,7 @@
len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
pos += 2;
- if (pos + len > end || len < 3) {
+ if (len > end - pos || len < 3) {
wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
"(len=%u; left=%u)",
len, (unsigned int) (end - pos));
@@ -473,7 +478,7 @@
r->encoding = *pos++;
realm_len = *pos++;
- if (pos + realm_len > f_end) {
+ if (realm_len > f_end - pos) {
wpa_printf(MSG_DEBUG, "No room for NAI Realm "
"(len=%u; left=%u)",
realm_len, (unsigned int) (f_end - pos));
@@ -485,13 +490,13 @@
return NULL;
pos += realm_len;
- if (pos + 1 > f_end) {
+ if (f_end - pos < 1) {
wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
return NULL;
}
r->eap_count = *pos++;
wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
- if (pos + r->eap_count * 3 > f_end) {
+ if (r->eap_count * 3 > f_end - pos) {
wpa_printf(MSG_DEBUG, "No room for EAP Methods");
return NULL;
}
@@ -746,7 +751,7 @@
return 0;
pos = wpabuf_head_u8(anqp);
end = pos + wpabuf_len(anqp);
- if (pos + 2 > end)
+ if (end - pos < 2)
return 0;
if (*pos != 0) {
wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
@@ -754,7 +759,7 @@
}
pos++;
udhl = *pos++;
- if (pos + udhl > end) {
+ if (udhl > end - pos) {
wpa_printf(MSG_DEBUG, "Invalid UDHL");
return 0;
}
@@ -764,12 +769,12 @@
plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
imsi, mnc_len);
- while (pos + 2 <= end) {
+ while (end - pos >= 2) {
u8 iei, len;
const u8 *l_end;
iei = *pos++;
len = *pos++ & 0x7f;
- if (pos + len > end)
+ if (len > end - pos)
break;
l_end = pos + len;
@@ -780,7 +785,7 @@
pos, len);
num = *pos++;
for (i = 0; i < num; i++) {
- if (pos + 3 > l_end)
+ if (l_end - pos < 3)
break;
if (os_memcmp(pos, plmn, 3) == 0 ||
os_memcmp(pos, plmn2, 3) == 0)
@@ -1082,12 +1087,12 @@
* OI #1, [OI #2], [OI #3]
*/
- if (pos + 2 > end)
+ if (end - pos < 2)
return 0;
pos++; /* skip Number of ANQP OIs */
lens = *pos++;
- if (pos + (lens & 0x0f) + (lens >> 4) > end)
+ if ((lens & 0x0f) + (lens >> 4) > end - pos)
return 0;
if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
@@ -1121,7 +1126,7 @@
/* Set of <OI Length, OI> duples */
while (pos < end) {
len = *pos++;
- if (pos + len > end)
+ if (len > end - pos)
break;
if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
return 1;
@@ -1182,6 +1187,7 @@
static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
struct wpa_cred *cred, struct wpa_bss *bss)
{
+#ifdef CONFIG_HS20
int res;
unsigned int dl_bandwidth, ul_bandwidth;
const u8 *wan;
@@ -1233,6 +1239,7 @@
if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
return 1;
}
+#endif /* CONFIG_HS20 */
return 0;
}
@@ -1260,9 +1267,11 @@
}
+#ifdef CONFIG_HS20
+
static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
{
- while (pos + 4 <= end) {
+ while (end - pos >= 4) {
if (pos[0] == proto && pos[3] == 1 /* Open */)
return 1;
pos += 4;
@@ -1275,7 +1284,7 @@
static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
u16 port)
{
- while (pos + 4 <= end) {
+ while (end - pos >= 4) {
if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
pos[3] == 1 /* Open */)
return 1;
@@ -1285,10 +1294,13 @@
return 0;
}
+#endif /* CONFIG_HS20 */
+
static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
struct wpa_cred *cred, struct wpa_bss *bss)
{
+#ifdef CONFIG_HS20
int res;
const u8 *capab, *end;
unsigned int i, j;
@@ -1325,6 +1337,7 @@
}
}
}
+#endif /* CONFIG_HS20 */
return 0;
}
@@ -2125,23 +2138,27 @@
pos = wpabuf_head(domain_names);
end = pos + wpabuf_len(domain_names);
- while (pos + 1 < end) {
- if (pos + 1 + pos[0] > end)
+ while (end - pos > 1) {
+ u8 elen;
+
+ elen = *pos++;
+ if (elen > end - pos)
break;
wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
- pos + 1, pos[0]);
- if (pos[0] == len &&
- os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
+ pos, elen);
+ if (elen == len &&
+ os_strncasecmp(domain, (const char *) pos, len) == 0)
return 1;
- if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
- const char *ap = (const char *) (pos + 1);
- int offset = pos[0] - len;
+ if (!exact_match && elen > len && pos[elen - len - 1] == '.') {
+ const char *ap = (const char *) pos;
+ int offset = elen - len;
+
if (os_strncasecmp(domain, ap + offset, len) == 0)
return 1;
}
- pos += 1 + pos[0];
+ pos += elen;
}
return 0;
@@ -2564,11 +2581,13 @@
return;
}
+#ifdef CONFIG_HS20
if (wpa_s->fetch_osu_icon_in_progress) {
wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
hs20_next_osu_icon(wpa_s);
return;
}
+#endif /* CONFIG_HS20 */
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -2602,6 +2621,7 @@
}
if (found == 0) {
+#ifdef CONFIG_HS20
if (wpa_s->fetch_osu_info) {
if (wpa_s->num_prov_found == 0 &&
wpa_s->fetch_osu_waiting_scan &&
@@ -2614,6 +2634,7 @@
hs20_osu_icon_fetch(wpa_s);
return;
}
+#endif /* CONFIG_HS20 */
wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
wpa_s->fetch_anqp_in_progress = 0;
if (wpa_s->network_select)
@@ -2716,6 +2737,41 @@
}
+static void anqp_add_extra(struct wpa_supplicant *wpa_s,
+ struct wpa_bss_anqp *anqp, u16 info_id,
+ const u8 *data, size_t slen)
+{
+ struct wpa_bss_anqp_elem *tmp, *elem = NULL;
+
+ if (!anqp)
+ return;
+
+ dl_list_for_each(tmp, &anqp->anqp_elems, struct wpa_bss_anqp_elem,
+ list) {
+ if (tmp->infoid == info_id) {
+ elem = tmp;
+ break;
+ }
+ }
+
+ if (!elem) {
+ elem = os_zalloc(sizeof(*elem));
+ if (!elem)
+ return;
+ elem->infoid = info_id;
+ dl_list_add(&anqp->anqp_elems, &elem->list);
+ } else {
+ wpabuf_free(elem->payload);
+ }
+
+ elem->payload = wpabuf_alloc_copy(data, slen);
+ if (!elem->payload) {
+ dl_list_del(&elem->list);
+ os_free(elem);
+ }
+}
+
+
static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
u16 info_id,
@@ -2849,6 +2905,7 @@
default:
wpa_msg(wpa_s, MSG_DEBUG,
"Interworking: Unsupported ANQP Info ID %u", info_id);
+ anqp_add_extra(wpa_s, anqp, info_id, data, slen);
break;
}
}
@@ -2871,8 +2928,10 @@
" dialog_token=%u result=%d status_code=%u",
MAC2STR(dst), dialog_token, result, status_code);
if (result != GAS_QUERY_SUCCESS) {
+#ifdef CONFIG_HS20
if (wpa_s->fetch_osu_icon_in_progress)
hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
anqp_result = "FAILURE";
goto out;
}
@@ -2882,8 +2941,10 @@
pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
wpa_msg(wpa_s, MSG_DEBUG,
"ANQP: Unexpected Advertisement Protocol in response");
+#ifdef CONFIG_HS20
if (wpa_s->fetch_osu_icon_in_progress)
hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
anqp_result = "INVALID_FRAME";
goto out;
}
@@ -2932,7 +2993,9 @@
}
out_parse_done:
+#ifdef CONFIG_HS20
hs20_notify_parse_done(wpa_s);
+#endif /* CONFIG_HS20 */
out:
wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
MAC2STR(dst), anqp_result);
diff --git a/wpa_supplicant/libwpa_test.c b/wpa_supplicant/libwpa_test.c
new file mode 100644
index 0000000..e51ab72
--- /dev/null
+++ b/wpa_supplicant/libwpa_test.c
@@ -0,0 +1,32 @@
+/*
+ * libwpa_test - Test program for libwpa_client.* library linking
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/wpa_ctrl.h"
+
+int main(int argc, char *argv[])
+{
+ struct wpa_ctrl *ctrl;
+
+ ctrl = wpa_ctrl_open("foo");
+ if (!ctrl)
+ return -1;
+ if (wpa_ctrl_attach(ctrl) == 0)
+ wpa_ctrl_detach(ctrl);
+ if (wpa_ctrl_pending(ctrl)) {
+ char buf[10];
+ size_t len;
+
+ len = sizeof(buf);
+ wpa_ctrl_recv(ctrl, buf, &len);
+ }
+ wpa_ctrl_close(ctrl);
+
+ return 0;
+}
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 1c93306..d5d47e1 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -12,6 +12,7 @@
#endif /* __linux__ */
#include "common.h"
+#include "fst/fst.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "p2p_supplicant.h"
@@ -309,6 +310,17 @@
"wpa_supplicant");
}
+ if (fst_global_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize FST");
+ exitcode = -1;
+ goto out;
+ }
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+ if (!fst_global_add_ctrl(fst_ctrl_cli))
+ wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif
+
for (i = 0; exitcode == 0 && i < iface_count; i++) {
struct wpa_supplicant *wpa_s;
@@ -334,6 +346,8 @@
wpa_supplicant_deinit(global);
+ fst_global_deinit();
+
out:
wpa_supplicant_fd_workaround(0);
os_free(ifaces);
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index ca012e2..8f74b5d 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -47,8 +47,8 @@
if (ifmsh->mconf) {
mesh_mpm_deinit(wpa_s, ifmsh);
- if (ifmsh->mconf->ies) {
- ifmsh->mconf->ies = NULL;
+ if (ifmsh->mconf->rsn_ie) {
+ ifmsh->mconf->rsn_ie = NULL;
/* We cannot free this struct
* because wpa_authenticator on
* hostapd side is also using it
@@ -171,6 +171,8 @@
ifmsh->conf = conf;
ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
+ ifmsh->bss[0]->dot11RSNASAERetransPeriod =
+ wpa_s->conf->dot11RSNASAERetransPeriod;
os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
mconf = mesh_config_create(ssid);
@@ -316,11 +318,22 @@
wpa_supplicant_mesh_deinit(wpa_s);
+ if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+ wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+ wpa_s->group_cipher = WPA_CIPHER_CCMP;
+ wpa_s->mgmt_group_cipher = 0;
+ } else {
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+ wpa_s->group_cipher = WPA_CIPHER_NONE;
+ wpa_s->mgmt_group_cipher = 0;
+ }
+
os_memset(¶ms, 0, sizeof(params));
params.meshid = ssid->ssid;
params.meshid_len = ssid->ssid_len;
ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq);
wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+ wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled;
if (ssid->beacon_int > 0)
params.beacon_int = ssid->beacon_int;
else if (wpa_s->conf->beacon_int > 0)
@@ -350,8 +363,8 @@
}
if (wpa_s->ifmsh) {
- params.ies = wpa_s->ifmsh->mconf->ies;
- params.ie_len = wpa_s->ifmsh->mconf->ie_len;
+ params.ies = wpa_s->ifmsh->mconf->rsn_ie;
+ params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len;
params.basic_rates = wpa_s->ifmsh->basic_rates;
}
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index b29b5ff..7ebd4d2 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -212,9 +212,6 @@
struct hostapd_data *bss = ifmsh->bss[0];
struct mesh_conf *conf = ifmsh->mconf;
u8 supp_rates[2 + 2 + 32];
-#ifdef CONFIG_IEEE80211N
- u8 ht_capa_oper[2 + 26 + 2 + 22];
-#endif /* CONFIG_IEEE80211N */
u8 *pos, *cat;
u8 ie_len, add_plid = 0;
int ret;
@@ -239,6 +236,15 @@
2 + 22; /* HT operation */
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+ buf_len += 2 + 12 + /* VHT Capabilities */
+ 2 + 5; /* VHT Operation */
+ }
+#endif /* CONFIG_IEEE80211AC */
+ if (type != PLINK_CLOSE)
+ buf_len += conf->rsn_ie_len; /* RSN IE */
+
buf = wpabuf_alloc(buf_len);
if (!buf)
return;
@@ -255,13 +261,16 @@
/* aid */
if (type == PLINK_CONFIRM)
- wpabuf_put_le16(buf, sta->peer_lid);
+ wpabuf_put_le16(buf, sta->aid);
/* IE: supp + ext. supp rates */
pos = hostapd_eid_supp_rates(bss, supp_rates);
pos = hostapd_eid_ext_supp_rates(bss, pos);
wpabuf_put_data(buf, supp_rates, pos - supp_rates);
+ /* IE: RSN IE */
+ wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len);
+
/* IE: Mesh ID */
wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
wpabuf_put_u8(buf, conf->meshid_len);
@@ -328,11 +337,22 @@
#ifdef CONFIG_IEEE80211N
if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+ u8 ht_capa_oper[2 + 26 + 2 + 22];
+
pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
pos = hostapd_eid_ht_operation(bss, pos);
wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+ u8 vht_capa_oper[2 + 12 + 2 + 5];
+
+ pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper);
+ pos = hostapd_eid_vht_operation(bss, pos);
+ wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
+ }
+#endif /* CONFIG_IEEE80211AC */
if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
wpa_msg(wpa_s, MSG_INFO,
@@ -542,6 +562,9 @@
return NULL;
}
+ /* Set WMM by default since Mesh STAs are QoS STAs */
+ sta->flags |= WLAN_STA_WMM;
+
/* initialize sta */
if (copy_supp_rates(wpa_s, sta, elems)) {
ap_free_sta(data, sta);
@@ -555,15 +578,27 @@
update_ht_state(data, sta);
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ copy_sta_vht_capab(data, sta, elems->vht_capabilities);
+ set_sta_vht_opmode(data, sta, elems->vht_opmode_notif);
+#endif /* CONFIG_IEEE80211AC */
+
+ if (hostapd_get_aid(data, sta) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
+ ap_free_sta(data, sta);
+ return NULL;
+ }
+
/* insert into driver */
os_memset(¶ms, 0, sizeof(params));
params.supp_rates = sta->supported_rates;
params.supp_rates_len = sta->supported_rates_len;
params.addr = addr;
params.plink_state = sta->plink_state;
- params.aid = sta->peer_lid;
+ params.aid = sta->aid;
params.listen_interval = 100;
params.ht_capabilities = sta->ht_capabilities;
+ params.vht_capabilities = sta->vht_capabilities;
params.flags |= WPA_STA_WMM;
params.flags_mask |= WPA_STA_AUTHENTICATED;
if (conf->security == MESH_CONF_SEC_NONE) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 936002d..747f1ae 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -190,7 +190,8 @@
static void mesh_rsn_deinit(struct mesh_rsn *rsn)
{
os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
- wpa_deinit(rsn->auth);
+ if (rsn->auth)
+ wpa_deinit(rsn->auth);
}
@@ -209,14 +210,15 @@
if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
mesh_rsn_deinit(mesh_rsn);
+ os_free(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;
+ conf->rsn_ie = (u8 *) ie;
+ conf->rsn_ie_len = ie_len;
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 822db74..45d06bf 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -17,6 +17,7 @@
#include "dbus/dbus_old.h"
#include "dbus/dbus_new.h"
#include "rsn_supp/wpa.h"
+#include "fst/fst.h"
#include "driver_i.h"
#include "scan.h"
#include "p2p_supplicant.h"
@@ -88,6 +89,16 @@
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+#ifdef CONFIG_FST
+ if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
+ if (new_state == WPA_COMPLETED)
+ fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
+ else if (old_state >= WPA_ASSOCIATED &&
+ new_state < WPA_ASSOCIATED)
+ fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
+ }
+#endif /* CONFIG_FST */
+
if (new_state == WPA_COMPLETED)
wpas_p2p_notif_connected(wpa_s);
else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
@@ -646,12 +657,30 @@
}
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason)
+{
+ /* Notify a group formation failed */
+ wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
+}
+
+
void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
}
+
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *go_dev_addr,
+ const u8 *bssid, int id, int op_freq)
+{
+ /* Notify a P2P Invitation Request */
+ wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+ id, op_freq);
+}
+
#endif /* CONFIG_P2P */
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 1aeec47..d9f0f5a 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -114,6 +114,8 @@
void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int network_id,
int client);
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+ const char *reason);
void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
@@ -135,5 +137,8 @@
struct wpa_ssid *ssid);
void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+ const u8 *sa, const u8 *go_dev_addr,
+ const u8 *bssid, int id, int op_freq);
#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 63af83a..6b3f83c 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -118,8 +118,9 @@
}
wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
- MACSTR " using interface %s",
- MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+ MACSTR " using interface %s (pending_action_tx=%p)",
+ MAC2STR(wpa_s->pending_action_dst), iface->ifname,
+ wpa_s->pending_action_tx);
res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
wpa_s->pending_action_dst,
wpa_s->pending_action_src,
@@ -183,8 +184,12 @@
return;
}
- wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
-
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Delete matching pending action frame (dst="
+ MACSTR " pending_action_tx=%p)", MAC2STR(dst),
+ wpa_s->pending_action_tx);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+ wpa_s->pending_action_tx);
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
@@ -250,8 +255,11 @@
if (wpa_s->pending_action_tx) {
wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
- "frame TX to " MACSTR,
- MAC2STR(wpa_s->pending_action_dst));
+ "frame TX to " MACSTR " (pending_action_tx=%p)",
+ MAC2STR(wpa_s->pending_action_dst),
+ wpa_s->pending_action_tx);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+ wpa_s->pending_action_tx);
wpabuf_free(wpa_s->pending_action_tx);
}
wpa_s->pending_action_tx_done = 0;
@@ -268,6 +276,12 @@
os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
wpa_s->pending_action_freq = freq;
wpa_s->pending_action_no_cck = no_cck;
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Stored pending action frame (dst=" MACSTR
+ " pending_action_tx=%p)",
+ MAC2STR(dst), wpa_s->pending_action_tx);
+ wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+ wpa_s->pending_action_tx);
if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
struct wpa_supplicant *iface;
@@ -428,6 +442,9 @@
*/
void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
{
+ wpa_printf(MSG_DEBUG,
+ "Off-channel: Clear pending Action frame TX (pending_action_tx=%p",
+ wpa_s->pending_action_tx);
wpabuf_free(wpa_s->pending_action_tx);
wpa_s->pending_action_tx = NULL;
}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b9ebd38..c7ddc99 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -47,6 +47,19 @@
#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
+/**
+ * Defines time interval in seconds when a GO needs to evacuate a frequency that
+ * it is currently using, but is no longer valid for P2P use cases.
+ */
+#define P2P_GO_FREQ_CHANGE_TIME 5
+
+/**
+ * Defines CSA parameters which are used when GO evacuates the no longer valid
+ * channel (and if the driver supports channel switch).
+ */
+#define P2P_GO_CSA_COUNT 7
+#define P2P_GO_CSA_BLOCK_TX 0
+
#ifndef P2P_MAX_CLIENT_IDLE
/*
* How many seconds to try to reconnect to the GO when connection in P2P client
@@ -85,6 +98,12 @@
#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
+/*
+ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
+ * was not possible.
+ */
+#define P2P_RECONSIDER_GO_MOVE_DELAY 30
+
enum p2p_group_removal_reason {
P2P_GROUP_REMOVAL_UNKNOWN,
P2P_GROUP_REMOVAL_SILENT,
@@ -94,7 +113,8 @@
P2P_GROUP_REMOVAL_UNAVAILABLE,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
P2P_GROUP_REMOVAL_PSK_FAILURE,
- P2P_GROUP_REMOVAL_FREQ_CONFLICT
+ P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
};
@@ -104,6 +124,10 @@
int go);
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+ int *force_freq, int *pref_freq, int go,
+ unsigned int *pref_freq_list,
+ unsigned int *num_pref_freq);
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
@@ -128,6 +152,16 @@
enum wpa_driver_if_type type);
static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
int already_deleted);
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num);
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
+static void
+wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs, unsigned int num,
+ enum wpas_p2p_channel_update_trig trig);
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
/*
@@ -515,27 +549,39 @@
}
+static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
+{
+ return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
+ !wpa_s->current_ssid->disabled &&
+ wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->p2p_persistent_group;
+}
+
+
+static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
+{
+ return p2p_is_active_persistent_group(wpa_s) &&
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
+}
+
+
/* Find an interface for a P2P group where we are the GO */
static struct wpa_supplicant *
wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *save = NULL;
- struct wpa_ssid *s;
if (!wpa_s)
return NULL;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- for (s = wpa_s->conf->ssid; s; s = s->next) {
- if (s->disabled || !s->p2p_group ||
- s->mode != WPAS_MODE_P2P_GO)
- continue;
+ if (!p2p_is_active_persistent_go(wpa_s))
+ continue;
- /* Prefer a group with connected clients */
- if (p2p_get_group_num_members(wpa_s->p2p_group))
- return wpa_s;
- save = wpa_s;
- }
+ /* Prefer a group with connected clients */
+ if (p2p_get_group_num_members(wpa_s->p2p_group))
+ return wpa_s;
+ save = wpa_s;
}
/* No group with connected clients, so pick the one without (if any) */
@@ -543,29 +589,23 @@
}
-/* Find an active P2P group where we are the GO */
-static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
- u8 *bssid)
+static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
{
- struct wpa_ssid *s, *empty = NULL;
+ return p2p_is_active_persistent_group(wpa_s) &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
- if (!wpa_s)
- return 0;
+/* Find an interface for a P2P group where we are the P2P Client */
+static struct wpa_supplicant *
+wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
+{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- for (s = wpa_s->conf->ssid; s; s = s->next) {
- if (s->disabled || !s->p2p_group ||
- s->mode != WPAS_MODE_P2P_GO)
- continue;
-
- os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
- if (p2p_get_group_num_members(wpa_s->p2p_group))
- return s;
- empty = s;
- }
+ if (p2p_is_active_persistent_cli(wpa_s))
+ return wpa_s;
}
- return empty;
+ return NULL;
}
@@ -584,20 +624,34 @@
}
-static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
+ unsigned int *force_freq,
+ unsigned int *pref_freq)
{
- struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+ struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
u8 conncap = P2PS_SETUP_NONE;
unsigned int owned_members = 0;
- unsigned int owner = 0;
- unsigned int client = 0;
- struct wpa_supplicant *go_wpa_s;
+ struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
struct wpa_ssid *persistent_go;
int p2p_no_group_iface;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+ if (force_freq)
+ *force_freq = 0;
+ if (pref_freq)
+ *pref_freq = 0;
+
+ size = P2P_MAX_PREF_CHANNELS;
+ if (force_freq && pref_freq &&
+ !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
+ (int *) pref_freq, 0, pref_freq_list, &size))
+ wpas_p2p_set_own_freq_preference(wpa_s,
+ *force_freq ? *force_freq :
+ *pref_freq);
+
/*
* For non-concurrent capable devices:
* If persistent_go, then no new.
@@ -605,36 +659,21 @@
* If client, then no GO.
*/
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+ if (go_wpa_s)
+ owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
- p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+ p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+ cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
- wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
- go_wpa_s, persistent_go);
-
- for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
- tmp_wpa_s = tmp_wpa_s->next) {
- for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
- wpa_printf(MSG_DEBUG,
- "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
- tmp_wpa_s, s, s->disabled,
- s->p2p_group, s->mode);
- if (!s->disabled && s->p2p_group) {
- if (s->mode == WPAS_MODE_P2P_GO) {
- owned_members +=
- p2p_get_group_num_members(
- tmp_wpa_s->p2p_group);
- owner++;
- } else
- client++;
- }
- }
- }
+ wpa_printf(MSG_DEBUG,
+ "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
+ go_wpa_s, owned_members, cli_wpa_s, persistent_go);
/* If not concurrent, restrict our choices */
if (p2p_no_group_iface) {
wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
- if (client)
+ if (cli_wpa_s)
return P2PS_SETUP_NONE;
if (go_wpa_s) {
@@ -666,10 +705,20 @@
/* If a required role has been specified, handle it here */
if (role && role != P2PS_SETUP_NEW) {
switch (incoming) {
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+ /*
+ * Peer has an active GO, so if the role allows it and
+ * we do not have any active roles, become client.
+ */
+ if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
+ !cli_wpa_s)
+ return P2PS_SETUP_CLIENT;
+
+ /* fall through */
+
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
- case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
- case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
conncap = role;
goto grp_owner;
@@ -678,7 +727,7 @@
* Must be a complimentary role - cannot be a client to
* more than one peer.
*/
- if (incoming == role || client)
+ if (incoming == role || cli_wpa_s)
return P2PS_SETUP_NONE;
return P2PS_SETUP_CLIENT;
@@ -704,7 +753,7 @@
switch (incoming) {
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
- if (client)
+ if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else if (!owned_members)
conncap = P2PS_SETUP_NEW;
@@ -719,13 +768,20 @@
break;
case P2PS_SETUP_GROUP_OWNER:
- if (!client)
+ if (!cli_wpa_s)
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
- if (client)
+ /*
+ * Peer has an active GO, so if the role allows it and
+ * we do not have any active roles, become client.
+ */
+ if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && !cli_wpa_s)
+ return P2PS_SETUP_CLIENT;
+
+ if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else {
u8 r;
@@ -747,15 +803,12 @@
(!incoming && (conncap & P2PS_SETUP_NEW))) {
if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
conncap &= ~P2PS_SETUP_GROUP_OWNER;
- wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
- owner, owned_members, conncap);
s = wpas_p2p_get_persistent_go(wpa_s);
-
- if (!s && !owner && p2p_no_group_iface) {
+ if (!s && !go_wpa_s && p2p_no_group_iface) {
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->own_addr);
- } else if (!s && !owner) {
+ } else if (!s && !go_wpa_s) {
if (wpas_p2p_add_group_interface(wpa_s,
WPA_IF_P2P_GO) < 0) {
wpa_printf(MSG_ERROR,
@@ -872,9 +925,12 @@
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
"timeout");
wpa_s->p2p_in_provisioning = 0;
+ wpas_p2p_group_formation_failed(wpa_s, 1);
}
wpa_s->p2p_in_invitation = 0;
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
/*
* Make sure wait for the first client does not remain active after the
@@ -946,6 +1002,8 @@
else
wpa_drv_deinit_p2p_cli(wpa_s);
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
return 0;
}
@@ -1258,6 +1316,7 @@
if (!success) {
wpa_msg_global(wpa_s->parent, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
+ wpas_notify_p2p_group_formation_failure(wpa_s, "");
if (already_deleted)
return;
wpas_p2p_group_delete(wpa_s,
@@ -1339,6 +1398,25 @@
};
+static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
+{
+ struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
+
+ wpa_printf(MSG_DEBUG,
+ "P2P: Free Action frame radio work @%p (freq=%u dst="
+ MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
+ wpa_s->p2p_send_action_work, awork->freq,
+ MAC2STR(awork->dst), MAC2STR(awork->src),
+ MAC2STR(awork->bssid), awork->wait_time);
+ wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
+ awork->buf, awork->len);
+ os_free(awork);
+ wpa_s->p2p_send_action_work->ctx = NULL;
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+}
+
+
static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
void *timeout_ctx)
{
@@ -1348,9 +1426,7 @@
return;
wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
- os_free(wpa_s->p2p_send_action_work->ctx);
- radio_work_done(wpa_s->p2p_send_action_work);
- wpa_s->p2p_send_action_work = NULL;
+ wpas_p2p_free_send_action_work(wpa_s);
}
@@ -1358,11 +1434,13 @@
{
if (wpa_s->p2p_send_action_work) {
struct send_action_work *awork;
+
awork = wpa_s->p2p_send_action_work->ctx;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Clear Action TX work @%p (wait_time=%u)",
+ wpa_s->p2p_send_action_work, awork->wait_time);
if (awork->wait_time == 0) {
- os_free(awork);
- radio_work_done(wpa_s->p2p_send_action_work);
- wpa_s->p2p_send_action_work = NULL;
+ wpas_p2p_free_send_action_work(wpa_s);
} else {
/*
* In theory, this should not be needed, but number of
@@ -1670,6 +1748,15 @@
struct wpa_ssid *ssid;
int network_id = -1;
+ wpa_s->ap_configured_cb = NULL;
+ wpa_s->ap_configured_cb_ctx = NULL;
+ wpa_s->ap_configured_cb_data = NULL;
+ if (!wpa_s->go_params) {
+ wpa_printf(MSG_ERROR,
+ "P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
+ return;
+ }
+
p2p_go_save_group_common_freqs(wpa_s, params);
p2p_go_dump_common_freqs(wpa_s);
@@ -1686,14 +1773,22 @@
params->persistent_group, "");
wpa_s->group_formation_reported = 1;
- if (wpa_s->parent->p2ps_join_addr_valid) {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "P2PS: Setting default PIN for " MACSTR,
- MAC2STR(wpa_s->parent->p2ps_join_addr));
- wpa_supplicant_ap_wps_pin(wpa_s,
- wpa_s->parent->p2ps_join_addr,
- "12345670", NULL, 0, 0);
- wpa_s->parent->p2ps_join_addr_valid = 0;
+ if (wpa_s->parent->p2ps_method_config_any) {
+ if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2PS: Setting default PIN for ANY");
+ wpa_supplicant_ap_wps_pin(wpa_s, NULL,
+ "12345670", NULL, 0,
+ 0);
+ } else {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2PS: Setting default PIN for " MACSTR,
+ MAC2STR(wpa_s->parent->p2ps_join_addr));
+ wpa_supplicant_ap_wps_pin(
+ wpa_s, wpa_s->parent->p2ps_join_addr,
+ "12345670", NULL, 0, 0);
+ }
+ wpa_s->parent->p2ps_method_config_any = 0;
}
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
@@ -1780,6 +1875,7 @@
wpa_s->show_group_started = 0;
wpa_s->p2p_go_group_formation_completed = 0;
wpa_s->group_formation_reported = 0;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
@@ -1790,6 +1886,8 @@
ssid->frequency = params->freq;
ssid->ht40 = params->ht40;
ssid->vht = params->vht;
+ ssid->max_oper_chwidth = params->max_oper_chwidth;
+ ssid->vht_center_freq2 = params->vht_center_freq2;
ssid->ssid = os_zalloc(params->ssid_len + 1);
if (ssid->ssid) {
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -1862,6 +1960,7 @@
d->num_sec_device_types = s->num_sec_device_types;
d->p2p_group_idle = s->p2p_group_idle;
+ d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
d->p2p_intra_bss = s->p2p_intra_bss;
d->persistent_reconnect = s->persistent_reconnect;
d->max_num_sta = s->max_num_sta;
@@ -2081,10 +2180,17 @@
return;
}
+ if (!res->role_go) {
+ /* Inform driver of the operating channel of GO. */
+ wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
+ }
+
if (wpa_s->p2p_go_ht40)
res->ht40 = 1;
if (wpa_s->p2p_go_vht)
res->vht = 1;
+ res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
+ res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -2128,18 +2234,22 @@
wpa_s->pending_interface_name[0] = '\0';
group_wpa_s->p2p_in_provisioning = 1;
- if (res->role_go)
+ if (res->role_go) {
wpas_start_wps_go(group_wpa_s, res, 1);
- else
+ } else {
+ os_get_reltime(&group_wpa_s->scan_min_time);
wpas_start_wps_enrollee(group_wpa_s, res);
+ }
} else {
wpa_s->p2p_in_provisioning = 1;
wpa_s->global->p2p_group_formation = wpa_s;
- if (res->role_go)
+ if (res->role_go) {
wpas_start_wps_go(wpa_s, res, 1);
- else
+ } else {
+ os_get_reltime(&wpa_s->scan_min_time);
wpas_start_wps_enrollee(ctx, res);
+ }
}
wpa_s->p2p_long_listen = 0;
@@ -2578,12 +2688,85 @@
}
-static int freq_included(const struct p2p_channels *channels, unsigned int freq)
+static int freq_included(struct wpa_supplicant *wpa_s,
+ const struct p2p_channels *channels,
+ unsigned int freq)
{
- if (channels == NULL)
- return 1; /* Assume no restrictions */
- return p2p_channels_includes_freq(channels, freq);
+ if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
+ wpas_p2p_go_is_peer_freq(wpa_s, freq))
+ return 1;
+ return 0;
+}
+
+static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
+{
+ unsigned int num = P2P_MAX_CHANNELS;
+ int *common_freqs;
+ int ret;
+
+ p2p_go_dump_common_freqs(wpa_s);
+ common_freqs = os_calloc(num, sizeof(int));
+ if (!common_freqs)
+ return;
+
+ ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
+ if (ret < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Failed to get group common freqs");
+ os_free(common_freqs);
+ return;
+ }
+
+ os_free(wpa_s->p2p_group_common_freqs);
+ wpa_s->p2p_group_common_freqs = common_freqs;
+ wpa_s->p2p_group_common_freqs_num = num;
+ p2p_go_dump_common_freqs(wpa_s);
+}
+
+
+/*
+ * Check if the given frequency is one of the possible operating frequencies
+ * set after the completion of the GO Negotiation.
+ */
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+ unsigned int i;
+
+ p2p_go_dump_common_freqs(wpa_s);
+
+ /* assume no restrictions */
+ if (!wpa_s->p2p_group_common_freqs_num)
+ return 1;
+
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ if (wpa_s->p2p_group_common_freqs[i] == freq)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
+ struct sta_info *sta, void *ctx)
+{
+ int *ecsa_support = ctx;
+
+ *ecsa_support &= sta->ecsa_supported;
+
+ return 0;
+}
+
+
+/* Check if all the peers support eCSA */
+static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
+{
+ int ecsa_support = 1;
+
+ ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
+ &ecsa_support);
+
+ return ecsa_support;
}
@@ -2759,7 +2942,7 @@
"running a GO but we are capable of MCC, "
"figure out the best channel to use");
*force_freq = 0;
- } else if (!freq_included(channels, *force_freq)) {
+ } else if (!freq_included(wpa_s, channels, *force_freq)) {
/* We are the GO, and *force_freq is not in the
* intersection */
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -2796,8 +2979,9 @@
if (s) {
int go = s->mode == WPAS_MODE_P2P_GO;
wpas_p2p_group_add_persistent(
- wpa_s, s, go, 0, op_freq, 0, 0, NULL,
- go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+ wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL,
+ go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
+ 1);
} else if (bssid) {
wpa_s->user_initiated_pd = 0;
wpas_p2p_join(wpa_s, bssid, go_dev_addr,
@@ -2828,6 +3012,8 @@
" unknown-network",
MAC2STR(sa), MAC2STR(go_dev_addr));
}
+ wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
+ bssid, 0, op_freq);
return;
}
@@ -2840,6 +3026,8 @@
"sa=" MACSTR " persistent=%d",
MAC2STR(sa), s->id);
}
+ wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+ s->id, op_freq);
}
@@ -2924,9 +3112,37 @@
wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
status, MAC2STR(peer));
if (wpa_s->pending_invite_ssid_id == -1) {
+ struct wpa_supplicant *group_if =
+ wpa_s->global->p2p_invite_group;
+
if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
wpas_remove_persistent_client(wpa_s, peer);
- return; /* Invitation to active group */
+
+ /*
+ * Invitation to an active group. If this is successful and we
+ * are the GO, set the client wait to postpone some concurrent
+ * operations and to allow provisioning and connection to happen
+ * more quickly.
+ */
+ if (status == P2P_SC_SUCCESS &&
+ group_if && group_if->current_ssid &&
+ group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
+ os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (group_if->p2p_go_csa_on_inv) {
+ wpa_printf(MSG_DEBUG,
+ "Testing: force P2P GO CSA after invitation");
+ eloop_cancel_timeout(
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ eloop_register_timeout(
+ 0, 50000,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ }
+ return;
}
if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
@@ -2966,10 +3182,10 @@
os_sleep(0, 50000);
if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
- freq_included(channels, neg_freq))
+ freq_included(wpa_s, channels, neg_freq))
freq = neg_freq;
else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
- freq_included(channels, peer_oper_freq))
+ freq_included(wpa_s, channels, peer_oper_freq))
freq = peer_oper_freq;
else
freq = 0;
@@ -2980,11 +3196,13 @@
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth,
channels,
ssid->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
- 0);
+ 0, 1);
}
@@ -3120,7 +3338,7 @@
u8 min_chan;
u8 max_chan;
u8 inc;
- enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
+ enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
};
static const struct p2p_oper_class_map op_class[] = {
@@ -3146,6 +3364,8 @@
* removing invalid channels.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80 },
+ { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160 },
{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
{ -1, 0, 0, 0, 0, BW20 }
};
@@ -3212,6 +3432,75 @@
}
+static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel)
+{
+ u8 center_channels[] = { 50, 114 };
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+ /*
+ * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+ * so the center channel is 14 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 14 &&
+ channel <= center_channels[i] + 14)
+ return center_channels[i];
+
+ return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ u8 center_chan;
+ int i, flags;
+ enum chan_allowed res, ret = ALLOWED;
+
+ center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+ /* VHT 160 MHz uses DFS channels in most countries. */
+
+ /* Check all the channels are available */
+ for (i = 0; i < 8; i++) {
+ int adj_chan = center_chan - 14 + i * 4;
+
+ res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+ if (res == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (res == NO_IR)
+ ret = NO_IR;
+
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+ return NOT_ALLOWED;
+ if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+ return NOT_ALLOWED;
+ if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+ return NOT_ALLOWED;
+ if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+ return NOT_ALLOWED;
+ if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+ return NOT_ALLOWED;
+ }
+
+ return ret;
+}
+
+
static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel, u8 bw)
@@ -3230,6 +3519,8 @@
res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
} else if (bw == BW80) {
res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+ } else if (bw == BW160) {
+ res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -3343,6 +3634,15 @@
}
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel)
+{
+ if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160))
+ return 0;
+ return wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+}
+
+
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
size_t buf_len)
{
@@ -3386,12 +3686,7 @@
{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid == NULL)
- continue;
- if (ssid->mode != WPAS_MODE_INFRA)
- continue;
- if (wpa_s->wpa_state != WPA_COMPLETED &&
- wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+ if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
continue;
if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
return wpa_s;
@@ -3508,7 +3803,8 @@
static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
size_t ssid_len, u8 *go_dev_addr,
- u8 *ret_ssid, size_t *ret_ssid_len)
+ u8 *ret_ssid, size_t *ret_ssid_len,
+ u8 *intended_iface_addr)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
@@ -3518,6 +3814,19 @@
os_memcpy(ret_ssid, s->ssid, s->ssid_len);
*ret_ssid_len = s->ssid_len;
os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+
+ if (s->mode != WPAS_MODE_P2P_GO) {
+ os_memset(intended_iface_addr, 0, ETH_ALEN);
+ } else if (wpas_p2p_create_iface(wpa_s)) {
+ if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
+ return 0;
+
+ os_memcpy(intended_iface_addr,
+ wpa_s->pending_interface_addr, ETH_ALEN);
+ } else {
+ os_memcpy(intended_iface_addr, wpa_s->own_addr,
+ ETH_ALEN);
+ }
return 1;
}
@@ -3526,24 +3835,40 @@
static int wpas_get_go_info(void *ctx, u8 *intended_addr,
- u8 *ssid, size_t *ssid_len, int *group_iface)
+ u8 *ssid, size_t *ssid_len, int *group_iface,
+ unsigned int *freq)
{
struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_supplicant *go;
struct wpa_ssid *s;
- u8 bssid[ETH_ALEN];
- s = wpas_p2p_group_go_ssid(wpa_s, bssid);
- if (!s) {
+ /*
+ * group_iface will be set to 1 only if a dedicated interface for P2P
+ * role is required. First, we try to reuse an active GO. However,
+ * if it is not present, we will try to reactivate an existing
+ * persistent group and set group_iface to 1, so the caller will know
+ * that the pending interface should be used.
+ */
+ *group_iface = 0;
+
+ if (freq)
+ *freq = 0;
+
+ go = wpas_p2p_get_go_group(wpa_s);
+ if (!go) {
s = wpas_p2p_get_persistent_go(wpa_s);
+ *group_iface = wpas_p2p_create_iface(wpa_s);
if (s)
- os_memcpy(bssid, s->bssid, ETH_ALEN);
+ os_memcpy(intended_addr, s->bssid, ETH_ALEN);
+ else
+ return 0;
+ } else {
+ s = go->current_ssid;
+ os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
+ if (freq)
+ *freq = go->assoc_freq;
}
- *group_iface = wpas_p2p_create_iface(wpa_s);
- if (!s)
- return 0;
-
- os_memcpy(intended_addr, bssid, ETH_ALEN);
os_memcpy(ssid, s->ssid, s->ssid_len);
*ssid_len = s->ssid_len;
@@ -3596,19 +3921,50 @@
}
+static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
+ const u8 *feat_cap, size_t feat_cap_len)
+{
+ static const char pref[] = " feature_cap=";
+ int ret;
+
+ buf[0] = '\0';
+
+ /*
+ * We expect a feature capability to contain at least one byte to be
+ * reported. The string buffer provided by the caller function is
+ * expected to be big enough to contain all bytes of the attribute for
+ * known specifications. This function truncates the reported bytes if
+ * the feature capability data exceeds the string buffer size.
+ */
+ if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
+ return;
+
+ os_memcpy(buf, pref, sizeof(pref));
+ ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
+ buf_len - sizeof(pref) + 1,
+ feat_cap, feat_cap_len);
+
+ if (ret != (2 * (int) feat_cap_len))
+ wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
+}
+
+
static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
const u8 *adv_mac, const u8 *ses_mac,
const u8 *grp_mac, u32 adv_id, u32 ses_id,
u8 conncap, int passwd_id,
const u8 *persist_ssid,
size_t persist_ssid_size, int response_done,
- int prov_start, const char *session_info)
+ int prov_start, const char *session_info,
+ const u8 *feat_cap, size_t feat_cap_len,
+ unsigned int freq)
{
struct wpa_supplicant *wpa_s = ctx;
u8 mac[ETH_ALEN];
- struct wpa_ssid *persistent_go, *stale, *s;
+ struct wpa_ssid *persistent_go, *stale, *s = NULL;
int save_config = 0;
struct wpa_supplicant *go_wpa_s;
+ char feat_cap_str[256];
if (!dev)
return;
@@ -3621,6 +3977,9 @@
if (!grp_mac)
grp_mac = mac;
+ wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
+ feat_cap, feat_cap_len);
+
if (prov_start) {
if (session_info == NULL) {
wpa_msg_global(wpa_s, MSG_INFO,
@@ -3628,22 +3987,22 @@
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d",
+ " dev_passwd_id=%d%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id);
+ passwd_id, feat_cap_str);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_START MACSTR
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d info='%s'",
+ " dev_passwd_id=%d info='%s'%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, session_info);
+ passwd_id, session_info, feat_cap_str);
}
return;
}
@@ -3665,16 +4024,25 @@
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d"
" adv_id=%x adv_mac=" MACSTR
- " session=%x mac=" MACSTR,
+ " session=%x mac=" MACSTR "%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
- ses_id, MAC2STR(ses_mac));
+ ses_id, MAC2STR(ses_mac), feat_cap_str);
return;
}
/* Clean up stale persistent groups with this device */
- s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
- persist_ssid_size);
+ if (persist_ssid && persist_ssid_size)
+ s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+ persist_ssid_size);
+
+ if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
+ is_zero_ether_addr(grp_mac)) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
+ return;
+ }
+
for (;;) {
stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
if (!stale)
@@ -3730,29 +4098,40 @@
" status=%d"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " persist=%d",
+ " persist=%d%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
- ses_id, MAC2STR(ses_mac), s->id);
+ ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
return;
}
if (conncap == P2PS_SETUP_GROUP_OWNER) {
- const char *go_ifname = NULL;
+ /*
+ * We need to copy the interface name. Simply saving a
+ * pointer isn't enough, since if we use pending_interface_name
+ * it will be overwritten when the group is added.
+ */
+ char go_ifname[100];
+
+ go_ifname[0] = '\0';
if (!go_wpa_s) {
wpa_s->global->pending_p2ps_group = 1;
+ wpa_s->global->pending_p2ps_group_freq = freq;
- if (wpa_s->conf->p2p_no_group_iface)
- go_ifname = wpa_s->ifname;
+ if (!wpas_p2p_create_iface(wpa_s))
+ os_memcpy(go_ifname, wpa_s->ifname,
+ sizeof(go_ifname));
else if (wpa_s->pending_interface_name[0])
- go_ifname = wpa_s->pending_interface_name;
+ os_memcpy(go_ifname,
+ wpa_s->pending_interface_name,
+ sizeof(go_ifname));
- if (!go_ifname) {
+ if (!go_ifname[0]) {
wpas_p2ps_prov_complete(
wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
dev, adv_mac, ses_mac,
- NULL, adv_id, ses_id, 0, 0,
- NULL, 0, 0, 0, NULL);
+ grp_mac, adv_id, ses_id, 0, 0,
+ NULL, 0, 0, 0, NULL, NULL, 0, 0);
return;
}
@@ -3760,34 +4139,41 @@
if (response_done && persistent_go) {
wpas_p2p_group_add_persistent(
wpa_s, persistent_go,
- 0, 0, 0, 0, 0, NULL,
+ 0, 0, freq, 0, 0, 0, 0, NULL,
persistent_go->mode ==
WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
- 0);
+ 0, 0);
} else if (response_done) {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
}
if (passwd_id == DEV_PW_P2PS_DEFAULT) {
- os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
- wpa_s->p2ps_join_addr_valid = 1;
- wpa_dbg(wpa_s, MSG_DEBUG,
- "P2PS: Saving PIN for " MACSTR,
- MAC2STR(dev));
+ os_memcpy(wpa_s->p2ps_join_addr, grp_mac,
+ ETH_ALEN);
+ wpa_s->p2ps_method_config_any = 1;
}
} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
- go_ifname = go_wpa_s->ifname;
+ os_memcpy(go_ifname, go_wpa_s->ifname,
+ sizeof(go_ifname));
- wpa_dbg(go_wpa_s, MSG_DEBUG,
- "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
- wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670",
- NULL, 0, 0);
+ if (is_zero_ether_addr(grp_mac)) {
+ wpa_dbg(go_wpa_s, MSG_DEBUG,
+ "P2P: Setting PIN-1 for ANY");
+ wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
+ "12345670", NULL, 0,
+ 0);
+ } else {
+ wpa_dbg(go_wpa_s, MSG_DEBUG,
+ "P2P: Setting PIN-1 for " MACSTR,
+ MAC2STR(grp_mac));
+ wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
+ "12345670", NULL, 0,
+ 0);
+ }
- os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
- wpa_s->p2ps_join_addr_valid = 1;
- wpa_dbg(wpa_s, MSG_DEBUG,
- "P2PS: Saving PIN for " MACSTR, MAC2STR(dev));
+ os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
+ wpa_s->p2ps_method_config_any = 1;
}
wpa_msg_global(wpa_s, MSG_INFO,
@@ -3795,11 +4181,11 @@
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d go=%s",
+ " dev_passwd_id=%d go=%s%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, go_ifname);
+ passwd_id, go_ifname, feat_cap_str);
return;
}
@@ -3817,22 +4203,22 @@
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d join=" MACSTR,
+ " dev_passwd_id=%d join=" MACSTR "%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, MAC2STR(grp_mac));
+ passwd_id, MAC2STR(grp_mac), feat_cap_str);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d",
+ " dev_passwd_id=%d%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id);
+ passwd_id, feat_cap_str);
}
}
@@ -3848,10 +4234,13 @@
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *persistent_go;
+ unsigned int freq;
if (!wpa_s->global->pending_p2ps_group)
return 0;
+ freq = wpa_s->global->pending_p2ps_group_freq;
+ wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->global->pending_p2ps_group = 0;
if (wpas_p2p_get_go_group(wpa_s))
@@ -3860,17 +4249,28 @@
if (persistent_go) {
wpas_p2p_group_add_persistent(
- wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+ wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL,
persistent_go->mode == WPAS_MODE_P2P_GO ?
- P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+ P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
} else {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
}
return 1;
}
+static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
+ unsigned int *len,
+ unsigned int *freq_list)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT, len, freq_list);
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
@@ -3924,6 +4324,7 @@
p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
p2p.p2ps_group_capability = p2ps_group_capability;
+ p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -4228,6 +4629,7 @@
}
wpa_msg_global(wpa_s->parent, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
+ wpas_notify_p2p_group_formation_failure(wpa_s, "");
}
}
@@ -4369,10 +4771,12 @@
wpa_s->p2p_persistent_group, 0, 0, 0,
wpa_s->p2p_go_intent,
wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth);
return;
}
@@ -4453,6 +4857,8 @@
wpa_msg_global(wpa_s->parent, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE
"reason=FREQ_CONFLICT");
+ wpas_notify_p2p_group_formation_failure(
+ wpa_s, "FREQ_CONFLICT");
return;
}
@@ -4472,6 +4878,9 @@
case WPS_PBC:
method = WPS_CONFIG_PUSHBUTTON;
break;
+ case WPS_P2PS:
+ method = WPS_CONFIG_P2PS;
+ break;
default:
method = 0;
break;
@@ -4714,11 +5123,16 @@
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
- int *force_freq, int *pref_freq, int go)
+ int *force_freq, int *pref_freq, int go,
+ unsigned int *pref_freq_list,
+ unsigned int *num_pref_freq)
{
struct wpa_used_freq_data *freqs;
int res, best_freq, num_unused;
- unsigned int freq_in_use = 0, num, i;
+ unsigned int freq_in_use = 0, num, i, max_pref_freq;
+
+ max_pref_freq = *num_pref_freq;
+ *num_pref_freq = 0;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
@@ -4783,6 +5197,47 @@
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
+ enum wpa_driver_if_type iface_type;
+
+ if (go)
+ iface_type = WPA_IF_P2P_GO;
+ else
+ iface_type = WPA_IF_P2P_CLIENT;
+
+ wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
+ best_freq, go);
+
+ res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
+ &max_pref_freq,
+ pref_freq_list);
+ if (!res && max_pref_freq > 0) {
+ *num_pref_freq = max_pref_freq;
+ i = 0;
+ while (wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]) &&
+ i < *num_pref_freq) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: preferred_freq_list[%d]=%d is disallowed",
+ i, pref_freq_list[i]);
+ i++;
+ }
+ if (i != *num_pref_freq) {
+ best_freq = pref_freq_list[i];
+ wpa_printf(MSG_DEBUG,
+ "P2P: Using preferred_freq_list[%d]=%d",
+ i, best_freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All driver preferred frequencies are disallowed for P2P use");
+ *num_pref_freq = 0;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: No preferred frequency list available");
+ }
+ }
+
/* We have a candidate frequency to use */
if (best_freq > 0) {
if (*pref_freq == 0 && num_unused > 0) {
@@ -4826,12 +5281,15 @@
* initiating Group Owner negotiation
* @go_intent: GO Intent or -1 to use default
* @freq: Frequency for the group or 0 for auto-selection
+ * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
* @persistent_id: Persistent group credentials to use for forcing GO
* parameters or -1 to generate new values (SSID/passphrase)
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
* interoperability workaround when initiating group formation
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: Channel width supported by GO operating with VHT support
+ * (VHT_CHANWIDTH_*).
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
* failure, -2 on failure due to channel not currently available,
* -3 if forced channel is not supported
@@ -4839,14 +5297,16 @@
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
- int go_intent, int freq, int persistent_id, int pd,
- int ht40, int vht)
+ int go_intent, int freq, unsigned int vht_center_freq2,
+ int persistent_id, int pd, int ht40, int vht,
+ unsigned int vht_chwidth)
{
int force_freq = 0, pref_freq = 0;
int ret = 0, res;
enum wpa_driver_if_type iftype;
const u8 *if_addr;
struct wpa_ssid *ssid = NULL;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -4863,6 +5323,8 @@
wpa_s->global->p2p_fail_on_wps_complete = 0;
wpa_s->global->pending_p2ps_group = 0;
+ wpa_s->global->pending_p2ps_group_freq = 0;
+ wpa_s->p2ps_method_config_any = 0;
if (go_intent < 0)
go_intent = wpa_s->conf->p2p_go_intent;
@@ -4879,6 +5341,8 @@
wpa_s->p2p_pd_before_go_neg = !!pd;
wpa_s->p2p_go_ht40 = !!ht40;
wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
+ wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -4923,13 +5387,16 @@
return ret;
}
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- go_intent == 15);
+ go_intent == 15, pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s,
force_freq ? force_freq : pref_freq);
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
if (wpa_s->create_p2p_iface) {
@@ -4944,8 +5411,10 @@
}
if_addr = wpa_s->pending_interface_addr;
- } else
+ } else {
if_addr = wpa_s->own_addr;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+ }
if (auth) {
if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
@@ -5090,6 +5559,38 @@
{
unsigned int r;
+ if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
+ unsigned int i, size = P2P_MAX_PREF_CHANNELS;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+ int res;
+
+ res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
+ &size, pref_freq_list);
+ if (!res && size > 0) {
+ i = 0;
+ while (wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]) &&
+ i < size) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: preferred_freq_list[%d]=%d is disallowed",
+ i, pref_freq_list[i]);
+ i++;
+ }
+ if (i != size) {
+ freq = pref_freq_list[i];
+ wpa_printf(MSG_DEBUG,
+ "P2P: Using preferred_freq_list[%d]=%d",
+ i, freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All driver preferred frequencies are disallowed for P2P use");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: No preferred frequency list available");
+ }
+ }
+
if (freq == 2) {
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
"band");
@@ -5153,30 +5654,45 @@
}
-static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
- struct p2p_go_neg_results *params,
- const struct p2p_channels *channels)
+static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
+ const struct p2p_channels *channels,
+ int freq)
+{
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
+ p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+ freq_included(wpa_s, channels, freq))
+ return 1;
+ return 0;
+}
+
+
+static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ const struct p2p_channels *channels)
{
unsigned int i, r;
/* first try some random selection of the social channels */
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
- return -1;
+ return;
for (i = 0; i < 3; i++) {
params->freq = 2412 + ((r + i) % 3) * 25;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
- /* try all channels in reg. class 81 */
+ /* try all other channels in operating class 81 */
for (i = 0; i < 11; i++) {
params->freq = 2412 + i * 5;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+
+ /* skip social channels; covered in the previous loop */
+ if (params->freq == 2412 ||
+ params->freq == 2437 ||
+ params->freq == 2462)
+ continue;
+
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
@@ -5184,7 +5700,7 @@
for (i = 0; i < 4; i++) {
params->freq = 5180 + i * 20;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
@@ -5193,7 +5709,7 @@
for (i = 0; i < 4; i++) {
params->freq = 5745 + i * 20;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
@@ -5201,7 +5717,7 @@
/* try social channel class 180 channel 2 */
params->freq = 58320 + 1 * 2160;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
@@ -5209,150 +5725,229 @@
for (i = 0; i < 4; i++) {
params->freq = 58320 + i * 2160;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
+ params->freq = 0;
wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
- return -1;
+ return;
out:
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
params->freq);
- return 0;
}
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
- int freq, int ht40, int vht,
+ int freq, int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
const struct p2p_channels *channels)
{
struct wpa_used_freq_data *freqs;
- unsigned int pref_freq, cand_freq;
+ unsigned int cand;
unsigned int num, i;
+ int ignore_no_freqs = 0;
os_memset(params, 0, sizeof(*params));
params->role_go = 1;
params->ht40 = ht40;
params->vht = vht;
- if (freq) {
- if (!freq_included(channels, freq)) {
- wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
- "accepted", freq);
- return -1;
- }
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
- "frequency %d MHz", freq);
- params->freq = freq;
- } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
- wpa_s->conf->p2p_oper_channel >= 1 &&
- wpa_s->conf->p2p_oper_channel <= 11 &&
- freq_included(channels,
- 2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
- params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
- "frequency %d MHz", params->freq);
- } else if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
- wpa_s->conf->p2p_oper_reg_class == 116 ||
- wpa_s->conf->p2p_oper_reg_class == 117 ||
- wpa_s->conf->p2p_oper_reg_class == 124 ||
- wpa_s->conf->p2p_oper_reg_class == 125 ||
- wpa_s->conf->p2p_oper_reg_class == 126 ||
- wpa_s->conf->p2p_oper_reg_class == 127) &&
- freq_included(channels,
- 5000 + 5 * wpa_s->conf->p2p_oper_channel)) {
- params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
- "frequency %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_overall_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_overall_freq) &&
- freq_included(channels, wpa_s->best_overall_freq)) {
- params->freq = wpa_s->best_overall_freq;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
- "channel %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_24_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_24_freq) &&
- freq_included(channels, wpa_s->best_24_freq)) {
- params->freq = wpa_s->best_24_freq;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
- "channel %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_5_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_5_freq) &&
- freq_included(channels, wpa_s->best_5_freq)) {
- params->freq = wpa_s->best_5_freq;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
- "channel %d MHz", params->freq);
- } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
- channels))) {
- params->freq = pref_freq;
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
- "channels", params->freq);
- } else {
- /* no preference, select some channel */
- if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
- return -1;
- }
+ params->max_oper_chwidth = max_oper_chwidth;
+ params->vht_center_freq2 = vht_center_freq2;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (!freqs)
return -1;
- num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
+ num = get_shared_radio_freqs_data(wpa_s, freqs,
+ wpa_s->num_multichan_concurrent);
- cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
+ wpa_s->wpa_state == WPA_COMPLETED) {
+ wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+ __func__);
- /* First try the best used frequency if possible */
- if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
- params->freq = cand_freq;
- } else if (!freq) {
- /* Try any of the used frequencies */
+ /*
+ * If the frequency selection is done for an active P2P GO that
+ * is not sharing a frequency, allow to select a new frequency
+ * even if there are no unused frequencies as we are about to
+ * move the P2P GO so its frequency can be re-used.
+ */
for (i = 0; i < num; i++) {
- if (freq_included(channels, freqs[i].freq)) {
- wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
- freqs[i].freq);
- params->freq = freqs[i].freq;
+ if (freqs[i].freq == wpa_s->current_ssid->frequency &&
+ freqs[i].flags == 0) {
+ ignore_no_freqs = 1;
break;
}
}
-
- if (i == num) {
- if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
- os_free(freqs);
- return -1;
- } else {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
- }
- }
- } else {
- for (i = 0; i < num; i++) {
- if (freqs[i].freq == freq)
- break;
- }
-
- if (i == num) {
- if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
- if (freq)
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
- os_free(freqs);
- return -1;
- } else {
- wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
- }
- }
}
+ /* try using the forced freq */
+ if (freq) {
+ if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Forced GO freq %d MHz not accepted",
+ freq);
+ goto fail;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (freqs[i].freq == freq) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: forced freq (%d MHz) is also shared",
+ freq);
+ params->freq = freq;
+ goto success;
+ }
+ }
+
+ if (!ignore_no_freqs &&
+ wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
+ freq);
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "P2P: force GO freq (%d MHz) on a free channel",
+ freq);
+ params->freq = freq;
+ goto success;
+ }
+
+ /* consider using one of the shared frequencies */
+ if (num) {
+ cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use shared freq (%d MHz) for GO",
+ freq);
+ params->freq = cand;
+ goto success;
+ }
+
+ /* try using one of the shared freqs */
+ for (i = 0; i < num; i++) {
+ if (wpas_p2p_supported_freq_go(wpa_s, channels,
+ freqs[i].freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use shared freq (%d MHz) for GO",
+ freq);
+ params->freq = freqs[i].freq;
+ goto success;
+ }
+ }
+ }
+
+ if (!ignore_no_freqs &&
+ wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Cannot force GO on any of the channels we are already using");
+ goto fail;
+ }
+
+ /* try using the setting from the configuration file */
+ if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+ wpa_s->conf->p2p_oper_channel >= 1 &&
+ wpa_s->conf->p2p_oper_channel <= 11 &&
+ wpas_p2p_supported_freq_go(
+ wpa_s, channels,
+ 2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
+ params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ goto success;
+ }
+
+ if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
+ wpa_s->conf->p2p_oper_reg_class == 116 ||
+ wpa_s->conf->p2p_oper_reg_class == 117 ||
+ wpa_s->conf->p2p_oper_reg_class == 124 ||
+ wpa_s->conf->p2p_oper_reg_class == 125 ||
+ wpa_s->conf->p2p_oper_reg_class == 126 ||
+ wpa_s->conf->p2p_oper_reg_class == 127) &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ 5000 +
+ 5 * wpa_s->conf->p2p_oper_channel)) {
+ params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+ "frequency %d MHz", params->freq);
+ goto success;
+ }
+
+ /* Try using best channels */
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_overall_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_overall_freq)) {
+ params->freq = wpa_s->best_overall_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
+ "channel %d MHz", params->freq);
+ goto success;
+ }
+
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_24_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_24_freq)) {
+ params->freq = wpa_s->best_24_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
+ "channel %d MHz", params->freq);
+ goto success;
+ }
+
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_5_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_5_freq)) {
+ params->freq = wpa_s->best_5_freq;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
+ "channel %d MHz", params->freq);
+ goto success;
+ }
+
+ /* try using preferred channels */
+ cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
+ if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
+ "channels", params->freq);
+ goto success;
+ }
+
+ /* Try using one of the group common freqs */
+ if (wpa_s->p2p_group_common_freqs) {
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ cand = wpa_s->p2p_group_common_freqs[i];
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use freq %d MHz common with the peer",
+ params->freq);
+ goto success;
+ }
+ }
+ }
+
+ /* no preference, select some channel */
+ wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
+
+ if (params->freq == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
+ goto fail;
+ }
+
+success:
os_free(freqs);
return 0;
+fail:
+ os_free(freqs);
+ return -1;
}
@@ -5395,15 +5990,18 @@
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @persistent_group: Whether to create a persistent group
* @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: channel bandwidth for GO operating with VHT support
* Returns: 0 on success, -1 on failure
*
* This function creates a new P2P group with the local end as the Group Owner,
* i.e., without using Group Owner Negotiation.
*/
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40, int vht)
+ int freq, int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth)
{
struct p2p_go_neg_results params;
@@ -5421,7 +6019,8 @@
if (freq < 0)
return -1;
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, NULL))
return -1;
if (params.freq &&
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
@@ -5455,13 +6054,15 @@
static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
struct wpa_ssid *params, int addr_allocated,
- int freq)
+ int freq, int force_scan)
{
struct wpa_ssid *ssid;
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
if (wpa_s == NULL)
return -1;
+ if (force_scan)
+ os_get_reltime(&wpa_s->scan_min_time);
wpa_s->p2p_last_4way_hs_fail = NULL;
wpa_supplicant_ap_deinit(wpa_s);
@@ -5469,6 +6070,7 @@
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
ssid->proto = WPA_PROTO_RSN;
@@ -5494,6 +6096,8 @@
wpa_s->show_group_started = 1;
wpa_s->p2p_in_invitation = 1;
wpa_s->p2p_invite_go_freq = freq;
+ wpa_s->p2p_go_group_formation_completed = 0;
+ wpa_s->global->p2p_group_formation = wpa_s;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
NULL);
@@ -5508,9 +6112,11 @@
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int force_freq, int neg_freq, int ht40,
- int vht, const struct p2p_channels *channels,
- int connection_timeout)
+ int force_freq, int neg_freq,
+ int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
+ const struct p2p_channels *channels,
+ int connection_timeout, int force_scan)
{
struct p2p_go_neg_results params;
int go = 0, freq;
@@ -5558,12 +6164,12 @@
} else {
freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
if (freq < 0 ||
- (freq > 0 && !freq_included(channels, freq)))
+ (freq > 0 && !freq_included(wpa_s, channels, freq)))
freq = 0;
}
} else if (ssid->mode == WPAS_MODE_INFRA) {
freq = neg_freq;
- if (freq <= 0 || !freq_included(channels, freq)) {
+ if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
struct os_reltime now;
struct wpa_bss *bss =
wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
@@ -5571,18 +6177,20 @@
os_get_reltime(&now);
if (bss &&
!os_reltime_expired(&now, &bss->last_update, 5) &&
- freq_included(channels, bss->freq))
+ freq_included(wpa_s, channels, bss->freq))
freq = bss->freq;
else
freq = 0;
}
- return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
+ return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
+ force_scan);
} else {
return -1;
}
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, channels))
return -1;
params.role_go = 1;
@@ -5734,6 +6342,8 @@
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
+ /* Complete group formation on successful data connection. */
+ wpa_s->p2p_go_group_formation_completed = 0;
} else if (ssid) {
/*
* Use a separate timeout for initial data connection to
@@ -5809,11 +6419,14 @@
u16 config_methods;
wpa_s->global->pending_p2ps_group = 0;
+ wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->pending_pd_use = NORMAL_PD;
if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
p2ps_prov->conncap = p2ps_group_capability(
- wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+ wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
+ &p2ps_prov->force_freq, &p2ps_prov->pref_freq);
+
wpa_printf(MSG_DEBUG,
"P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
__func__, p2ps_prov->conncap,
@@ -5874,7 +6487,8 @@
if (!offchannel_pending_action_tx(wpa_s))
return;
- wpas_p2p_action_tx_clear(wpa_s);
+ if (wpa_s->p2p_send_action_work)
+ wpas_p2p_free_send_action_work(wpa_s);
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
@@ -6105,13 +6719,15 @@
/* Invite to reinvoke a persistent group */
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int vht, int pref_freq)
+ int vht_center_freq2, int ht40, int vht, int max_chwidth,
+ int pref_freq)
{
enum p2p_invite_role role;
u8 *bssid = NULL;
int force_freq = 0;
int res;
int no_pref_freq_given = pref_freq == 0;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->global->p2p_invite_group = NULL;
if (peer_addr)
@@ -6121,6 +6737,9 @@
wpa_s->p2p_persistent_go_freq = freq;
wpa_s->p2p_go_ht40 = !!ht40;
+ wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_GO;
if (peer_addr == NULL) {
@@ -6145,10 +6764,13 @@
}
wpa_s->pending_invite_ssid_id = ssid->id;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_GO);
+ role == P2P_INVITE_ROLE_GO,
+ pref_freq_list, &size);
if (res)
return res;
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -6185,10 +6807,13 @@
int persistent;
int freq = 0, force_freq = 0, pref_freq = 0;
int res;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
wpa_s->p2p_go_vht = 0;
+ wpa_s->p2p_go_vht_center_freq2 = 0;
+ wpa_s->p2p_go_max_oper_chwidth = 0;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -6236,8 +6861,10 @@
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_ACTIVE_GO);
+ role == P2P_INVITE_ROLE_ACTIVE_GO,
+ pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@@ -6267,6 +6894,15 @@
return;
wpa_s->show_group_started = 0;
+ if (!wpa_s->p2p_go_group_formation_completed &&
+ wpa_s->global->p2p_group_formation == wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Marking group formation completed on client on data connection");
+ wpa_s->p2p_go_group_formation_completed = 1;
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+ wpa_s->p2p_in_invitation = 0;
+ }
os_memset(go_dev_addr, 0, ETH_ALEN);
if (ssid->bssid_set)
@@ -6782,14 +7418,22 @@
}
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig)
{
struct p2p_channels chan, cli_chan;
- struct wpa_supplicant *ifs;
+ struct wpa_used_freq_data *freqs = NULL;
+ unsigned int num = wpa_s->num_multichan_concurrent;
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
return;
+ freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+ if (!freqs)
+ return;
+
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
os_memset(&chan, 0, sizeof(chan));
os_memset(&cli_chan, 0, sizeof(cli_chan));
if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
@@ -6800,27 +7444,17 @@
p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- int freq;
- if (!ifs->current_ssid ||
- !ifs->current_ssid->p2p_group ||
- (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
- ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
- continue;
- freq = ifs->current_ssid->frequency;
- if (freq_included(&chan, freq)) {
- wpa_dbg(ifs, MSG_DEBUG,
- "P2P GO operating frequency %d MHz in valid range",
- freq);
- continue;
- }
+ wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
- wpa_dbg(ifs, MSG_DEBUG,
- "P2P GO operating in invalid frequency %d MHz", freq);
- /* TODO: Consider using CSA or removing the group within
- * wpa_supplicant */
- wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
- }
+ /*
+ * The used frequencies map changed, so it is possible that a GO is
+ * using a channel that is no longer valid for P2P use. It is also
+ * possible that due to policy consideration, it would be preferable to
+ * move it to a frequency already used by other station interfaces.
+ */
+ wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
+
+ os_free(freqs);
}
@@ -6891,6 +7525,7 @@
wpa_s->ifname);
found = 1;
wpas_p2p_group_formation_failed(wpa_s, 0);
+ break;
}
}
@@ -7119,10 +7754,12 @@
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth);
return ret;
}
@@ -7656,7 +8293,8 @@
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
- params->go_freq, -1, 0, 1, 1);
+ params->go_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
}
@@ -7732,7 +8370,8 @@
"connection handover");
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
}
@@ -7746,7 +8385,8 @@
"connection handover");
res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
if (res)
return res;
@@ -8080,6 +8720,16 @@
u8 curr_chan, cand, chan;
unsigned int i;
+ /*
+ * If possible, optimize the Listen channel to be a channel that is
+ * already used by one of the other interfaces.
+ */
+ if (!wpa_s->conf->p2p_optimize_listen_chan)
+ return;
+
+ if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+ return;
+
curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
for (i = 0, cand = 0; i < num; i++) {
ieee80211_freq_to_chan(freqs[i].freq, &chan);
@@ -8101,23 +8751,194 @@
}
-void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
{
- struct wpa_used_freq_data *freqs;
- unsigned int num = wpa_s->num_multichan_concurrent;
+ struct hostapd_config *conf;
+ struct p2p_go_neg_results params;
+ struct csa_settings csa_settings;
+ struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+ int old_freq = current_ssid->frequency;
+ int ret;
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
- return;
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+ return -1;
+ }
/*
- * If possible, optimize the Listen channel to be a channel that is
- * already used by one of the other interfaces.
+ * TODO: This function may not always work correctly. For example,
+ * when we have a running GO and a BSS on a DFS channel.
*/
- if (!wpa_s->conf->p2p_optimize_listen_chan)
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: Failed to select new frequency for GO");
+ return -1;
+ }
+
+ if (current_ssid->frequency == params.freq) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: Selected same frequency - not moving GO");
+ return 0;
+ }
+
+ conf = hostapd_config_defaults();
+ if (!conf) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: Failed to allocate default config");
+ return -1;
+ }
+
+ current_ssid->frequency = params.freq;
+ if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: Failed to create new GO config");
+ ret = -1;
+ goto out;
+ }
+
+ if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: CSA to a different band is not supported");
+ ret = -1;
+ goto out;
+ }
+
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = P2P_GO_CSA_COUNT;
+ csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
+ csa_settings.freq_params.freq = params.freq;
+ csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
+ csa_settings.freq_params.ht_enabled = conf->ieee80211n;
+ csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
+
+ if (conf->ieee80211ac) {
+ int freq1 = 0, freq2 = 0;
+ u8 chan, opclass;
+
+ if (ieee80211_freq_to_channel_ext(params.freq,
+ conf->secondary_channel,
+ conf->vht_oper_chwidth,
+ &opclass, &chan) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
+ ret = -1;
+ goto out;
+ }
+
+ if (conf->vht_oper_centr_freq_seg0_idx)
+ freq1 = ieee80211_chan_to_freq(
+ NULL, opclass,
+ conf->vht_oper_centr_freq_seg0_idx);
+
+ if (conf->vht_oper_centr_freq_seg1_idx)
+ freq2 = ieee80211_chan_to_freq(
+ NULL, opclass,
+ conf->vht_oper_centr_freq_seg1_idx);
+
+ if (freq1 < 0 || freq2 < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P CSA: Selected invalid VHT center freqs");
+ ret = -1;
+ goto out;
+ }
+
+ csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
+ csa_settings.freq_params.center_freq1 = freq1;
+ csa_settings.freq_params.center_freq2 = freq2;
+
+ switch (conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_80MHZ:
+ case VHT_CHANWIDTH_80P80MHZ:
+ csa_settings.freq_params.bandwidth = 80;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ csa_settings.freq_params.bandwidth = 160;
+ break;
+ }
+ }
+
+ ret = ap_switch_channel(wpa_s, &csa_settings);
+out:
+ current_ssid->frequency = old_freq;
+ hostapd_config_free(conf);
+ return ret;
+}
+
+
+static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_go_neg_results params;
+ struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
+ current_ssid->frequency);
+
+ /* Stop the AP functionality */
+ /* TODO: Should do this in a way that does not indicated to possible
+ * P2P Clients in the group that the group is terminated. */
+ wpa_supplicant_ap_deinit(wpa_s);
+
+ /* Reselect the GO frequency */
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+ return;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
+ params.freq);
+
+ if (params.freq &&
+ !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Selected freq (%u MHz) is not valid for P2P",
+ params.freq);
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+ return;
+ }
+
+ /* Update the frequency */
+ current_ssid->frequency = params.freq;
+ wpa_s->connect_without_scan = current_ssid;
+ wpa_s->reassociate = 1;
+ wpa_s->disconnected = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->ap_iface || !wpa_s->current_ssid)
return;
- if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+ wpas_p2p_go_update_common_freqs(wpa_s);
+
+ /* Do not move GO in the middle of a CSA */
+ if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: CSA is in progress - not moving GO");
return;
+ }
+
+ /*
+ * First, try a channel switch flow. If it is not supported or fails,
+ * take down the GO and bring it up again.
+ */
+ if (wpas_p2p_move_go_csa(wpa_s) < 0)
+ wpas_p2p_move_go_no_csa(wpa_s);
+}
+
+
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_used_freq_data *freqs = NULL;
+ unsigned int num = wpa_s->num_multichan_concurrent;
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
if (!freqs)
@@ -8125,11 +8946,187 @@
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
- wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+ /* Previous attempt to move a GO was not possible -- try again. */
+ wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
+ WPAS_P2P_CHANNEL_UPDATE_ANY);
+
os_free(freqs);
}
+/*
+ * Consider moving a GO from its currently used frequency:
+ * 1. It is possible that due to regulatory consideration the frequency
+ * can no longer be used and there is a need to evacuate the GO.
+ * 2. It is possible that due to MCC considerations, it would be preferable
+ * to move the GO to a channel that is currently used by some other
+ * station interface.
+ *
+ * In case a frequency that became invalid is once again valid, cancel a
+ * previously initiated GO frequency change.
+ */
+static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num)
+{
+ unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
+ unsigned int timeout;
+ int freq;
+
+ wpas_p2p_go_update_common_freqs(wpa_s);
+
+ freq = wpa_s->current_ssid->frequency;
+ for (i = 0, invalid_freq = 0; i < num; i++) {
+ if (freqs[i].freq == freq) {
+ flags = freqs[i].flags;
+
+ /* The channel is invalid, must change it */
+ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Freq=%d MHz no longer valid for GO",
+ freq);
+ invalid_freq = 1;
+ }
+ } else if (freqs[i].flags == 0) {
+ /* Freq is not used by any other station interface */
+ continue;
+ } else if (!p2p_supported_freq(wpa_s->global->p2p,
+ freqs[i].freq)) {
+ /* Freq is not valid for P2P use cases */
+ continue;
+ } else if (wpa_s->conf->p2p_go_freq_change_policy ==
+ P2P_GO_FREQ_MOVE_SCM) {
+ policy_move = 1;
+ } else if (wpa_s->conf->p2p_go_freq_change_policy ==
+ P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
+ wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+ policy_move = 1;
+ } else if ((wpa_s->conf->p2p_go_freq_change_policy ==
+ P2P_GO_FREQ_MOVE_SCM_ECSA) &&
+ wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+ if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
+ policy_move = 1;
+ } else if ((wpa_s->drv_flags &
+ WPA_DRIVER_FLAGS_AP_CSA) &&
+ wpas_p2p_go_clients_support_ecsa(wpa_s)) {
+ u8 chan;
+
+ /*
+ * We do not support CSA between bands, so move
+ * GO only within the same band.
+ */
+ if (wpa_s->ap_iface->current_mode->mode ==
+ ieee80211_freq_to_chan(freqs[i].freq,
+ &chan))
+ policy_move = 1;
+ }
+ }
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
+ invalid_freq, policy_move, flags);
+
+ /*
+ * The channel is valid, or we are going to have a policy move, so
+ * cancel timeout.
+ */
+ if (!invalid_freq || policy_move) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Cancel a GO move from freq=%d MHz", freq);
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+
+ if (wpas_p2p_in_progress(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ return;
+ }
+ }
+
+ if (!invalid_freq && (!policy_move || flags != 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Not initiating a GO frequency change");
+ return;
+ }
+
+ /*
+ * Do not consider moving GO if it is in the middle of a CSA. When the
+ * CSA is finished this flow should be retriggered.
+ */
+ if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Not initiating a GO frequency change - CSA is in progress");
+ return;
+ }
+
+ if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
+ timeout = P2P_GO_FREQ_CHANGE_TIME;
+ else
+ timeout = 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
+ freq, timeout);
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+ eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
+}
+
+
+static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num,
+ enum wpas_p2p_channel_update_trig trig)
+{
+ struct wpa_supplicant *ifs;
+
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
+ NULL);
+
+ /*
+ * Travers all the radio interfaces, and for each GO interface, check
+ * if there is a need to move the GO from the frequency it is using,
+ * or in case the frequency is valid again, cancel the evacuation flow.
+ */
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs->current_ssid == NULL ||
+ ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ /*
+ * The GO was just started or completed channel switch, no need
+ * to move it.
+ */
+ if (wpa_s == ifs &&
+ (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
+ trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move - schedule re-consideration");
+ eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ continue;
+ }
+
+ wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+ }
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+
+ wpas_p2p_update_channel_list(wpa_s,
+ WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
+}
+
+
void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
{
if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 1df34d0..21ee41f 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -17,6 +17,15 @@
struct wps_event_fail;
struct p2ps_provision;
+enum wpas_p2p_channel_update_trig {
+ WPAS_P2P_CHANNEL_UPDATE_ANY,
+ WPAS_P2P_CHANNEL_UPDATE_DRIVER,
+ WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
+ WPAS_P2P_CHANNEL_UPDATE_AVOID,
+ WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
+ WPAS_P2P_CHANNEL_UPDATE_CS,
+};
+
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
const char *conf_p2p_dev);
struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
@@ -26,17 +35,21 @@
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join,
- int auth, int go_intent, int freq, int persistent_id,
- int pd, int ht40, int vht);
+ int auth, int go_intent, int freq,
+ unsigned int vht_center_freq2, int persistent_id,
+ int pd, int ht40, int vht, unsigned int max_oper_chwidth);
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
int freq, struct wpa_ssid *ssid);
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40, int vht);
+ int freq, int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int force_freq, int neg_freq, int ht40,
- int vht, const struct p2p_channels *channels,
- int connection_timeout);
+ int force_freq, int neg_freq,
+ int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
+ const struct p2p_channels *channels,
+ int connection_timeout, int force_scan);
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
enum wpas_p2p_prov_disc_use {
@@ -90,7 +103,8 @@
const char *service);
int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
u32 adv_id, const char *adv_str, u8 svc_state,
- u16 config_methods, const char *svc_info);
+ u16 config_methods, const char *svc_info,
+ const u8 *cpt_priority);
int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
@@ -101,7 +115,8 @@
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int vht, int pref_freq);
+ int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth, int pref_freq);
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr);
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
@@ -130,6 +145,8 @@
struct hostapd_hw_modes *mode, u8 channel);
int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
const u8 *p2p_dev_addr,
@@ -160,7 +177,10 @@
unsigned int rx_freq, int ssi_signal);
void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
int registrar);
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig);
+
void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
int freq_24, int freq_5, int freq_overall);
void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
@@ -221,7 +241,9 @@
{
}
-static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+static inline void
+wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig)
{
}
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
index f4aa3e0..f8675e6 100644
--- a/wpa_supplicant/p2p_supplicant_sd.c
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -48,7 +48,7 @@
u8 *spos_tmp;
/* Offset */
- if (*spos + 2 > end) {
+ if (end - *spos < 2) {
wpa_printf(MSG_DEBUG, "P2P: No room for full "
"DNS offset field");
return -1;
@@ -74,14 +74,14 @@
return 0;
(*spos)++;
- if (*spos + len > end) {
+ if (len > end - *spos) {
wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
"sequence - no room for label with length "
"%u", len);
return -1;
}
- if (*upos + len + 2 > uend)
+ if (len + 2 > uend - *upos)
return -2;
os_memcpy(*upos, *spos, len);
@@ -722,11 +722,11 @@
if (resp == NULL)
return;
- while (pos + 1 < end) {
+ while (end - pos > 1) {
wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end || slen < 2) {
+ if (slen > end - pos || slen < 2) {
wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
"length");
wpabuf_free(resp);
@@ -827,10 +827,10 @@
u8 svc_len;
/* Sanity check fixed length+svc_str */
- if (pos + 6 >= tlv_end)
+ if (6 >= tlv_end - pos)
break;
svc_len = pos[6];
- if (pos + svc_len + 10 > tlv_end)
+ if (svc_len + 10 > tlv_end - pos)
break;
/* Advertisement ID */
@@ -917,13 +917,13 @@
}
}
- while (pos < end) {
+ while (end - pos >= 2) {
u8 srv_proto, srv_trans_id, status;
wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
slen = WPA_GET_LE16(pos);
pos += 2;
- if (pos + slen > end || slen < 3) {
+ if (slen > end - pos || slen < 3) {
wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
"length");
return;
@@ -1185,13 +1185,14 @@
int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
int auto_accept, u32 adv_id,
const char *adv_str, u8 svc_state,
- u16 config_methods, const char *svc_info)
+ u16 config_methods, const char *svc_info,
+ const u8 *cpt_priority)
{
int ret;
ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
adv_str, svc_state, config_methods,
- svc_info);
+ svc_info, cpt_priority);
if (ret == 0)
wpas_p2p_sd_service_update(wpa_s);
return ret;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index e81465c..3c3f9e0 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -267,13 +267,12 @@
int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params,
- int interval)
+ struct wpa_driver_scan_params *params)
{
int ret;
wpa_supplicant_notify_scanning(wpa_s, 1);
- ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+ ret = wpa_drv_sched_scan(wpa_s, params);
if (ret)
wpa_supplicant_notify_scanning(wpa_s, 0);
else
@@ -485,6 +484,12 @@
wpas_hs20_add_indication(extra_ie, -1);
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies &&
+ wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
+ wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
+#endif /* CONFIG_FST */
+
return extra_ie;
}
@@ -802,6 +807,9 @@
}
if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+#ifdef CONFIG_AP
+ !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
wpa_s->conf->ap_scan == 2) {
wpa_s->connect_without_scan = NULL;
wpa_s->prev_scan_wildcard = 0;
@@ -1173,6 +1181,7 @@
unsigned int max_sched_scan_ssids;
int wildcard = 0;
int need_ssids;
+ struct sched_scan_plan scan_plan;
if (!wpa_s->sched_scan_supported)
return -1;
@@ -1262,11 +1271,6 @@
if (!ssid || !wpa_s->prev_sched_ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
- if (wpa_s->conf->sched_scan_interval)
- wpa_s->sched_scan_interval =
- wpa_s->conf->sched_scan_interval;
- if (wpa_s->sched_scan_interval == 0)
- wpa_s->sched_scan_interval = 10;
wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
wpa_s->first_sched_scan = 1;
ssid = wpa_s->conf->ssid;
@@ -1351,14 +1355,51 @@
scan_params = ¶ms;
scan:
+ wpa_s->sched_scan_timed_out = 0;
+
+ /*
+ * We cannot support multiple scan plans if the scan request includes
+ * too many SSID's, so in this case use only the last scan plan and make
+ * it run infinitely. It will be stopped by the timeout.
+ */
+ if (wpa_s->sched_scan_plans_num == 1 ||
+ (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+ params.sched_scan_plans = wpa_s->sched_scan_plans;
+ params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+ } else if (wpa_s->sched_scan_plans_num > 1) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Too many SSIDs. Default to using single scheduled_scan plan");
+ params.sched_scan_plans =
+ &wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
+ 1];
+ params.sched_scan_plans_num = 1;
+ } else {
+ if (wpa_s->conf->sched_scan_interval)
+ scan_plan.interval = wpa_s->conf->sched_scan_interval;
+ else
+ scan_plan.interval = 10;
+
+ if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+ wpa_printf(MSG_WARNING,
+ "Scan interval too long(%u), use the maximum allowed(%u)",
+ scan_plan.interval,
+ wpa_s->max_sched_scan_plan_interval);
+ scan_plan.interval =
+ wpa_s->max_sched_scan_plan_interval;
+ }
+
+ scan_plan.iterations = 0;
+ params.sched_scan_plans = &scan_plan;
+ params.sched_scan_plans_num = 1;
+ }
+
if (ssid || !wpa_s->first_sched_scan) {
wpa_dbg(wpa_s, MSG_DEBUG,
- "Starting sched scan: interval %d timeout %d",
- wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+ "Starting sched scan: interval %u timeout %d",
+ params.sched_scan_plans[0].interval,
+ wpa_s->sched_scan_timeout);
} else {
- wpa_dbg(wpa_s, MSG_DEBUG,
- "Starting sched scan: interval %d (no timeout)",
- wpa_s->sched_scan_interval);
+ wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
}
wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1372,8 +1413,7 @@
}
}
- ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
- wpa_s->sched_scan_interval);
+ ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
wpabuf_free(extra_ie);
os_free(params.filter_ssids);
if (ret) {
@@ -1391,9 +1431,12 @@
wpa_s, NULL);
wpa_s->first_sched_scan = 0;
wpa_s->sched_scan_timeout /= 2;
- wpa_s->sched_scan_interval *= 2;
- if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
- wpa_s->sched_scan_interval = 10;
+ params.sched_scan_plans[0].interval *= 2;
+ if ((unsigned int) wpa_s->sched_scan_timeout <
+ params.sched_scan_plans[0].interval ||
+ params.sched_scan_plans[0].interval >
+ wpa_s->max_sched_scan_plan_interval) {
+ params.sched_scan_plans[0].interval = 10;
wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
}
}
@@ -1512,8 +1555,8 @@
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == ie)
return pos;
@@ -1541,8 +1584,8 @@
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1578,8 +1621,8 @@
pos += res->ie_len;
end = pos + res->beacon_ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1614,8 +1657,8 @@
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
+ while (end - pos > 1) {
+ if (2 + pos[1] > end - pos)
break;
if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1679,7 +1722,7 @@
snr_a_full = wa->snr;
snr_a = MIN(wa->snr, GREAT_SNR);
snr_b_full = wb->snr;
- snr_b = MIN(wa->snr, GREAT_SNR);
+ snr_b = MIN(wb->snr, GREAT_SNR);
} else {
/* Level is not in dBm, so we can't calculate
* SNR. Just use raw level (units unknown). */
@@ -2139,6 +2182,9 @@
wpa_s->scan_work = NULL;
radio_work_done(work);
}
+
+ if (wpa_s->wpa_state == WPA_SCANNING)
+ wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
}
@@ -2205,6 +2251,19 @@
params->only_new_results = src->only_new_results;
params->low_priority = src->low_priority;
+ if (src->sched_scan_plans_num > 0) {
+ params->sched_scan_plans =
+ os_malloc(sizeof(*src->sched_scan_plans) *
+ src->sched_scan_plans_num);
+ if (!params->sched_scan_plans)
+ goto failed;
+
+ os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
+ sizeof(*src->sched_scan_plans) *
+ src->sched_scan_plans_num);
+ params->sched_scan_plans_num = src->sched_scan_plans_num;
+ }
+
if (src->mac_addr_rand) {
params->mac_addr_rand = src->mac_addr_rand;
@@ -2242,6 +2301,7 @@
os_free((u8 *) params->extra_ies);
os_free(params->freqs);
os_free(params->filter_ssids);
+ os_free(params->sched_scan_plans);
/*
* Note: params->mac_addr_mask points to same memory allocation and
@@ -2255,10 +2315,11 @@
int wpas_start_pno(struct wpa_supplicant *wpa_s)
{
- int ret, interval, prio;
+ int ret, prio;
size_t i, num_ssid, num_match_ssid;
struct wpa_ssid *ssid;
struct wpa_driver_scan_params params;
+ struct sched_scan_plan scan_plan;
if (!wpa_s->sched_scan_supported)
return -1;
@@ -2352,8 +2413,20 @@
if (wpa_s->conf->filter_rssi)
params.filter_rssi = wpa_s->conf->filter_rssi;
- interval = wpa_s->conf->sched_scan_interval ?
- wpa_s->conf->sched_scan_interval : 10;
+ if (wpa_s->sched_scan_plans_num) {
+ params.sched_scan_plans = wpa_s->sched_scan_plans;
+ params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+ } else {
+ /* Set one scan plan that will run infinitely */
+ if (wpa_s->conf->sched_scan_interval)
+ scan_plan.interval = wpa_s->conf->sched_scan_interval;
+ else
+ scan_plan.interval = 10;
+
+ scan_plan.iterations = 0;
+ params.sched_scan_plans = &scan_plan;
+ params.sched_scan_plans_num = 1;
+ }
if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2368,7 +2441,7 @@
}
}
- ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval);
+ ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms);
os_free(params.filter_ssids);
if (ret == 0)
wpa_s->pno = 1;
@@ -2453,3 +2526,133 @@
wpa_s->mac_addr_rand_enable |= type;
return 0;
}
+
+
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->scan_work && wpa_s->own_scan_running) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
+ return wpa_drv_abort_scan(wpa_s);
+ }
+
+ return 0;
+}
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct sched_scan_plan *scan_plans = NULL;
+ const char *token, *context = NULL;
+ unsigned int num = 0;
+
+ if (!cmd)
+ return -1;
+
+ if (!cmd[0]) {
+ wpa_printf(MSG_DEBUG, "Clear sched scan plans");
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = NULL;
+ wpa_s->sched_scan_plans_num = 0;
+ return 0;
+ }
+
+ while ((token = cstr_token(cmd, " ", &context))) {
+ int ret;
+ struct sched_scan_plan *scan_plan, *n;
+
+ n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+ if (!n)
+ goto fail;
+
+ scan_plans = n;
+ scan_plan = &scan_plans[num];
+ num++;
+
+ ret = sscanf(token, "%u:%u", &scan_plan->interval,
+ &scan_plan->iterations);
+ if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+ wpa_printf(MSG_ERROR,
+ "Invalid sched scan plan input: %s", token);
+ goto fail;
+ }
+
+ if (!scan_plan->interval) {
+ wpa_printf(MSG_ERROR,
+ "scan plan %u: Interval cannot be zero",
+ num);
+ goto fail;
+ }
+
+ if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
+ wpa_printf(MSG_WARNING,
+ "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
+ num, scan_plan->interval,
+ wpa_s->max_sched_scan_plan_interval);
+ scan_plan->interval =
+ wpa_s->max_sched_scan_plan_interval;
+ }
+
+ if (ret == 1) {
+ scan_plan->iterations = 0;
+ break;
+ }
+
+ if (!scan_plan->iterations) {
+ wpa_printf(MSG_ERROR,
+ "scan plan %u: Number of iterations cannot be zero",
+ num);
+ goto fail;
+ }
+
+ if (scan_plan->iterations >
+ wpa_s->max_sched_scan_plan_iterations) {
+ wpa_printf(MSG_WARNING,
+ "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
+ num, scan_plan->iterations,
+ wpa_s->max_sched_scan_plan_iterations);
+ scan_plan->iterations =
+ wpa_s->max_sched_scan_plan_iterations;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "scan plan %u: interval=%u iterations=%u",
+ num, scan_plan->interval, scan_plan->iterations);
+ }
+
+ if (!scan_plans) {
+ wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+ goto fail;
+ }
+
+ if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+ wpa_printf(MSG_ERROR,
+ "All scan plans but the last must specify a number of iterations");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
+ num, scan_plans[num - 1].interval);
+
+ if (num > wpa_s->max_sched_scan_plans) {
+ wpa_printf(MSG_WARNING,
+ "Too many scheduled scan plans (only %u supported)",
+ wpa_s->max_sched_scan_plans);
+ wpa_printf(MSG_WARNING,
+ "Use only the first %u scan plans, and the last one (in infinite loop)",
+ wpa_s->max_sched_scan_plans - 1);
+ os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+ &scan_plans[num - 1], sizeof(*scan_plans));
+ num = wpa_s->max_sched_scan_plans;
+ }
+
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = scan_plans;
+ wpa_s->sched_scan_plans_num = num;
+
+ return 0;
+
+fail:
+ os_free(scan_plans);
+ wpa_printf(MSG_ERROR, "invalid scan plans list");
+ return -1;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 7650f5a..93ec9b3 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -40,8 +40,7 @@
struct wpa_scan_results *scan_res);
int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
- struct wpa_driver_scan_params *params,
- int interval);
+ struct wpa_driver_scan_params *params);
int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
struct wpa_driver_scan_params *
wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
@@ -54,5 +53,6 @@
int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
unsigned int type, const u8 *addr,
const u8 *mask);
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a472feb..2a3a728 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -438,6 +438,21 @@
}
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies) {
+ int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+ if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
+ sizeof(wpa_s->sme.assoc_req_ie)) {
+ os_memcpy(wpa_s->sme.assoc_req_ie +
+ wpa_s->sme.assoc_req_ie_len,
+ wpabuf_head(wpa_s->fst_ies),
+ fst_ies_len);
+ wpa_s->sme.assoc_req_ie_len += fst_ies_len;
+ }
+ }
+#endif /* CONFIG_FST */
+
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
sizeof(ext_capab));
if (ext_capab_len > 0) {
@@ -583,7 +598,8 @@
wpa_s->connect_work = work;
if (cwork->bss_removed ||
- !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+ !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
+ wpas_network_disabled(wpa_s, cwork->ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
@@ -616,6 +632,8 @@
radio_remove_works(wpa_s, "sme-connect", 0);
}
+ wpas_abort_ongoing_scan(wpa_s);
+
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
@@ -801,8 +819,22 @@
#endif /* CONFIG_SAE */
if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
- wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
- "code %d)", data->auth.status_code);
+ char *ie_txt = NULL;
+
+ if (data->auth.ies && data->auth.ies_len) {
+ size_t buflen = 2 * data->auth.ies_len + 1;
+ ie_txt = os_malloc(buflen);
+ if (ie_txt) {
+ wpa_snprintf_hex(ie_txt, buflen, data->auth.ies,
+ data->auth.ies_len);
+ }
+ }
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
+ " auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+ MAC2STR(data->auth.peer), data->auth.auth_type,
+ data->auth.auth_transaction, data->auth.status_code,
+ ie_txt);
+ os_free(ie_txt);
if (data->auth.status_code !=
WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index bfdee25..03ac507 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -2,6 +2,8 @@
Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
# NetworkManager users will probably want the dbus version instead.
diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
index 20ba0ad..c8a744d 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
@@ -2,6 +2,8 @@
Description=WPA supplicant daemon (interface- and wired driver-specific version)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
# NetworkManager users will probably want the dbus version instead.
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 10e62bc..7788b38 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -2,6 +2,8 @@
Description=WPA supplicant daemon (interface-specific version)
Requires=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
# NetworkManager users will probably want the dbus version instead.
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in
index 4351ad8..ea964ce 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -1,5 +1,7 @@
[Unit]
Description=WPA supplicant
+Before=network.target
+Wants=network.target
[Service]
Type=dbus
diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c
deleted file mode 100644
index 3bfbed5..0000000
--- a/wpa_supplicant/tests/link_test.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Dummy functions to allow link_test to be linked. The need for these
- * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to
- * be built outside hostapd.
- */
-
-#include "includes.h"
-
-#include "common.h"
-
-
-struct hostapd_data;
-struct sta_info;
-struct rsn_pmksa_cache_entry;
-struct eapol_state_machine;
-struct hostapd_eap_user;
-struct hostapd_bss_config;
-struct hostapd_vlan;
-
-
-struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
-{
- return NULL;
-}
-
-
-int ap_for_each_sta(struct hostapd_data *hapd,
- int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
- void *ctx),
- void *ctx)
-{
- return 0;
-}
-
-
-void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
- u32 session_timeout)
-{
-}
-
-
-int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
- int old_vlanid)
-{
- return 0;
-}
-
-
-void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
- int success)
-{
-}
-
-
-void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
- u8 *buf, size_t len)
-{
-}
-
-
-void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
-{
-}
-
-
-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
- struct eapol_state_machine *eapol)
-{
-}
-
-
-const struct hostapd_eap_user *
-hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
- size_t identity_len, int phase2)
-{
- return NULL;
-}
-
-
-const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
-{
- return NULL;
-}
diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c
deleted file mode 100644
index f60b182..0000000
--- a/wpa_supplicant/tests/test_eap_sim_common.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Test program for EAP-SIM PRF
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "eap_common/eap_sim_common.c"
-
-
-static int test_eap_sim_prf(void)
-{
- /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
- u8 xkey[] = {
- 0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
- 0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
- 0xeb, 0x5a, 0x38, 0xb6
- };
- u8 w[] = {
- 0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
- 0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
- 0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
- 0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
- 0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
- };
- u8 buf[40];
-
- printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
- eap_sim_prf(xkey, buf, sizeof(buf));
- if (memcmp(w, buf, sizeof(w)) != 0) {
- printf("eap_sim_prf failed\n");
- return 1;
- }
-
- return 0;
-}
-
-
-int main(int argc, char *argv[])
-{
- int errors = 0;
-
- errors += test_eap_sim_prf();
-
- return errors;
-}
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
deleted file mode 100644
index 39971f2..0000000
--- a/wpa_supplicant/tests/test_wpa.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Test program for combined WPA authenticator/supplicant
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "../config.h"
-#include "rsn_supp/wpa.h"
-#include "rsn_supp/wpa_ie.h"
-#include "ap/wpa_auth.h"
-
-
-struct wpa {
- u8 auth_addr[ETH_ALEN];
- u8 supp_addr[ETH_ALEN];
- u8 psk[PMK_LEN];
-
- /* from authenticator */
- u8 auth_eapol_dst[ETH_ALEN];
- u8 *auth_eapol;
- size_t auth_eapol_len;
-
- /* from supplicant */
- u8 *supp_eapol;
- size_t supp_eapol_len;
-
- struct wpa_sm *supp;
- struct wpa_authenticator *auth_group;
- struct wpa_state_machine *auth;
-
- struct wpa_ssid ssid;
- u8 supp_ie[80];
- size_t supp_ie_len;
-};
-
-
-static int supp_get_bssid(void *ctx, u8 *bssid)
-{
- struct wpa *wpa = ctx;
- wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
- os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
- return 0;
-}
-
-
-static void supp_set_state(void *ctx, enum wpa_states state)
-{
- wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
-}
-
-
-static void auth_eapol_rx(void *eloop_data, void *user_ctx)
-{
- struct wpa *wpa = eloop_data;
-
- wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
- wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
- wpa->supp_eapol_len);
-}
-
-
-static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
- size_t len)
-{
- struct wpa *wpa = ctx;
-
- wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
- "len=%lu)",
- __func__, MAC2STR(dest), proto, (unsigned long) len);
-
- os_free(wpa->supp_eapol);
- wpa->supp_eapol = os_malloc(len);
- if (wpa->supp_eapol == NULL)
- return -1;
- os_memcpy(wpa->supp_eapol, buf, len);
- wpa->supp_eapol_len = len;
- eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
-
- return 0;
-}
-
-
-static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
- u16 data_len, size_t *msg_len, void **data_pos)
-{
- struct ieee802_1x_hdr *hdr;
-
- wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
- __func__, type, data_len);
-
- *msg_len = sizeof(*hdr) + data_len;
- hdr = os_malloc(*msg_len);
- if (hdr == NULL)
- return NULL;
-
- hdr->version = 2;
- hdr->type = type;
- hdr->length = host_to_be16(data_len);
-
- if (data)
- os_memcpy(hdr + 1, data, data_len);
- else
- os_memset(hdr + 1, 0, data_len);
-
- if (data_pos)
- *data_pos = hdr + 1;
-
- return (u8 *) hdr;
-}
-
-
-static int supp_get_beacon_ie(void *ctx)
-{
- struct wpa *wpa = ctx;
- const u8 *ie;
- size_t ielen;
-
- wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-
- ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen);
- if (ie == NULL || ielen < 1)
- return -1;
- if (ie[0] == WLAN_EID_RSN)
- return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
- return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
-}
-
-
-static int supp_set_key(void *ctx, enum wpa_alg alg,
- const u8 *addr, int key_idx, int set_tx,
- const u8 *seq, size_t seq_len,
- const u8 *key, size_t key_len)
-{
- wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
- "set_tx=%d)",
- __func__, alg, MAC2STR(addr), key_idx, set_tx);
- wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
- wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
- return 0;
-}
-
-
-static int supp_mlme_setprotection(void *ctx, const u8 *addr,
- int protection_type, int key_type)
-{
- wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
- "key_type=%d)",
- __func__, MAC2STR(addr), protection_type, key_type);
- return 0;
-}
-
-
-static void supp_cancel_auth_timeout(void *ctx)
-{
- wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-}
-
-
-static int supp_init(struct wpa *wpa)
-{
- struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
- if (ctx == NULL)
- return -1;
-
- ctx->ctx = wpa;
- ctx->msg_ctx = wpa;
- ctx->set_state = supp_set_state;
- ctx->get_bssid = supp_get_bssid;
- ctx->ether_send = supp_ether_send;
- ctx->get_beacon_ie = supp_get_beacon_ie;
- ctx->alloc_eapol = supp_alloc_eapol;
- ctx->set_key = supp_set_key;
- ctx->mlme_setprotection = supp_mlme_setprotection;
- ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
- wpa->supp = wpa_sm_init(ctx);
- if (wpa->supp == NULL) {
- wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
- return -1;
- }
-
- wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
- wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
- wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
- wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
- wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
- wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
- wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN);
-
- wpa->supp_ie_len = sizeof(wpa->supp_ie);
- if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
- &wpa->supp_ie_len) < 0) {
- wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
- " failed");
- return -1;
- }
-
- wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
-
- return 0;
-}
-
-
-static void auth_logger(void *ctx, const u8 *addr, logger_level level,
- const char *txt)
-{
- if (addr)
- wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
- MAC2STR(addr), txt);
- else
- wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
-}
-
-
-static void supp_eapol_rx(void *eloop_data, void *user_ctx)
-{
- struct wpa *wpa = eloop_data;
-
- wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
- wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
- wpa->auth_eapol_len);
-}
-
-
-static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
- size_t data_len, int encrypt)
-{
- struct wpa *wpa = ctx;
-
- wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
- "encrypt=%d)",
- __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
-
- os_free(wpa->auth_eapol);
- wpa->auth_eapol = os_malloc(data_len);
- if (wpa->auth_eapol == NULL)
- return -1;
- os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN);
- os_memcpy(wpa->auth_eapol, data, data_len);
- wpa->auth_eapol_len = data_len;
- eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
-
- return 0;
-}
-
-
-static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
-{
- struct wpa *wpa = ctx;
- wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
- __func__, MAC2STR(addr), prev_psk);
- if (prev_psk)
- return NULL;
- return wpa->psk;
-}
-
-
-static int auth_init_group(struct wpa *wpa)
-{
- struct wpa_auth_config conf;
- struct wpa_auth_callbacks cb;
-
- wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
-
- os_memset(&conf, 0, sizeof(conf));
- conf.wpa = 2;
- conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
- conf.wpa_pairwise = WPA_CIPHER_CCMP;
- conf.rsn_pairwise = WPA_CIPHER_CCMP;
- conf.wpa_group = WPA_CIPHER_CCMP;
- conf.eapol_version = 2;
-
- os_memset(&cb, 0, sizeof(cb));
- cb.ctx = wpa;
- cb.logger = auth_logger;
- cb.send_eapol = auth_send_eapol;
- cb.get_psk = auth_get_psk;
-
- wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb);
- if (wpa->auth_group == NULL) {
- wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
- return -1;
- }
-
- return 0;
-}
-
-
-static int auth_init(struct wpa *wpa)
-{
- wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
- if (wpa->auth == NULL) {
- wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
- return -1;
- }
-
- if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie,
- wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) {
- wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
- return -1;
- }
-
- wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
-
- wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
-
- return 0;
-}
-
-
-static void deinit(struct wpa *wpa)
-{
- wpa_auth_sta_deinit(wpa->auth);
- wpa_sm_deinit(wpa->supp);
- wpa_deinit(wpa->auth_group);
- os_free(wpa->auth_eapol);
- wpa->auth_eapol = NULL;
- os_free(wpa->supp_eapol);
- wpa->supp_eapol = NULL;
-}
-
-
-int main(int argc, char *argv[])
-{
- struct wpa wpa;
-
- if (os_program_init())
- return -1;
-
- os_memset(&wpa, 0, sizeof(wpa));
- os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
- os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
- os_memset(wpa.psk, 0x44, PMK_LEN);
-
- wpa_debug_level = 0;
- wpa_debug_show_keys = 1;
-
- if (eloop_init()) {
- wpa_printf(MSG_ERROR, "Failed to initialize event loop");
- return -1;
- }
-
- if (auth_init_group(&wpa) < 0)
- return -1;
-
- if (supp_init(&wpa) < 0)
- return -1;
-
- if (auth_init(&wpa) < 0)
- return -1;
-
- wpa_printf(MSG_DEBUG, "Starting eloop");
- eloop_run();
- wpa_printf(MSG_DEBUG, "eloop done");
-
- deinit(&wpa);
-
- eloop_destroy();
-
- os_program_deinit();
-
- return 0;
-}
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7d79499..e1596cb 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -37,12 +37,14 @@
/* set the TFS IE to driver */
static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
- const u8 *addr, u8 *buf, u16 *buf_len,
+ const u8 *addr, const u8 *buf, u16 buf_len,
enum wnm_oper oper)
{
+ u16 len = buf_len;
+
wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
- return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+ return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
}
@@ -137,6 +139,8 @@
if (res < 0)
wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
"(action=%d, intval=%d)", action, intval);
+ else
+ wpa_s->wnmsleep_used = 1;
os_free(wnmsleep_ie);
os_free(wnmtfs_ie);
@@ -147,8 +151,8 @@
static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
- u8 *tfsresp_ie_start,
- u8 *tfsresp_ie_end)
+ const u8 *tfsresp_ie_start,
+ const u8 *tfsresp_ie_end)
{
wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
wpa_s->bssid, NULL, NULL);
@@ -164,7 +168,7 @@
/* pass the TFS Resp IE(s) to driver for processing */
if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
tfsresp_ie_start,
- &tfsresp_ie_len,
+ tfsresp_ie_len,
WNM_SLEEP_TFS_RESP_IE_SET))
wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
}
@@ -193,8 +197,8 @@
return;
}
- while (ptr + 1 < end) {
- if (ptr + 2 + ptr[1] > end) {
+ while (end - ptr > 1) {
+ if (2 + ptr[1] > end - ptr) {
wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
"length");
if (end > ptr) {
@@ -245,14 +249,20 @@
* Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
* WNM-Sleep Mode IE | TFS Response IE
*/
- u8 *pos = (u8 *) frm; /* point to payload after the action field */
+ const u8 *pos = frm; /* point to payload after the action field */
u16 key_len_total;
struct wnm_sleep_element *wnmsleep_ie = NULL;
/* multiple TFS Resp IE (assuming consecutive) */
- u8 *tfsresp_ie_start = NULL;
- u8 *tfsresp_ie_end = NULL;
+ const u8 *tfsresp_ie_start = NULL;
+ const u8 *tfsresp_ie_end = NULL;
size_t left;
+ if (!wpa_s->wnmsleep_used) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association");
+ return;
+ }
+
if (len < 3)
return;
key_len_total = WPA_GET_LE16(frm + 1);
@@ -265,14 +275,14 @@
return;
}
pos += 3 + key_len_total;
- while (pos - frm < len) {
+ while (pos - frm + 1 < len) {
u8 ie_len = *(pos + 1);
- if (pos + 2 + ie_len > frm + len) {
+ if (2 + ie_len > frm + len - pos) {
wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
break;
}
wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
- if (*pos == WLAN_EID_WNMSLEEP)
+ if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
wnmsleep_ie = (struct wnm_sleep_element *) pos;
else if (*pos == WLAN_EID_TFS_RESP) {
if (!tfsresp_ie_start)
@@ -643,6 +653,7 @@
if (bss == wpa_s->current_bss) {
wpa_printf(MSG_DEBUG,
"WNM: Already associated with the preferred candidate");
+ wnm_deallocate_memory(wpa_s);
return 1;
}
@@ -796,7 +807,7 @@
unsigned int beacon_int;
u8 valid_int;
- if (pos + 5 > end)
+ if (end - pos < 5)
return;
if (wpa_s->current_bss)
@@ -819,7 +830,7 @@
pos += 5;
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
- if (pos + 12 > end) {
+ if (end - pos < 12) {
wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
return;
}
@@ -830,7 +841,7 @@
if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
char url[256];
- if (pos + 1 > end || pos + 1 + pos[0] > end) {
+ if (end - pos < 1 || 1 + pos[0] > end - pos) {
wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
"Management Request (URL)");
return;
@@ -866,7 +877,7 @@
if (wpa_s->wnm_neighbor_report_elements == NULL)
return;
- while (pos + 2 <= end &&
+ while (end - pos >= 2 &&
wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
{
u8 tag = *pos++;
@@ -874,7 +885,7 @@
wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
tag);
- if (pos + len > end) {
+ if (len > end - pos) {
wpa_printf(MSG_DEBUG, "WNM: Truncated request");
return;
}
@@ -977,7 +988,7 @@
pos = data;
end = data + len;
- while (pos + 1 < end) {
+ while (end - pos > 1) {
ie = *pos++;
ie_len = *pos++;
wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
@@ -1015,7 +1026,7 @@
url = NULL;
osu_method = 1;
} else {
- if (pos + url_len + 1 > ie_end) {
+ if (url_len + 1 > ie_end - pos) {
wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
url_len,
(int) (ie_end - pos));
@@ -1054,7 +1065,7 @@
"Imminent - Reason Code %u "
"Re-Auth Delay %u URL Length %u",
code, reauth_delay, url_len);
- if (pos + url_len > ie_end)
+ if (url_len > ie_end - pos)
break;
url = os_malloc(url_len + 1);
if (url == NULL)
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index c5d8333..275bf39 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -66,6 +66,12 @@
"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n";
+#define VENDOR_ELEM_FRAME_ID \
+ " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
+ "3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \
+ "7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \
+ "11: Assoc Req (P2P), 12: Assoc Resp (P2P)"
+
static struct wpa_ctrl *ctrl_conn;
static struct wpa_ctrl *mon_conn;
static int wpa_cli_quit = 0;
@@ -76,6 +82,7 @@
#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
#endif /* CONFIG_CTRL_IFACE_DIR */
static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
@@ -107,7 +114,9 @@
{
printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
"[-a<action file>] \\\n"
- " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
+ " [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
+ "\\\n"
+ " [-s<wpa_client_socket_file_path>] "
"[command..]\n"
" -h = help (show this usage text)\n"
" -v = shown version information\n"
@@ -330,6 +339,13 @@
}
#endif /* ANDROID */
+ if (client_socket_dir && client_socket_dir[0] &&
+ access(client_socket_dir, F_OK) < 0) {
+ perror(client_socket_dir);
+ os_free(cfile);
+ return -1;
+ }
+
if (cfile == NULL) {
flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
cfile = os_malloc(flen);
@@ -343,14 +359,14 @@
}
}
- ctrl_conn = wpa_ctrl_open(cfile);
+ ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
if (ctrl_conn == NULL) {
os_free(cfile);
return -1;
}
if (attach && interactive)
- mon_conn = wpa_ctrl_open(cfile);
+ mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
else
mon_conn = NULL;
os_free(cfile);
@@ -502,6 +518,10 @@
return wpa_ctrl_command(ctrl, "STATUS-WPS");
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
+#ifdef ANDROID
+ if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
+ return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
+#endif /* ANDROID */
return wpa_ctrl_command(ctrl, "STATUS");
}
@@ -618,6 +638,7 @@
"eapol_version", "ap_scan", "bgscan",
#ifdef CONFIG_MESH
"user_mpm", "max_peer_links", "mesh_max_inactivity",
+ "dot11RSNASAERetransPeriod",
#endif /* CONFIG_MESH */
"disable_scan_offload", "fast_reauth", "opensc_engine_path",
"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
@@ -1533,7 +1554,7 @@
"ssid", "scan_ssid", "bssid", "bssid_blacklist",
"bssid_whitelist", "psk", "proto", "key_mgmt",
"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
- "freq_list",
+ "freq_list", "max_oper_chwidth",
#ifdef IEEE8021X_EAPOL
"eap", "identity", "anonymous_identity", "password", "ca_cert",
"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1749,6 +1770,13 @@
}
+static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "ABORT_SCAN");
+}
+
+
static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
@@ -1851,14 +1879,15 @@
/*
* INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
- * <driver_param>TAB<bridge_name>[TAB<create>]
+ * <driver_param>TAB<bridge_name>[TAB<create>[TAB<type>]]
*/
res = os_snprintf(cmd, sizeof(cmd),
- "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
+ "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
argv[0],
argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
- argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "");
+ argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "",
+ argc > 7 ? argv[7] : "");
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
@@ -2462,6 +2491,27 @@
return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
}
+
+static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv);
+}
+
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
@@ -2801,6 +2851,13 @@
}
+static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -2979,6 +3036,9 @@
{ "scan_results", wpa_cli_cmd_scan_results, NULL,
cli_cmd_flag_none,
"= get latest scan results" },
+ { "abort_scan", wpa_cli_cmd_abort_scan, NULL,
+ cli_cmd_flag_none,
+ "= request ongoing scan to be aborted" },
{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<<idx> | <bssid>> = get detailed scan result info" },
@@ -2995,8 +3055,10 @@
{ "interface_add", wpa_cli_cmd_interface_add, NULL,
cli_cmd_flag_none,
"<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
- " <bridge_name> = adds new interface, all parameters but <ifname>\n"
- " are optional" },
+ " <bridge_name> <create> <type> = adds new interface, all "
+ "parameters but\n"
+ " <ifname> are optional. Supported types are station ('sta') and "
+ "AP ('ap')" },
{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
cli_cmd_flag_none,
"<ifname> = removes the interface" },
@@ -3226,6 +3288,18 @@
{ "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<address|iface=address> = remove a peer from all groups" },
+ { "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL,
+ cli_cmd_flag_none,
+ "<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n"
+ VENDOR_ELEM_FRAME_ID },
+ { "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL,
+ cli_cmd_flag_none,
+ "<frame id> = get vendor specific IE(s) to frame(s)\n"
+ VENDOR_ELEM_FRAME_ID },
+ { "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL,
+ cli_cmd_flag_none,
+ "<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n"
+ VENDOR_ELEM_FRAME_ID },
#endif /* CONFIG_P2P */
#ifdef CONFIG_WIFI_DISPLAY
{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
@@ -3355,6 +3429,9 @@
"<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
"mask=mac-address-mask] = scan MAC randomization"
},
+ { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
+ cli_cmd_flag_none,
+ "<interface type> = retrieve preferred freq list for the specified interface type" },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
@@ -3585,6 +3662,9 @@
const char *ifname = ctrl_ifname;
char ifname_buf[100];
+ if (eloop_terminated())
+ return;
+
pos = msg;
if (os_strncmp(pos, "IFNAME=", 7) == 0) {
const char *end;
@@ -4166,18 +4246,17 @@
{
char *ifname = NULL;
+#ifdef ANDROID
+ char ifprop[PROPERTY_VALUE_MAX];
+ if (property_get("wifi.interface", ifprop, NULL) != 0) {
+ ifname = os_strdup(ifprop);
+ printf("Using interface '%s'\n", ifname ? ifname : "N/A");
+ }
+#else /* ANDROID */
#ifdef CONFIG_CTRL_IFACE_UNIX
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
if (!dir) {
-#ifdef ANDROID
- char ifprop[PROPERTY_VALUE_MAX];
- if (property_get("wifi.interface", ifprop, NULL) != 0) {
- ifname = os_strdup(ifprop);
- printf("Using interface '%s'\n", ifname);
- return ifname;
- }
-#endif /* ANDROID */
return NULL;
}
while ((dent = readdir(dir))) {
@@ -4221,6 +4300,7 @@
}
wpa_ctrl_close(ctrl);
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+#endif /* ANDROID */
return ifname;
}
@@ -4237,7 +4317,7 @@
return -1;
for (;;) {
- c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
+ c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
if (c < 0)
break;
switch (c) {
@@ -4269,6 +4349,9 @@
case 'P':
pid_file = optarg;
break;
+ case 's':
+ client_socket_dir = optarg;
+ break;
default:
usage();
return -1;
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 3bd3a9c..a0aa05e 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -639,7 +639,7 @@
void WpaGui::updateNetworks()
{
- char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+ char buf[4096], *start, *end, *id, *ssid, *bssid, *flags;
size_t len;
int first_active = -1;
int was_selected = -1;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 4611a1d..850ec40 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -31,6 +31,7 @@
const struct wpa_driver_ops *driver;
void *drv_priv;
+ void *drv_global_priv;
struct sockaddr_un drv_addr;
int wpas_registered;
@@ -48,6 +49,10 @@
if (iface->driver->deinit)
iface->driver->deinit(iface->drv_priv);
iface->drv_priv = NULL;
+ if (iface->drv_global_priv) {
+ iface->driver->global_deinit(iface->drv_global_priv);
+ iface->drv_global_priv = NULL;
+ }
iface->wpas_registered = 0;
}
@@ -58,10 +63,24 @@
iface->l2 = NULL;
}
- if (iface->driver->init == NULL)
+ if (iface->driver->init2) {
+ if (iface->driver->global_init) {
+ iface->drv_global_priv = iface->driver->global_init();
+ if (!iface->drv_global_priv) {
+ wpa_printf(MSG_INFO,
+ "Failed to initialize driver global context");
+ return;
+ }
+ } else {
+ iface->drv_global_priv = NULL;
+ }
+ iface->drv_priv = iface->driver->init2(iface, iface->ifname,
+ iface->drv_global_priv);
+ } else if (iface->driver->init) {
+ iface->drv_priv = iface->driver->init(iface, iface->ifname);
+ } else {
return;
-
- iface->drv_priv = iface->driver->init(iface, iface->ifname);
+ }
if (iface->drv_priv == NULL) {
wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
return;
@@ -87,6 +106,10 @@
if (iface->driver->deinit)
iface->driver->deinit(iface->drv_priv);
iface->drv_priv = NULL;
+ if (iface->drv_global_priv) {
+ iface->driver->global_deinit(iface->drv_global_priv);
+ iface->drv_global_priv = NULL;
+ }
iface->wpas_registered = 0;
}
}
@@ -172,6 +195,58 @@
}
+static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
+ void *buf, size_t len)
+{
+ struct wpa_driver_auth_params params;
+ struct privsep_cmd_authenticate *auth;
+ int res, i;
+
+ if (iface->drv_priv == NULL || iface->driver->authenticate == NULL)
+ return;
+
+ if (len < sizeof(*auth)) {
+ wpa_printf(MSG_DEBUG, "Invalid authentication request");
+ return;
+ }
+
+ auth = buf;
+ if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+ wpa_printf(MSG_DEBUG, "Authentication request overflow");
+ return;
+ }
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.freq = auth->freq;
+ params.bssid = auth->bssid;
+ params.ssid = auth->ssid;
+ if (auth->ssid_len > SSID_MAX_LEN)
+ return;
+ params.ssid_len = auth->ssid_len;
+ params.auth_alg = auth->auth_alg;
+ for (i = 0; i < 4; i++) {
+ if (auth->wep_key_len[i]) {
+ params.wep_key[i] = auth->wep_key[i];
+ params.wep_key_len[i] = auth->wep_key_len[i];
+ }
+ }
+ params.wep_tx_keyidx = auth->wep_tx_keyidx;
+ params.local_state_change = auth->local_state_change;
+ params.p2p = auth->p2p;
+ if (auth->ie_len) {
+ params.ie = (u8 *) (auth + 1);
+ params.ie_len = auth->ie_len;
+ }
+ if (auth->sae_data_len) {
+ params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
+ params.sae_data_len = auth->sae_data_len;
+ }
+
+ res = iface->driver->authenticate(iface->drv_priv, ¶ms);
+ wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res);
+}
+
+
static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
void *buf, size_t len)
{
@@ -307,6 +382,10 @@
iface->driver->get_capa(iface->drv_priv, &capa) < 0)
goto fail;
+ /* For now, no support for passing extended_capa pointers */
+ capa.extended_capa = NULL;
+ capa.extended_capa_mask = NULL;
+ capa.extended_capa_len = 0;
sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
sizeof(*from));
return;
@@ -356,7 +435,8 @@
}
proto = reg_cmd[0];
- if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+ if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
+ proto != ETH_P_80211_ENCAP) {
wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
"ethertype 0x%x", proto);
return;
@@ -529,6 +609,9 @@
pos[cmd_len] = '\0';
wpa_priv_cmd_set_country(iface, pos);
break;
+ case PRIVSEP_CMD_AUTHENTICATE:
+ wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len);
+ break;
}
}
@@ -698,6 +781,36 @@
}
+static void wpa_priv_send_auth(struct wpa_priv_interface *iface,
+ union wpa_event_data *data)
+{
+ size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len;
+ struct privsep_event_auth *auth;
+ u8 *buf, *pos;
+
+ buf = os_malloc(buflen);
+ if (buf == NULL)
+ return;
+
+ auth = (struct privsep_event_auth *) buf;
+ pos = (u8 *) (auth + 1);
+
+ os_memcpy(auth->peer, data->auth.peer, ETH_ALEN);
+ os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN);
+ auth->auth_type = data->auth.auth_type;
+ auth->auth_transaction = data->auth.auth_transaction;
+ auth->status_code = data->auth.status_code;
+ if (data->auth.ies) {
+ os_memcpy(pos, data->auth.ies, data->auth.ies_len);
+ auth->ies_len = data->auth.ies_len;
+ }
+
+ wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen);
+
+ os_free(buf);
+}
+
+
static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
union wpa_event_data *data)
{
@@ -851,6 +964,10 @@
&data->michael_mic_failure.unicast,
sizeof(int));
break;
+ case EVENT_SCAN_STARTED:
+ wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL,
+ 0);
+ break;
case EVENT_SCAN_RESULTS:
wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
0);
@@ -874,9 +991,12 @@
case EVENT_FT_RESPONSE:
wpa_priv_send_ft_response(iface, data);
break;
+ case EVENT_AUTH:
+ wpa_priv_send_auth(iface, data);
+ break;
default:
- wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
- event);
+ wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO",
+ event, event_to_string(event));
break;
}
}
@@ -944,8 +1064,9 @@
"contributors\n"
"\n"
"usage:\n"
- " wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
- "[driver:ifname ...]\n");
+ " wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] "
+ "<driver:ifname> \\\n"
+ " [driver:ifname ...]\n");
}
@@ -982,20 +1103,20 @@
break;
default:
usage();
- goto out;
+ goto out2;
}
}
if (optind >= argc) {
usage();
- goto out;
+ goto out2;
}
wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
- goto out;
+ goto out2;
}
for (i = optind; i < argc; i++) {
@@ -1025,7 +1146,9 @@
eloop_destroy();
- os_daemonize_terminate(pid_file);
+out2:
+ if (daemonize)
+ os_daemonize_terminate(pid_file);
os_free(pid_file);
os_program_deinit();
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 722294d..7e5c07a 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -35,6 +35,7 @@
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "p2p/p2p.h"
+#include "fst/fst.h"
#include "blacklist.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
@@ -544,6 +545,10 @@
}
wmm_ac_notify_disassoc(wpa_s);
+
+ wpa_s->sched_scan_plans_num = 0;
+ os_free(wpa_s->sched_scan_plans);
+ wpa_s->sched_scan_plans = NULL;
}
@@ -962,6 +967,11 @@
wpa_supplicant_terminate_proc(global);
}
}
+
+ if (wpa_debug_reopen_file() < 0) {
+ /* Ignore errors since we cannot really do much to fix this */
+ wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+ }
}
@@ -1149,6 +1159,10 @@
return -1;
}
+#ifdef CONFIG_NO_WPA
+ wpa_s->group_cipher = WPA_CIPHER_NONE;
+ wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
sel = ie.group_cipher & ssid->group_cipher;
wpa_s->group_cipher = wpa_pick_group_cipher(sel);
if (wpa_s->group_cipher < 0) {
@@ -1168,6 +1182,7 @@
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
@@ -1585,6 +1600,8 @@
struct wpa_connect_work *cwork;
int rand_style;
+ wpa_s->own_disconnect_req = 0;
+
if (ssid->mac_addr == -1)
rand_style = wpa_s->conf->mac_addr;
else
@@ -1693,6 +1710,8 @@
return;
}
+ wpas_abort_ongoing_scan(wpa_s);
+
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
@@ -1714,6 +1733,36 @@
}
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+ const struct wpa_ssid *ssid)
+{
+ enum hostapd_hw_mode hw_mode;
+ struct hostapd_hw_modes *mode = NULL;
+ u8 channel;
+ int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+ if (ssid->disable_ht)
+ return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+ hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+ if (hw_mode == NUM_HOSTAPD_MODES)
+ return 0;
+ for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == hw_mode) {
+ mode = &wpa_s->hw.modes[i];
+ break;
+ }
+ }
+
+ if (!mode)
+ return 0;
+
+ return mode->vht_capab != 0;
+}
+
+
void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
struct hostapd_freq_params *freq)
@@ -1726,8 +1775,10 @@
struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
u8 channel;
int i, chan_idx, ht40 = -1, res, obss_scan = 1;
- unsigned int j;
+ unsigned int j, k;
struct hostapd_freq_params vht_freq;
+ int chwidth, seg0, seg1;
+ u32 vht_caps = 0;
freq->freq = ssid->frequency;
@@ -1877,12 +1928,12 @@
"IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
freq->channel, freq->sec_channel_offset);
- /* Not sure if mesh is ready for VHT */
- if (ssid->mode != WPAS_MODE_IBSS)
+ if (!drv_supports_vht(wpa_s, ssid))
return;
/* For IBSS check VHT_IBSS flag */
- if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+ if (ssid->mode == WPAS_MODE_IBSS &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
return;
vht_freq = *freq;
@@ -1913,12 +1964,45 @@
return;
}
+ chwidth = VHT_CHANWIDTH_80MHZ;
+ seg0 = vht80[j] + 6;
+ seg1 = 0;
+
+ if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+ /* setup center_freq2, bandwidth */
+ for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+ /* Only accept 80 MHz segments separated by a gap */
+ if (j == k || abs(vht80[j] - vht80[k]) == 16)
+ continue;
+ for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = VHT_CHANWIDTH_80P80MHZ;
+ vht_caps |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ seg1 = vht80[k] + 6;
+ }
+
+ if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+ break;
+ }
+ }
+
if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
freq->channel, freq->ht_enabled,
vht_freq.vht_enabled,
freq->sec_channel_offset,
- VHT_CHANWIDTH_80MHZ,
- vht80[j] + 6, 0, 0) != 0)
+ chwidth, seg0, seg1, vht_caps) != 0)
return;
*freq = vht_freq;
@@ -1966,7 +2050,8 @@
wpa_s->connect_work = work;
- if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+ if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+ wpas_network_disabled(wpa_s, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
@@ -2015,7 +2100,9 @@
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
- wpa_supplicant_cancel_sched_scan(wpa_s);
+ if (!wpa_s->pno)
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
wpa_supplicant_cancel_scan(wpa_s);
/* Starting new association, so clear the possibly used WPA IE from the
@@ -2188,6 +2275,18 @@
}
}
+#ifdef CONFIG_FST
+ if (wpa_s->fst_ies) {
+ int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+ if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+ os_memcpy(wpa_ie + wpa_ie_len,
+ wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+ wpa_ie_len += fst_ies_len;
+ }
+ }
+#endif /* CONFIG_FST */
+
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
@@ -2558,15 +2657,20 @@
} else
wpa_supplicant_enable_one_network(wpa_s, ssid);
- if (wpa_s->reassociate && !wpa_s->disconnected) {
+ if (wpa_s->reassociate && !wpa_s->disconnected &&
+ (!wpa_s->current_ssid ||
+ wpa_s->wpa_state == WPA_DISCONNECTED ||
+ wpa_s->wpa_state == WPA_SCANNING)) {
if (wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
"new network to scan filters");
wpa_supplicant_cancel_sched_scan(wpa_s);
}
- if (wpa_supplicant_fast_associate(wpa_s) != 1)
+ if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
+ }
}
}
@@ -2667,7 +2771,8 @@
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
}
- if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+ if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+ wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/* We are already associated with the selected network */
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected network - do nothing");
@@ -2694,8 +2799,10 @@
wpa_s->reassociate = 1;
if (wpa_s->connect_without_scan ||
- wpa_supplicant_fast_associate(wpa_s) != 1)
+ wpa_supplicant_fast_associate(wpa_s) != 1) {
+ wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+ }
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
@@ -2770,6 +2877,11 @@
if (ap_scan < 0 || ap_scan > 2)
return -1;
+ if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
#ifdef ANDROID
if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
wpa_s->wpa_state >= WPA_ASSOCIATING &&
@@ -3275,6 +3387,12 @@
}
}
+ if (wpa_s->conf->ap_scan == 2 &&
+ os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+ wpa_printf(MSG_INFO,
+ "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+ }
+
wpa_clear_keys(wpa_s, NULL);
/* Make sure that TKIP countermeasures are not left enabled (could
@@ -3700,6 +3818,124 @@
}
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return (is_zero_ether_addr(wpa_s->bssid) ||
+ wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+ enum hostapd_hw_mode *hw_mode,
+ u8 *channel)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ if (wpa_s->current_bss) {
+ *hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+ channel);
+ } else if (wpa_s->hw.num_modes) {
+ *hw_mode = wpa_s->hw.modes[0].mode;
+ } else {
+ WPA_ASSERT(0);
+ *hw_mode = 0;
+ }
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *modes = wpa_s->hw.modes;
+ return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+ wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+ return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(data), wpabuf_len(data),
+ 0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+ return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+ const u8 *buf, size_t size)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+ struct mb_ies_info info;
+
+ WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+ if (!mb_ies_info_by_ies(&info, buf, size)) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = mb_ies_by_info(&info);
+ }
+}
+
+
+const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ *get_ctx = NULL;
+ if (!is_zero_ether_addr(wpa_s->bssid))
+ return (wpa_s->received_mb_ies || !mb_only) ?
+ wpa_s->bssid : NULL;
+ return NULL;
+}
+
+
+const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
+ Boolean mb_only)
+{
+ return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+ struct fst_wpa_obj *iface_obj)
+{
+ iface_obj->ctx = wpa_s;
+ iface_obj->get_bssid = wpas_fst_get_bssid_cb;
+ iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+ iface_obj->get_hw_modes = wpas_fst_get_hw_modes;
+ iface_obj->set_ies = wpas_fst_set_ies_cb;
+ iface_obj->send_action = wpas_fst_send_action_cb;
+ iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb;
+ iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb;
+ iface_obj->get_peer_first = wpas_fst_get_peer_first;
+ iface_obj->get_peer_next = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
const struct wpa_driver_capa *capa)
{
@@ -3718,6 +3954,56 @@
}
+static enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+ if (freq < 3000)
+ return BAND_2_4_GHZ;
+ if (freq > 50000)
+ return BAND_60_GHZ;
+ return BAND_5_GHZ;
+}
+
+
+static unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s,
+ const int *freqs)
+{
+ int i;
+ unsigned int band = 0;
+
+ if (freqs) {
+ /* freqs are specified for the radio work */
+ for (i = 0; freqs[i]; i++)
+ band |= wpas_freq_to_band(freqs[i]);
+ } else {
+ /*
+ * freqs are not specified, implies all
+ * the supported freqs by HW
+ */
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].num_channels != 0) {
+ if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211B ||
+ wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211G)
+ band |= BAND_2_4_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211A)
+ band |= BAND_5_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211AD)
+ band |= BAND_60_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211ANY)
+ band = BAND_2_4_GHZ | BAND_5_GHZ |
+ BAND_60_GHZ;
+ }
+ }
+ }
+
+ return band;
+}
+
+
static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
const char *rn)
{
@@ -3770,11 +4056,103 @@
}
#endif /* CONFIG_P2P */
+ if (work->started) {
+ work->wpa_s->radio->num_active_works--;
+ wpa_dbg(work->wpa_s, MSG_DEBUG,
+ "radio_work_free('%s'@%p: num_active_works --> %u",
+ work->type, work,
+ work->wpa_s->radio->num_active_works);
+ }
+
dl_list_del(&work->list);
os_free(work);
}
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+ struct wpa_radio_work *active_work = NULL;
+ struct wpa_radio_work *tmp;
+
+ /* Get the active work to know the type and band. */
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started) {
+ active_work = tmp;
+ break;
+ }
+ }
+
+ if (!active_work) {
+ /* No active work, start one */
+ radio->num_active_works = 0;
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+ return tmp;
+ }
+ return NULL;
+ }
+
+ if (os_strcmp(active_work->type, "sme-connect") == 0 ||
+ os_strcmp(active_work->type, "connect") == 0) {
+ /*
+ * If the active work is either connect or sme-connect,
+ * do not parallelize them with other radio works.
+ */
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "Do not parallelize radio work with %s",
+ active_work->type);
+ return NULL;
+ }
+
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started)
+ continue;
+
+ /*
+ * If connect or sme-connect are enqueued, parallelize only
+ * those operations ahead of them in the queue.
+ */
+ if (os_strcmp(tmp->type, "connect") == 0 ||
+ os_strcmp(tmp->type, "sme-connect") == 0)
+ break;
+
+ /*
+ * Check that the radio works are distinct and
+ * on different bands.
+ */
+ if (os_strcmp(active_work->type, tmp->type) != 0 &&
+ (active_work->bands != tmp->bands)) {
+ /*
+ * If a scan has to be scheduled through nl80211 scan
+ * interface and if an external scan is already running,
+ * do not schedule the scan since it is likely to get
+ * rejected by kernel.
+ */
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "active_work:%s new_work:%s",
+ active_work->type, tmp->type);
+ return tmp;
+ }
+ }
+
+ /* Did not find a radio work to schedule in parallel. */
+ return NULL;
+}
+
+
static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_radio *radio = eloop_ctx;
@@ -3783,26 +4161,48 @@
struct wpa_supplicant *wpa_s;
work = dl_list_first(&radio->work, struct wpa_radio_work, list);
- if (work == NULL)
- return;
-
- if (work->started)
- return; /* already started and still in progress */
-
- wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
- radio_list);
- if (wpa_s && wpa_s->radio->external_scan_running) {
- wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ if (work == NULL) {
+ radio->num_active_works = 0;
return;
}
+ wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+ radio_list);
+
+ if (!(wpa_s &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+ if (work->started)
+ return; /* already started and still in progress */
+
+ if (wpa_s && wpa_s->radio->external_scan_running) {
+ wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ return;
+ }
+ } else {
+ work = NULL;
+ if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+ /* get the work to schedule next */
+ work = radio_work_get_next_work(radio);
+ }
+ if (!work)
+ return;
+ }
+
+ wpa_s = work->wpa_s;
os_get_reltime(&now);
os_reltime_sub(&now, &work->time, &diff);
- wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting radio work '%s'@%p after %ld.%06ld second wait",
work->type, work, diff.sec, diff.usec);
work->started = 1;
work->time = now;
+ radio->num_active_works++;
+
work->cb(work, 0);
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+ radio->num_active_works < MAX_ACTIVE_WORKS)
+ radio_work_check_next(wpa_s);
}
@@ -3910,6 +4310,7 @@
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx)
{
+ struct wpa_radio *radio = wpa_s->radio;
struct wpa_radio_work *work;
int was_empty;
@@ -3924,6 +4325,16 @@
work->cb = cb;
work->ctx = ctx;
+ if (freq)
+ work->bands = wpas_freq_to_band(freq);
+ else if (os_strcmp(type, "scan") == 0 ||
+ os_strcmp(type, "p2p-scan") == 0)
+ work->bands = wpas_get_bands(wpa_s,
+ ((struct wpa_driver_scan_params *)
+ ctx)->freqs);
+ else
+ work->bands = wpas_get_bands(wpa_s, NULL);
+
was_empty = dl_list_empty(&wpa_s->radio->work);
if (next)
dl_list_add(&wpa_s->radio->work, &work->list);
@@ -3932,6 +4343,12 @@
if (was_empty) {
wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
radio_work_check_next(wpa_s);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+ && radio->num_active_works < MAX_ACTIVE_WORKS) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Try to schedule a radio work (num_active_works=%u)",
+ radio->num_active_works);
+ radio_work_check_next(wpa_s);
}
return 0;
@@ -4187,6 +4604,11 @@
wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
wpa_s->max_scan_ssids = capa.max_scan_ssids;
wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+ wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+ wpa_s->max_sched_scan_plan_interval =
+ capa.max_sched_scan_plan_interval;
+ wpa_s->max_sched_scan_plan_iterations =
+ capa.max_sched_scan_plan_iterations;
wpa_s->sched_scan_supported = capa.sched_scan_supported;
wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -4238,6 +4660,28 @@
return -1;
}
+#ifdef CONFIG_FST
+ if (wpa_s->conf->fst_group_id) {
+ struct fst_iface_cfg cfg;
+ struct fst_wpa_obj iface_obj;
+
+ fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+ os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+ sizeof(cfg.group_id));
+ cfg.priority = wpa_s->conf->fst_priority;
+ cfg.llt = wpa_s->conf->fst_llt;
+
+ wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+ &iface_obj, &cfg);
+ if (!wpa_s->fst) {
+ wpa_msg(wpa_s, MSG_ERROR,
+ "FST: Cannot attach iface %s to group %s",
+ wpa_s->ifname, cfg.group_id);
+ return -1;
+ }
+ }
+#endif /* CONFIG_FST */
+
if (wpas_wps_init(wpa_s))
return -1;
@@ -4304,6 +4748,8 @@
wpas_rrm_reset(wpa_s);
+ wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
return 0;
}
@@ -4346,6 +4792,17 @@
wpas_ctrl_radio_work_flush(wpa_s);
radio_remove_interface(wpa_s);
+#ifdef CONFIG_FST
+ if (wpa_s->fst) {
+ fst_detach(wpa_s->fst);
+ wpa_s->fst = NULL;
+ }
+ if (wpa_s->received_mb_ies) {
+ wpabuf_free(wpa_s->received_mb_ies);
+ wpa_s->received_mb_ies = NULL;
+ }
+#endif /* CONFIG_FST */
+
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
@@ -4578,6 +5035,33 @@
#endif /* CONFIG_NO_WPA_MSG */
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_global *global = eloop_ctx;
+ struct wpa_supplicant *wpa_s;
+
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+ if (global->p2p)
+ p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+ for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+ ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+ }
+}
+
+
/**
* wpa_supplicant_init - Initialize %wpa_supplicant
* @params: Parameters for %wpa_supplicant
@@ -4652,9 +5136,11 @@
if (params->override_ctrl_interface)
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_P2P
if (params->conf_p2p_dev)
global->params.conf_p2p_dev =
os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
wpa_debug_show_keys = global->params.wpa_debug_show_keys =
@@ -4704,6 +5190,9 @@
}
#endif /* CONFIG_WIFI_DISPLAY */
+ eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+ wpas_periodic, global, NULL);
+
return global;
}
@@ -4755,6 +5244,8 @@
if (global == NULL)
return;
+ eloop_cancel_timeout(wpas_periodic, global, NULL);
+
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_deinit(global);
#endif /* CONFIG_WIFI_DISPLAY */
@@ -4791,7 +5282,9 @@
os_free(global->params.ctrl_interface_group);
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_P2P
os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
os_free(global->p2p_disallow_freq.range);
os_free(global->p2p_go_avoid_freq.range);
@@ -4821,6 +5314,9 @@
if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
wpas_init_ext_pw(wpa_s);
+ if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+ wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
@@ -5330,7 +5826,8 @@
continue;
if (ifs->current_ssid->mode == WPAS_MODE_AP ||
- ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+ ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+ ifs->current_ssid->mode == WPAS_MODE_MESH)
freq = ifs->current_ssid->frequency;
else if (wpa_drv_get_bssid(ifs, bssid) == 0)
freq = ifs->assoc_freq;
@@ -5346,7 +5843,7 @@
freqs_data[idx++].freq = freq;
if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
- freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+ freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
WPA_FREQ_USED_BY_P2P_CLIENT :
WPA_FREQ_USED_BY_INFRA_STATION;
}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d380965..2ce1cc4 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -108,12 +108,35 @@
# the driver reports successful association; each network block should have
# explicit security policy (i.e., only one option in the lists) for
# key_mgmt, pairwise, group, proto variables
+# Note: ap_scan=2 should not be used with the nl80211 driver interface (the
+# current Linux interface). ap_scan=1 is optimized work working with nl80211.
+# For finding networks using hidden SSID, scan_ssid=1 in the network block can
+# be used with nl80211.
# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
# created immediately regardless of scan results. ap_scan=1 mode will first try
# to scan for existing networks and only if no matches with the enabled
# networks are found, a new IBSS or AP mode network is created.
ap_scan=1
+# Whether to force passive scan for network connection
+#
+# By default, scans will send out Probe Request frames on channels that allow
+# active scanning. This advertise the local station to the world. Normally this
+# is fine, but users may wish to do passive scanning where the radio should only
+# listen quietly for Beacon frames and not send any Probe Request frames. Actual
+# functionality may be driver dependent.
+#
+# This parameter can be used to force only passive scanning to be used
+# for network connection cases. It should be noted that this will slow
+# down scan operations and reduce likelihood of finding the AP. In
+# addition, some use cases will override this due to functional
+# requirements, e.g., for finding an AP that uses hidden SSID
+# (scan_ssid=1) or P2P device discovery.
+#
+# 0: Do normal scans (allow active scans) (default)
+# 1: Do passive scans.
+#passive_scan=0
+
# MPM residency
# By default, wpa_supplicant implements the mesh peering manager (MPM) for an
# open mesh. However, if the driver can implement the MPM, you may set this to
@@ -291,7 +314,9 @@
# up to the limit of 300 seconds (3, 9, 27 ... 300)
# For periodic module, parameters would be <fixed interval>
#autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan.
+# Note: If sched_scan_plans are configured and supported by the driver,
+# autoscan is ignored.
# filter_ssids - SSID-based scan result filtering
# 0 = do not filter scan results (default)
@@ -593,6 +618,27 @@
# Hotspot 2.0
# hs20=1
+# Scheduled scan plans
+#
+# A space delimited list of scan plans. Each scan plan specifies the scan
+# interval and number of iterations, delimited by a colon. The last scan plan
+# will run infinitely and thus must specify only the interval and not the number
+# of iterations.
+#
+# The driver advertises the maximum number of scan plans supported. If more scan
+# plans than supported are configured, only the first ones are set (up to the
+# maximum supported). The last scan plan that specifies only the interval is
+# always set as the last plan.
+#
+# If the scan interval or the number of iterations for a scan plan exceeds the
+# maximum supported, it will be set to the maximum supported value.
+#
+# Format:
+# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
+#
+# Example:
+# sched_scan_plans=10:100 20:200 30
+
# network block
#
# Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -986,6 +1032,7 @@
# EAP workarounds are disabled with eap_workaround=0.
# For EAP-FAST, this must be set to 0 (or left unconfigured for the
# default value to be used automatically).
+# tls_disable_tlsv1_0=1 - disable use of TLSv1.0
# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
# that have issues interoperating with updated TLS version)
# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
@@ -1136,6 +1183,32 @@
# 2: MCS 0-9
# 3: not supported
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 msec.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
# Example blocks:
# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4f63456..a8b273b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -225,7 +225,7 @@
* This can also be %NULL. In such a case, if a P2P Device dedicated
* interfaces is created, the main configuration file will be used.
*/
- const char *conf_p2p_dev;
+ char *conf_p2p_dev;
#endif /* CONFIG_P2P */
};
@@ -278,6 +278,7 @@
unsigned int p2p_24ghz_social_channels:1;
unsigned int pending_p2ps_group:1;
unsigned int pending_group_iface_for_p2ps:1;
+ unsigned int pending_p2ps_group_freq;
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
@@ -300,10 +301,19 @@
char name[16]; /* from driver_ops get_radio_name() or empty if not
* available */
unsigned int external_scan_running:1;
+ unsigned int num_active_works;
struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
struct dl_list work; /* struct wpa_radio_work::list entries */
};
+#define MAX_ACTIVE_WORKS 2
+
+enum wpa_radio_work_band {
+ BAND_2_4_GHZ = BIT(0),
+ BAND_5_GHZ = BIT(1),
+ BAND_60_GHZ = BIT(2),
+};
+
/**
* struct wpa_radio_work - Radio work item
*/
@@ -316,6 +326,7 @@
void *ctx;
unsigned int started:1;
struct os_reltime time;
+ unsigned int bands;
};
int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
@@ -500,9 +511,10 @@
struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
int sched_scan_timeout;
- int sched_scan_interval;
int first_sched_scan;
int sched_scan_timed_out;
+ struct sched_scan_plan *sched_scan_plans;
+ size_t sched_scan_plans_num;
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
@@ -593,6 +605,8 @@
} scan_req, last_scan_req;
enum wpa_states scan_prev_wpa_state;
struct os_reltime scan_trigger_time, scan_start_time;
+ /* Minimum freshness requirement for connection purposes */
+ struct os_reltime scan_min_time;
int scan_runs; /* number of scan runs since WPS was started */
int *next_scan_freqs;
int *manual_scan_freqs;
@@ -632,6 +646,9 @@
int max_scan_ssids;
int max_sched_scan_ssids;
+ unsigned int max_sched_scan_plans;
+ unsigned int max_sched_scan_plan_interval;
+ unsigned int max_sched_scan_plan_iterations;
int sched_scan_supported;
unsigned int max_match_sets;
unsigned int max_remain_on_chan;
@@ -656,6 +673,7 @@
unsigned int reattach:1; /* reassociation to the same BSS requested */
unsigned int mac_addr_changed:1;
unsigned int added_vif:1;
+ unsigned int wnmsleep_used:1;
struct os_reltime last_mac_addr_change;
int last_mac_addr_style;
@@ -720,6 +738,7 @@
int mesh_if_idx;
unsigned int mesh_if_created:1;
unsigned int mesh_ht_enabled:1;
+ unsigned int mesh_vht_enabled:1;
int mesh_auth_block_duration; /* sec */
#endif /* CONFIG_MESH */
@@ -820,7 +839,7 @@
unsigned int p2p_nfc_tag_enabled:1;
unsigned int p2p_peer_oob_pk_hash_known:1;
unsigned int p2p_disable_ip_addr_req:1;
- unsigned int p2ps_join_addr_valid:1;
+ unsigned int p2ps_method_config_any:1;
unsigned int p2p_cli_probe:1;
int p2p_persistent_go_freq;
int p2p_persistent_id;
@@ -842,6 +861,9 @@
int *p2p_group_common_freqs;
unsigned int p2p_group_common_freqs_num;
u8 p2ps_join_addr[ETH_ALEN];
+
+ unsigned int p2p_go_max_oper_chwidth;
+ unsigned int p2p_go_vht_center_freq2;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;
@@ -967,6 +989,7 @@
struct l2_packet_data *l2_test;
unsigned int extra_roc_dur;
enum wpa_supplicant_test_failure test_failure;
+ unsigned int p2p_go_csa_on_inv:1;
#endif /* CONFIG_TESTING_OPTIONS */
struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -977,6 +1000,12 @@
u8 last_tspecs_count;
struct rrm_data rrm;
+
+#ifdef CONFIG_FST
+ struct fst_iface *fst;
+ const struct wpabuf *fst_ies;
+ struct wpabuf *received_mb_ies;
+#endif /* CONFIG_FST */
};
@@ -1149,4 +1178,16 @@
int *freq_array, unsigned int len);
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
+
+#ifdef CONFIG_FST
+
+struct fst_wpa_obj;
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+ struct fst_wpa_obj *iface_obj);
+
+#endif /* CONFIG_FST */
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
+
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 29c22ba..7986695 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1013,7 +1013,6 @@
wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
}
-#endif /* CONFIG_NO_WPA */
static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
@@ -1028,6 +1027,7 @@
else
return 0;
}
+#endif /* CONFIG_NO_WPA */
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
@@ -1124,6 +1124,7 @@
}
}
#endif /* CONFIG_P2P */
+ conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
}
wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
}
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 2db7914..5c674b2 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -683,6 +683,13 @@
}
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+ return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb,
+ wpa_s, NULL);
+}
+
+
static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
{
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
@@ -955,8 +962,20 @@
static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
+ union wps_event_data data;
+
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
"out");
+ os_memset(&data, 0, sizeof(data));
+ data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
+ data.fail.error_indication = WPS_EI_NO_ERROR;
+ /*
+ * Call wpas_notify_wps_event_fail() directly instead of through
+ * wpa_supplicant_wps_event() which would end up registering unnecessary
+ * timeouts (those are only for the case where the failure happens
+ * during an EAP-WSC exchange).
+ */
+ wpas_notify_wps_event_fail(wpa_s, &data.fail);
wpas_clear_wps(wpa_s);
}
@@ -1236,6 +1255,22 @@
}
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+ union wps_event_data data;
+
+ os_memset(&data, 0, sizeof(data));
+ data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+ data.fail.error_indication = WPS_EI_NO_ERROR;
+ /*
+ * Call wpas_notify_wps_event_fail() directly instead of through
+ * wpa_supplicant_wps_event() which would end up registering unnecessary
+ * timeouts (those are only for the case where the failure happens
+ * during an EAP-WSC exchange).
+ */
+ wpas_notify_wps_event_fail(wpa_s, &data.fail);
+}
+
/* Cancel the wps pbc/pin requests */
int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
{
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 683bd50..c8fe47e 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -33,6 +33,7 @@
int p2p_group);
int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, int p2p_group, u16 dev_pw_id);
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);
int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, struct wps_new_ap_settings *settings);
@@ -84,6 +85,7 @@
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
#else /* CONFIG_WPS */
@@ -146,6 +148,12 @@
{
}
+static inline int
+wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
#endif /* CONFIG_WPS */
#endif /* WPS_SUPPLICANT_H */