Cumulative patch from commit f10ff62e4eda7917a8e28fe492fd98723a5e97c4
f10ff62 Describe preferred mechanism for submitting contributions
fcdb359 Use "STATUS-NO_EVENTS" instead of "STATUS" in get_wpa_status function
73ed03f wpa_supplicant: Add GTK RSC relaxation workaround
ea6030c Restore previous wpa_state in scan-only result handler
1e74ae4 WNM: Clear BSS TM data if already associated with preferred candidate
d129b02 EAP-pwd: Add support for Brainpool Elliptic Curves
a34eace dbus: Remove unused dict helper functions
cdcb2d0 wpa_cli: Add support for vendor_elem_* commands
17b7032 EAP peer: Clear ignore flag in INITIALIZE state
1f1e619 Add test programs for checking libwpa_client linking
736b7cb wpa_supplicant/Makefile: Fix libwpa_client build
2e38079 TLS: Fix memory leak with multiple TLS server instances
7b0f550 eap_sim_db: Implement eap_sim_db_expire_pending()
45c3e72 Add frequency to operating class determination for 5 GHz 100..140
e50c50d dbus: Expose interface globals via D-Bus properties
1aa0fb7 dbus: Pass property description to getters/setters
c93b7e1 RSN: Check result of EAPOL-Key frame send request
95be79f Allow -1 as value to disable frag_threshold
bc50bb0 Extend the range of values for the RTS threshold
053693d hostapd: Add feature to start all interfaces at the same time in sync
9578413 Reserve QCA vendor specific nl80211 commands 110..114
5d4c508 Assign QCA commands and attributes for Tx power scaling and OTA testing
5d1d69a P2P: Filter control chars in group client device name similarly to peer
f67d1a0 TDLS: Do not send error case of TPK M3 if TX fails
1248e58 wpa_supplicant: Reopen debug log file upon receipt of SIGHUP signal
d8fd633 Do not write ERROR level log entries if debug file is not used
67deaa5 l2_packet: Add build option to disable Linux packet socket workaround
fa46426 RSN: Do not try to connect if PMF disabled and AP requires it
8acbe7f WNM: Verify WNM Sleep Mode element length
dacd789 WNM: Mark set TFS buffer const
...
f24b979 OpenSSL: Merge error returns
84d6a17 TLS: Remove unused tls_capabilities()
7867227 ms_funcs: Merge similar return cases
3596361 hw_features: Merge similar return case in check_40mhz_2g4()
aac1efe Reject the initial 4-way handshake if initial GTK setup fails
2da5256 Add backtrace-based error path testing mechanism
55413ce P2P: Do not allow 40 MHz co-ex PRI/SEC switch to force MCC
Next patches were skipped due to explicit cherry-pick:
bddc51e RSN: Stop connection attempt on apparent PMK mismatch
3fdaaa8 Throttle control interface event message bursts
a530fe7 Add wpa_supplicant EVENT_TEST control interface command
ee1e3f5 hostapd: Global control interface notifications
2e95cfc Add debug prints for wpa_supplicant ctrl_iface socket send operations
ce7d0eb Update AP WPA/RSN IE on all associations if driver can select BSS
844dfeb QCA vendor command support to set band to driver
Change-Id: I909996c5afcd3b5d123ea1e23c0e1212021f7625
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
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..656f0a7 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)
@@ -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..5f65b7d 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,19 @@
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_wb_chsw_wrapper(hapd, pos);
}
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -524,8 +553,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 +568,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 +675,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 +832,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 +877,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 +951,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 +965,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 +1032,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 +1055,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 +1083,20 @@
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_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..37537b3 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,6 +18,7 @@
#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"
@@ -42,10 +43,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 +59,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 +90,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);
@@ -155,13 +156,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 +182,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 +203,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 +256,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 +289,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 +306,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 +329,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 +402,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 +412,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 +435,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 +447,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 +461,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 +492,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 +699,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 +735,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 +746,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 +763,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 +821,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 +839,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 +858,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 +885,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 +899,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 +926,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 +989,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 +1124,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..fc8786d 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;
}
@@ -726,6 +732,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..a2dd132 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -49,9 +49,12 @@
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);
+
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 +64,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..531a67d 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -399,19 +399,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 +611,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 +626,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..b66f244 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -24,5 +24,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..8d2c428 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -131,6 +131,59 @@
}
+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;
+}
+
+
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..68fdb72 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)
{
@@ -1709,15 +1715,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 +2020,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 +2097,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 +2181,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 +2575,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