[wpa_supplicant] Cumulative patch from c4e90da6d
Bug: 124017368
Test: Device boots up and connects to WPA3/OWE wifi networks, run traffic.
Test: Able to turn on/off softap, associate wifi STA, run traffic.
Test: DPP functional test.
Test: Regression test passed (Bug: 124052942)
c4e90da6d MBO: Move the WNM-Notification subtype definitions to common location
105b14f54 HS 2.0: Update the T&C Acceptance subtype value
65b487ae5 HS 2.0: Add QUIET=1 support for building hs20-osu-client
73f285dad Add FT-PSK to GET_CAPABILITY key_mgmt
6110753b1 nl80211: Clear PMKID add command message buffer
0fa33e05b nl80211: Clear connect command message buffer
b14e8ea1d nl80211: Request kernel to trim off payload of netlink requests from acks
789b48bb4 EAP peer: Clear temporary message buffers before freeing
8f99a3c26 Clear config item writing buffer before freeing it
a68e9b698 D-Bus: Fix P2P DeleteService dict iteration
0607346f1 D-Bus: Fix a memory leak in DeleteService handler
d05dda61d PEAP: Explicitly clear temporary keys from memory when using CMK
4e1cd3468 EAP-PEAP: Derive EMSK and use 128-octet derivation for MSK
d8c20ec59 DPP: Clear dpp_listen_freq on remain-on-channel failure
59fa20538 P2P: Allow the avoid channels for P2P discovery/negotiation
e34cd9f06 WNM: Fix WNM-Sleep Mode Request bounds checking
159a7fbde crl_reload_interval: Add CRL reloading support
83c860813 AP: Add wpa_psk_file reloading in runtime
ec5c39a55 AP: Allow identifying which passphrase station used with wpa_psk_file
b08c9ad0c AP: Expose PMK outside of wpa_auth module
89896c000 tests: Use python3 compatible print statement
bab493b90 tests: Use python3 compatible "except" statement
0dab47733 Write multi_ap_backhaul_sta to wpa_supplicant config
98251c6f2 dbus: Document more possible BSS/RSA/KeyMgmt values
1e591df06 Check supported types in wpas_mac_addr_rand_scan_set()
c85249aa1 Fix test compilation error related to sme_event_unprot_disconnect()
42d308635 SAE: Advertise Password Identifier use
59c693064 HS 2.0 server: Command line option to fetch the version information
2d1762fa4 HS 2.0 server: Alternative subrem updateNode for certificate credentials
d97cf2a11 HS 2.0 server: Use noMOUpdate in client certificate subrem
13a200a92 FILS: Remove notes about experimental implementation
86d4e0537 dbus: Expose support of SAE key management in BSS properties
Change-Id: I83ffca34ff5349c226db6215ff1ae35c3b7ab335
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 6d4c041..3b45075 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -13,6 +13,7 @@
#include "utils/common.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "hostapd.h"
@@ -362,7 +363,7 @@
}
-static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
+static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
157, 184, 192 };
@@ -376,7 +377,7 @@
}
-static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 36, 52, 100, 116, 132, 149 };
unsigned int i;
@@ -389,6 +390,19 @@
}
+static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 100 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
+ if (chan->chan == allowed[i])
+ return 1;
+
+ return 0;
+}
+
+
static int acs_survey_is_sufficient(struct freq_survey *survey)
{
if (!(survey->filled & SURVEY_HAS_NF)) {
@@ -565,6 +579,7 @@
long double factor, ideal_factor = 0;
int i, j;
int n_chans = 1;
+ u32 bw;
unsigned int k;
/* TODO: HT40- support */
@@ -579,16 +594,23 @@
iface->conf->secondary_channel)
n_chans = 2;
- if (iface->conf->ieee80211ac &&
- iface->conf->vht_oper_chwidth == 1)
- n_chans = 4;
+ if (iface->conf->ieee80211ac) {
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ }
+ }
- /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+ bw = num_chan_to_bw(n_chans);
- wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
- n_chans == 1 ? 20 :
- n_chans == 2 ? 40 :
- 80);
+ /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
+
+ wpa_printf(MSG_DEBUG,
+ "ACS: Survey analysis for selected bandwidth %d MHz", bw);
for (i = 0; i < iface->current_mode->num_channels; i++) {
double total_weight;
@@ -596,12 +618,23 @@
chan = &iface->current_mode->channels[i];
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ /* Since in the current ACS implementation the first channel is
+ * always a primary channel, skip channels not available as
+ * primary until more sophisticated channel selection is
+ * implemented. */
+ if (!chan_pri_allowed(chan))
continue;
if (!is_in_chanlist(iface, chan))
continue;
+ if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: BW %u is not supported",
+ chan->chan, bw);
+ continue;
+ }
+
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
@@ -614,12 +647,24 @@
}
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
- iface->conf->ieee80211ac &&
- iface->conf->vht_oper_chwidth == 1 &&
- !acs_usable_vht80_chan(chan)) {
- wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
- chan->chan);
- continue;
+ iface->conf->ieee80211ac) {
+ if (iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_80MHZ &&
+ !acs_usable_vht80_chan(chan)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for VHT80",
+ chan->chan);
+ continue;
+ }
+
+ if (iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_160MHZ &&
+ !acs_usable_vht160_chan(chan)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for VHT160",
+ chan->chan);
+ continue;
+ }
}
factor = 0;
@@ -632,6 +677,13 @@
if (!adj_chan)
break;
+ if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+ chan->chan, adj_chan->chan, bw);
+ break;
+ }
+
if (acs_usable_chan(adj_chan)) {
factor += adj_chan->interference_factor;
total_weight += 1;
@@ -744,10 +796,14 @@
case VHT_CHANWIDTH_80MHZ:
offset = 6;
break;
+ case VHT_CHANWIDTH_160MHZ:
+ offset = 14;
+ break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
- wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
+ wpa_printf(MSG_INFO,
+ "ACS: Only VHT20/40/80/160 is supported now");
return;
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index f9b6f29..9611dc0 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -131,6 +131,15 @@
* This can be enabled by default once the implementation has been fully
* completed and tested with other implementations. */
bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
+
+ bss->send_probe_response = 1;
+
+#ifdef CONFIG_HS20
+ bss->hs20_release = (HS20_VERSION >> 4) + 1;
+#endif /* CONFIG_HS20 */
+
+ /* Default to strict CRL checking. */
+ bss->check_crl_strict = 1;
}
@@ -193,7 +202,6 @@
conf->beacon_int = 100;
conf->rts_threshold = -1; /* use driver default: 2347 */
conf->fragm_threshold = -1; /* user driver default: 2346 */
- conf->send_probe_response = 1;
/* Set to invalid value means do not add Power Constraint IE */
conf->local_pwr_constraint = -1;
@@ -233,6 +241,9 @@
* environments for the current frequency band in the country. */
conf->country[2] = ' ';
+ conf->rssi_reject_assoc_rssi = 0;
+ conf->rssi_reject_assoc_timeout = 30;
+
return conf;
}
@@ -248,6 +259,12 @@
{
FILE *f;
char buf[128], *pos;
+ const char *keyid;
+ char *context;
+ char *context2;
+ char *token;
+ char *name;
+ char *value;
int line = 0, ret = 0, len, ok;
u8 addr[ETH_ALEN];
struct hostapd_wpa_psk *psk;
@@ -277,9 +294,35 @@
if (buf[0] == '\0')
continue;
- if (hwaddr_aton(buf, addr)) {
+ context = NULL;
+ keyid = NULL;
+ while ((token = str_token(buf, " ", &context))) {
+ if (!os_strchr(token, '='))
+ break;
+ context2 = NULL;
+ name = str_token(token, "=", &context2);
+ value = str_token(token, "", &context2);
+ if (!value)
+ value = "";
+ if (!os_strcmp(name, "keyid")) {
+ keyid = value;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Unrecognized '%s=%s' on line %d in '%s'",
+ name, value, line, fname);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret == -1)
+ break;
+
+ if (!token)
+ token = "";
+ if (hwaddr_aton(token, addr)) {
wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
- "line %d in '%s'", buf, line, fname);
+ "line %d in '%s'", token, line, fname);
ret = -1;
break;
}
@@ -295,15 +338,14 @@
else
os_memcpy(psk->addr, addr, ETH_ALEN);
- pos = buf + 17;
- if (*pos == '\0') {
+ pos = str_token(buf, "", &context);
+ if (!pos) {
wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
line, fname);
os_free(psk);
ret = -1;
break;
}
- pos++;
ok = 0;
len = os_strlen(pos);
@@ -322,6 +364,18 @@
break;
}
+ if (keyid) {
+ len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
+ if ((size_t) len >= sizeof(psk->keyid)) {
+ wpa_printf(MSG_ERROR,
+ "PSK keyid too long on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+ }
+
psk->next = ssid->wpa_psk;
ssid->wpa_psk = psk;
}
@@ -538,6 +592,7 @@
os_free(conf->ocsp_stapling_response_multi);
os_free(conf->dh_file);
os_free(conf->openssl_ciphers);
+ os_free(conf->openssl_ecdh_curves);
os_free(conf->pac_opaque_encr_key);
os_free(conf->eap_fast_a_id);
os_free(conf->eap_fast_a_id_info);
@@ -644,6 +699,7 @@
os_free(conf->hs20_operator_icon);
}
os_free(conf->subscr_remediation_url);
+ os_free(conf->hs20_sim_provisioning_url);
os_free(conf->t_c_filename);
os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
@@ -1003,6 +1059,15 @@
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OCV
+ if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
+ bss->ocv) {
+ wpa_printf(MSG_ERROR,
+ "OCV: PMF needs to be enabled whenever using OCV");
+ return -1;
+ }
+#endif /* CONFIG_OCV */
+
return 0;
}
@@ -1146,3 +1211,26 @@
}
}
}
+
+
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
+{
+ int with_id = 0, without_id = 0;
+ struct sae_password_entry *pw;
+
+ if (conf->ssid.wpa_passphrase)
+ without_id = 1;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier)
+ with_id = 1;
+ else
+ without_id = 1;
+ if (with_id && without_id)
+ break;
+ }
+
+ if (with_id && !without_id)
+ return 2;
+ return with_id;
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 778366d..6963df4 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -42,6 +42,7 @@
#define MESH_CONF_SEC_AMPE BIT(2)
unsigned int security;
enum mfp_options ieee80211w;
+ int ocv;
unsigned int pairwise_cipher;
unsigned int group_cipher;
unsigned int mgmt_group_cipher;
@@ -122,6 +123,7 @@
int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
struct vlan_description vlan_desc;
char ifname[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
int configured;
int dynamic_vlan;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -132,6 +134,7 @@
};
#define PMK_LEN 32
+#define KEYID_LEN 32
#define MIN_PASSPHRASE_LEN 8
#define MAX_PASSPHRASE_LEN 63
struct hostapd_sta_wpa_psk_short {
@@ -145,6 +148,7 @@
struct hostapd_wpa_psk {
struct hostapd_wpa_psk *next;
int group;
+ char keyid[KEYID_LEN];
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
@@ -335,6 +339,9 @@
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
int assoc_sa_query_retry_timeout;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
enum {
PSK_RADIUS_IGNORED = 0,
PSK_RADIUS_ACCEPTED = 1,
@@ -384,12 +391,15 @@
char *private_key;
char *private_key_passwd;
int check_crl;
+ int check_crl_strict;
+ unsigned int crl_reload_interval;
unsigned int tls_session_lifetime;
unsigned int tls_flags;
char *ocsp_stapling_response;
char *ocsp_stapling_response_multi;
char *dh_file;
char *openssl_ciphers;
+ char *openssl_ecdh_curves;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@@ -557,6 +567,7 @@
int na_mcast_to_ucast;
#ifdef CONFIG_HS20
int hs20;
+ int hs20_release;
int disable_dgaf;
u16 anqp_domain_id;
unsigned int hs20_oper_friendly_name_count;
@@ -596,6 +607,7 @@
unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *hs20_sim_provisioning_url;
char *t_c_filename;
u32 t_c_timestamp;
char *t_c_server_url;
@@ -686,6 +698,12 @@
#endif /* CONFIG_OWE */
int coloc_intf_reporting;
+
+ u8 send_probe_response;
+
+#define BACKHAUL_BSS 1
+#define FRONTHAUL_BSS 2
+ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
};
/**
@@ -717,7 +735,6 @@
u16 beacon_int;
int rts_threshold;
int fragm_threshold;
- u8 send_probe_response;
u8 channel;
u8 acs;
struct wpa_freq_range_list acs_ch_list;
@@ -829,12 +846,16 @@
#ifdef CONFIG_IEEE80211AX
struct he_phy_capabilities_info he_phy_capab;
struct he_operation he_op;
+ struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_VHT_ENABLED BIT(0)
#define CH_SWITCH_VHT_DISABLED BIT(1)
unsigned int ch_switch_vht_config;
+
+ int rssi_reject_assoc_rssi;
+ int rssi_reject_assoc_timeout;
};
@@ -862,5 +883,6 @@
int hostapd_config_check(struct hostapd_config *conf, int full_config);
void hostapd_set_security_params(struct hostapd_bss_config *bss,
int full_config);
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 728d7f0..067cf86 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -176,7 +176,8 @@
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
- if (hapd->conf->mbo_enabled || hapd->enable_oce) {
+ if (hapd->conf->mbo_enabled ||
+ OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
add_buf_data(&proberesp, buf, pos - buf) < 0 ||
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index db93fde..d45ab84 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -356,4 +356,12 @@
return hapd->driver->stop_ap(hapd->drv_priv);
}
+static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ struct wpa_channel_info *ci)
+{
+ if (!hapd->driver || !hapd->driver->channel_info)
+ return -1;
+ return hapd->driver->channel_info(hapd->drv_priv, ci);
+}
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 95d004e..1bb3d9f 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -136,6 +136,7 @@
#ifdef CONFIG_HS20
srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method;
+ srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
srv.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
@@ -200,6 +201,16 @@
os_memset(&conf, 0, sizeof(conf));
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ if (hapd->conf->crl_reload_interval > 0 &&
+ hapd->conf->check_crl <= 0) {
+ wpa_printf(MSG_INFO,
+ "Cannot enable CRL reload functionality - it depends on check_crl being set");
+ } else if (hapd->conf->crl_reload_interval > 0) {
+ conf.crl_reload_interval =
+ hapd->conf->crl_reload_interval;
+ wpa_printf(MSG_INFO,
+ "Enabled CRL reload functionality");
+ }
conf.tls_flags = hapd->conf->tls_flags;
conf.event_cb = authsrv_tls_event;
conf.cb_ctx = hapd;
@@ -217,6 +228,7 @@
params.private_key_passwd = hapd->conf->private_key_passwd;
params.dh_file = hapd->conf->dh_file;
params.openssl_ciphers = hapd->conf->openssl_ciphers;
+ params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
params.ocsp_stapling_response =
hapd->conf->ocsp_stapling_response;
params.ocsp_stapling_response_multi =
@@ -229,7 +241,8 @@
}
if (tls_global_set_verify(hapd->ssl_ctx,
- hapd->conf->check_crl)) {
+ hapd->conf->check_crl,
+ hapd->conf->check_crl_strict)) {
wpa_printf(MSG_ERROR, "Failed to enable check_crl");
authsrv_deinit(hapd);
return -1;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 59bd4af..3e62991 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -397,7 +397,8 @@
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation);
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
}
#endif /* CONFIG_IEEE80211AX */
@@ -510,6 +511,7 @@
if (hapd->iconf->ieee80211ax) {
pos = hostapd_eid_he_capab(hapd, pos);
pos = hostapd_eid_he_operation(hapd, pos);
+ pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -767,7 +769,7 @@
ie, ie_len, ssi_signal) > 0)
return;
- if (!hapd->iconf->send_probe_response)
+ if (!hapd->conf->send_probe_response)
return;
if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
@@ -1085,7 +1087,8 @@
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation);
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1222,6 +1225,7 @@
if (hapd->iconf->ieee80211ax) {
tailpos = hostapd_eid_he_capab(hapd, tailpos);
tailpos = hostapd_eid_he_operation(hapd, tailpos);
+ tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1357,6 +1361,18 @@
#endif /* CONFIG_HS20 */
params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
params->pbss = hapd->conf->pbss;
+
+ if (hapd->conf->ftm_responder) {
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
+ params->ftm_responder = 1;
+ params->lci = hapd->iface->conf->lci;
+ params->civic = hapd->iface->conf->civic;
+ } else {
+ wpa_printf(MSG_WARNING,
+ "Not configuring FTM responder as the driver doesn't advertise support for it");
+ }
+ }
+
return 0;
}
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 21b813e..3128aed 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -207,6 +207,7 @@
char *buf, size_t buflen)
{
int len, res, ret, i;
+ const char *keyid;
if (!sta)
return 0;
@@ -341,6 +342,13 @@
len += ret;
}
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 993dd19..79cd00f 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -142,18 +142,30 @@
{
struct hostapd_channel_data *first_chan, *chan;
int i;
+ u32 bw = num_chan_to_bw(num_chans);
if (first_chan_idx + num_chans > mode->num_channels)
return 0;
first_chan = &mode->channels[first_chan_idx];
+ /* hostapd DFS implementation assumes the first channel as primary.
+ * If it's not allowed to use the first channel as primary, decline the
+ * whole channel range. */
+ if (!chan_pri_allowed(first_chan))
+ return 0;
+
for (i = 0; i < num_chans; i++) {
chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
first_chan_idx);
if (!chan)
return 0;
+ /* HT 40 MHz secondary channel availability checked only for
+ * primary channel */
+ if (!chan_bw_allowed(chan, bw, 1, !i))
+ return 0;
+
if (!dfs_channel_available(chan, skip_radar))
return 0;
}
@@ -197,7 +209,8 @@
/* Skip HT40/VHT incompatible channels */
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel &&
- !dfs_is_chan_allowed(chan, n_chans))
+ (!dfs_is_chan_allowed(chan, n_chans) ||
+ !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)))
continue;
/* Skip incompatible chandefs */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index 6d8c2f4..ed37fc8 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -88,6 +88,15 @@
}
}
+ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+ (u8 *) buf, len);
+ }
+ }
+
if (msgtype == DHCPACK) {
if (b->your_ip == 0)
return;
@@ -124,15 +133,6 @@
}
sta->ipaddr = b->your_ip;
}
-
- if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
- for (sta = hapd->sta_list; sta; sta = sta->next) {
- if (!(sta->flags & WLAN_STA_AUTHORIZED))
- continue;
- x_snoop_mcast_to_ucast_convert_send(hapd, sta,
- (u8 *) buf, len);
- }
- }
}
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 4ec044e..149f389 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -505,9 +505,9 @@
}
-static void hostapd_dpp_set_configurator(struct hostapd_data *hapd,
- struct dpp_authentication *auth,
- const char *cmd)
+static int hostapd_dpp_set_configurator(struct hostapd_data *hapd,
+ struct dpp_authentication *auth,
+ const char *cmd)
{
const char *pos, *end;
struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
@@ -521,7 +521,7 @@
char *group_id = NULL;
if (!cmd)
- return;
+ return 0;
wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
pos = os_strstr(cmd, " ssid=");
@@ -618,10 +618,12 @@
conf_ap->akm = DPP_AKM_PSK;
if (psk_set) {
os_memcpy(conf_ap->psk, psk, PMK_LEN);
- } else {
+ } else if (pass_len > 0) {
conf_ap->passphrase = os_strdup(pass);
if (!conf_ap->passphrase)
goto fail;
+ } else {
+ goto fail;
}
} else if (os_strstr(cmd, " conf=ap-dpp")) {
conf_ap->akm = DPP_AKM_DPP;
@@ -663,13 +665,15 @@
auth->conf_ap = conf_ap;
auth->conf = conf;
os_free(group_id);
- return;
+ return 0;
fail:
- wpa_printf(MSG_DEBUG, "DPP: Failed to set configurator parameters");
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "DPP: Failed to set configurator parameters");
dpp_configuration_free(conf_sta);
dpp_configuration_free(conf_ap);
os_free(group_id);
+ return -1;
}
@@ -842,7 +846,11 @@
if (!hapd->dpp_auth)
goto fail;
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd);
+ if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ goto fail;
+ }
hapd->dpp_auth->neg_freq = neg_freq;
@@ -967,8 +975,12 @@
return;
}
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
- hapd->dpp_configurator_params);
+ if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
+ hapd->dpp_configurator_params) < 0) {
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
@@ -1892,9 +1904,9 @@
return -1;
curve = get_param(cmd, " curve=");
- hostapd_dpp_set_configurator(hapd, auth, cmd);
-
- if (dpp_configurator_own_config(auth, curve, 1) == 0) {
+ hostapd_dpp_set_testing_options(hapd, auth);
+ if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 &&
+ dpp_configurator_own_config(auth, curve, 1) == 0) {
hostapd_dpp_handle_config_obj(hapd, auth);
ret = 0;
}
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 1135aea..54be3b5 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -38,6 +38,7 @@
#include "mbo_ap.h"
#include "dpp_hostapd.h"
#include "fils_hlp.h"
+#include "neighbor_db.h"
#ifdef CONFIG_FILS
@@ -739,9 +740,12 @@
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
int offset, int width, int cf1, int cf2)
{
+ /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
+
#ifdef NEED_AP_MLME
int channel, chwidth, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
+ size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
@@ -824,6 +828,9 @@
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
}
+
+ for (i = 0; i < hapd->iface->num_bss; i++)
+ hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
#endif /* NEED_AP_MLME */
}
@@ -1072,19 +1079,23 @@
struct sta_info *sta;
size_t plen __maybe_unused;
u16 fc;
+ u8 *action __maybe_unused;
- if (drv_mgmt->frame_len < 24 + 1)
+ if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
return;
- plen = drv_mgmt->frame_len - 24 - 1;
+ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1;
mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
fc = le_to_host16(mgmt->frame_control);
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",
- mgmt->u.action.category, (int) plen);
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " plen %d",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen);
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
@@ -1100,10 +1111,7 @@
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
- ieee802_11_sa_query_action(
- hapd, mgmt->sa,
- mgmt->u.action.u.sa_query_resp.action,
- mgmt->u.action.u.sa_query_resp.trans_id);
+ ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
}
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM_AP
@@ -1721,6 +1729,11 @@
hostapd_reconfig_encryption(hapd);
hapd->reenable_beacon = 1;
ieee802_11_set_beacon(hapd);
+#ifdef NEED_AP_MLME
+ } else if (hapd->disabled && hapd->iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
+ hostapd_handle_dfs(hapd->iface);
+#endif /* NEED_AP_MLME */
}
break;
case EVENT_INTERFACE_DISABLED:
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index fab307f..a510ee3 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -92,7 +92,7 @@
} else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
user->remediation = strlen(argv[i]) > 0;
} else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) {
- user->t_c_timestamp = strtol(argv[i], 0, 10);
+ user->t_c_timestamp = strtol(argv[i], NULL, 10);
}
}
@@ -139,6 +139,7 @@
struct hostapd_eap_user *user = NULL;
char id_str[256], cmd[300];
size_t i;
+ int res;
if (identity_len >= sizeof(id_str)) {
wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
@@ -174,6 +175,7 @@
if (hapd->tmp_eap_user.identity == NULL)
return NULL;
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+ hapd->tmp_eap_user.identity_len = identity_len;
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
@@ -182,9 +184,12 @@
return NULL;
}
- os_snprintf(cmd, sizeof(cmd),
- "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
- id_str, phase2);
+ res = os_snprintf(cmd, sizeof(cmd),
+ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+ id_str, phase2);
+ if (os_snprintf_error(sizeof(cmd), res))
+ goto fail;
+
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
SQLITE_OK) {
@@ -214,6 +219,7 @@
}
}
+fail:
sqlite3_close(db);
return user;
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 2a359ab..6da514a 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -580,6 +580,19 @@
u8 *tmp, *tmp_pos;
int ret = 0;
+ if (sta->fils_pending_assoc_req &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
+ /* Do not process FILS HLP request again if the station
+ * retransmits (Re)Association Request frame before the previous
+ * HLP response has either been received or timed out. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Do not relay another HLP request from "
+ MACSTR
+ " before processing of the already pending one has been completed",
+ MAC2STR(sta->addr));
+ return 1;
+ }
+
/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
wpabuf_free(sta->hlp_dhcp_discover);
sta->hlp_dhcp_discover = NULL;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 117ee08..342585f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -176,8 +176,27 @@
}
+static int hostapd_iface_conf_changed(struct hostapd_config *newconf,
+ struct hostapd_config *oldconf)
+{
+ size_t i;
+
+ if (newconf->num_bss != oldconf->num_bss)
+ return 1;
+
+ for (i = 0; i < newconf->num_bss; i++) {
+ if (os_strcmp(newconf->bss[i]->iface,
+ oldconf->bss[i]->iface) != 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
int hostapd_reload_config(struct hostapd_iface *iface)
{
+ struct hapd_interfaces *interfaces = iface->interfaces;
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_config *newconf, *oldconf;
size_t j;
@@ -200,6 +219,35 @@
hostapd_clear_old(iface);
oldconf = hapd->iconf;
+ if (hostapd_iface_conf_changed(newconf, oldconf)) {
+ char *fname;
+ int res;
+
+ wpa_printf(MSG_DEBUG,
+ "Configuration changes include interface/BSS modification - force full disable+enable sequence");
+ fname = os_strdup(iface->config_fname);
+ if (!fname) {
+ hostapd_config_free(newconf);
+ return -1;
+ }
+ hostapd_remove_iface(interfaces, hapd->conf->iface);
+ iface = hostapd_init(interfaces, fname);
+ os_free(fname);
+ hostapd_config_free(newconf);
+ if (!iface) {
+ wpa_printf(MSG_ERROR,
+ "Failed to initialize interface on config reload");
+ return -1;
+ }
+ iface->interfaces = interfaces;
+ interfaces->iface[interfaces->count] = iface;
+ interfaces->count++;
+ res = hostapd_enable_iface(iface);
+ if (res < 0)
+ wpa_printf(MSG_ERROR,
+ "Failed to enable interface on config reload");
+ return res;
+ }
iface->conf = newconf;
for (j = 0; j < iface->num_bss; j++) {
@@ -1620,127 +1668,6 @@
#endif /* CONFIG_FST */
-
-#ifdef NEED_AP_MLME
-static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
- int ht, int vht)
-{
- if (!ht && !vht)
- return NR_CHAN_WIDTH_20;
- if (!hapd->iconf->secondary_channel)
- return NR_CHAN_WIDTH_20;
- if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
- return NR_CHAN_WIDTH_40;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
- return NR_CHAN_WIDTH_80;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
- return NR_CHAN_WIDTH_160;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
- return NR_CHAN_WIDTH_80P80;
- return NR_CHAN_WIDTH_20;
-}
-#endif /* NEED_AP_MLME */
-
-
-static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
-{
-#ifdef NEED_AP_MLME
- u16 capab = hostapd_own_capab_info(hapd);
- int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
- int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
- struct wpa_ssid_value ssid;
- u8 channel, op_class;
- u8 center_freq1_idx = 0, center_freq2_idx = 0;
- enum nr_chan_width width;
- u32 bssid_info;
- struct wpabuf *nr;
-
- if (!(hapd->conf->radio_measurements[0] &
- WLAN_RRM_CAPS_NEIGHBOR_REPORT))
- return;
-
- bssid_info = 3; /* AP is reachable */
- bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
- bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
-
- if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
- bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
-
- bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
-
- if (hapd->conf->wmm_enabled) {
- bssid_info |= NEI_REP_BSSID_INFO_QOS;
-
- if (hapd->conf->wmm_uapsd &&
- (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
- bssid_info |= NEI_REP_BSSID_INFO_APSD;
- }
-
- if (ht) {
- bssid_info |= NEI_REP_BSSID_INFO_HT |
- NEI_REP_BSSID_INFO_DELAYED_BA;
-
- /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
- if (vht)
- bssid_info |= NEI_REP_BSSID_INFO_VHT;
- }
-
- /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
-
- 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;
- width = hostapd_get_nr_chan_width(hapd, ht, vht);
- if (vht) {
- center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
- if (width == NR_CHAN_WIDTH_80P80)
- center_freq2_idx =
- hapd->iconf->vht_oper_centr_freq_seg1_idx;
- } else if (ht) {
- ieee80211_freq_to_chan(hapd->iface->freq +
- 10 * hapd->iconf->secondary_channel,
- ¢er_freq1_idx);
- }
-
- ssid.ssid_len = hapd->conf->ssid.ssid_len;
- os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
-
- /*
- * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
- * phy type + wide bandwidth channel subelement.
- */
- nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
- if (!nr)
- return;
-
- wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
- wpabuf_put_le32(nr, bssid_info);
- wpabuf_put_u8(nr, op_class);
- wpabuf_put_u8(nr, channel);
- wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
-
- /*
- * Wide Bandwidth Channel subelement may be needed to allow the
- * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
- * Figure 9-301.
- */
- wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
- wpabuf_put_u8(nr, 3);
- wpabuf_put_u8(nr, width);
- wpabuf_put_u8(nr, center_freq1_idx);
- wpabuf_put_u8(nr, center_freq2_idx);
-
- hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
- hapd->iconf->civic, hapd->iconf->stationary_ap);
-
- wpabuf_free(nr);
-#endif /* NEED_AP_MLME */
-}
-
-
#ifdef CONFIG_OWE
static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
@@ -2037,7 +1964,7 @@
iface->interfaces->terminate_on_error--;
for (j = 0; j < iface->num_bss; j++)
- hostapd_set_own_neighbor_report(iface->bss[j]);
+ hostapd_neighbor_set_own_report(iface->bss[j]);
return 0;
@@ -2682,7 +2609,7 @@
if (conf == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
"configuration", __func__);
- return NULL;
+ return NULL;
}
if (driver) {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 28b3a1c..85e63d3 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -14,6 +14,13 @@
#include "ap_config.h"
#include "drivers/driver.h"
+#define OCE_STA_CFON_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_STA_CFON) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
+#define OCE_AP_ENABLED(hapd) \
+ ((hapd->conf->oce & OCE_AP) && \
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
+
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
@@ -324,11 +331,6 @@
#ifdef CONFIG_MBO
unsigned int mbo_assoc_disallow;
- /**
- * enable_oce - Enable OCE if it is enabled by user and device also
- * supports OCE.
- */
- u8 enable_oce;
#endif /* CONFIG_MBO */
struct dl_list nr_db;
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 98d016d..532580e 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -25,17 +25,20 @@
if (!hapd->conf->hs20)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
- *eid++ = 7;
+ *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_INDICATION_OUI_TYPE;
- conf = HS20_VERSION; /* Release Number */
- conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
+ if (hapd->conf->hs20_release >= 2)
+ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
if (hapd->conf->disable_dgaf)
conf |= HS20_DGAF_DISABLED;
*eid++ = conf;
- WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
- eid += 2;
+ if (hapd->conf->hs20_release >= 2) {
+ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+ eid += 2;
+ }
return eid;
}
@@ -84,6 +87,10 @@
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (hapd->conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
WPA_PUT_LE16(eid, capab);
eid += 2;
@@ -184,13 +191,14 @@
{
struct wpabuf *buf;
int ret;
- size_t url_len = os_strlen(url);
+ size_t url_len;
if (!url) {
wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available");
return -1;
}
+ url_len = os_strlen(url);
if (5 + url_len > 255) {
wpa_printf(MSG_INFO,
"HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'",
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 5279abc..9d3d990 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -229,9 +229,6 @@
{
int pri_chan, sec_chan;
- if (!iface->conf->secondary_channel)
- return 1; /* HT40 not used */
-
pri_chan = iface->conf->channel;
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
@@ -697,30 +694,25 @@
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
int channel, int primary)
{
- int i;
struct hostapd_channel_data *chan;
if (!iface->current_mode)
return 0;
- for (i = 0; i < iface->current_mode->num_channels; i++) {
- chan = &iface->current_mode->channels[i];
- if (chan->chan != channel)
- continue;
+ chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+ if (!chan)
+ return 0;
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
- return 1;
+ if ((primary && chan_pri_allowed(chan)) ||
+ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+ return 1;
- wpa_printf(MSG_DEBUG,
- "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
- primary ? "" : "Configured HT40 secondary ",
- i, chan->chan, chan->flag,
- chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
- chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
- }
-
- wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
- channel, primary ? "primary" : "secondary");
+ wpa_printf(MSG_INFO,
+ "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+ channel, primary ? "primary" : "secondary",
+ chan->flag,
+ chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+ chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
return 0;
}
@@ -728,6 +720,12 @@
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
int secondary_chan;
+ struct hostapd_channel_data *pri_chan;
+
+ pri_chan = hw_get_channel_chan(iface->current_mode,
+ iface->conf->channel, NULL);
+ if (!pri_chan)
+ return 0;
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
return 0;
@@ -742,13 +740,15 @@
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_chan = iface->conf->channel + 4;
- if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_chan = iface->conf->channel - 4;
- if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 90788de..376bbd8 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -21,6 +21,7 @@
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/sae.h"
+#include "common/ocv.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -62,6 +63,22 @@
int *is_pub);
#endif /* CONFIG_FILS */
+
+u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 multi_ap_val = 0;
+
+ if (!hapd->conf->multi_ap)
+ return eid;
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
+
+ return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+}
+
+
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
@@ -1743,7 +1760,8 @@
static void handle_auth(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi)
{
u16 auth_alg, auth_transaction, status_code;
u16 resp = WLAN_STATUS_SUCCESS;
@@ -1925,6 +1943,7 @@
sta = ap_get_sta(hapd, mgmt->sa);
if (sta) {
sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+ sta->ft_over_ds = 0;
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
@@ -1973,6 +1992,9 @@
}
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
+#ifdef CONFIG_MBO
+ sta->auth_rssi = rssi;
+#endif /* CONFIG_MBO */
res = ieee802_11_set_radius_info(
hapd, sta, res, session_timeout, acct_interim_interval,
@@ -2210,6 +2232,57 @@
return WLAN_STATUS_SUCCESS;
}
+static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *multi_ap_ie, size_t multi_ap_len)
+{
+ u8 multi_ap_value = 0;
+
+ sta->flags &= ~WLAN_STA_MULTI_AP;
+
+ if (!hapd->conf->multi_ap)
+ return WLAN_STATUS_SUCCESS;
+
+ if (multi_ap_ie) {
+ const u8 *multi_ap_subelem;
+
+ multi_ap_subelem = get_ie(multi_ap_ie + 4,
+ multi_ap_len - 4,
+ MULTI_AP_SUB_ELEM_TYPE);
+ if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
+ multi_ap_value = multi_ap_subelem[2];
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE has missing or invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ }
+
+ if (multi_ap_value == MULTI_AP_BACKHAUL_STA)
+ sta->flags |= WLAN_STA_MULTI_AP;
+
+ if ((hapd->conf->multi_ap & BACKHAUL_BSS) &&
+ multi_ap_value == MULTI_AP_BACKHAUL_STA)
+ return WLAN_STATUS_SUCCESS;
+
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS) {
+ if (multi_ap_value == MULTI_AP_BACKHAUL_STA) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Backhaul STA tries to associate with fronthaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+}
+
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
@@ -2466,6 +2539,11 @@
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
#ifdef CONFIG_IEEE80211N
resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
@@ -2485,6 +2563,10 @@
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation);
+ 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;
@@ -2713,10 +2795,20 @@
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
+ int release;
+
sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
elems.hs20_len - 4);
- } else
+ release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
+ if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: PMF not negotiated by release %d station "
+ MACSTR, release, MAC2STR(sta->addr));
+ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ }
+ } else {
sta->hs20_ie = NULL;
+ }
wpabuf_free(sta->roaming_consortium);
if (elems.roaming_cons_sel)
@@ -2747,6 +2839,35 @@
}
#endif /* CONFIG_MBO */
+#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
+ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+#endif /* CONFIG_FILS && CONFIG_OCV */
+
ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
elems.supp_op_classes_len);
@@ -2791,7 +2912,7 @@
static int add_associated_sta(struct hostapd_data *hapd,
- struct sta_info *sta)
+ struct sta_info *sta, int reassoc)
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
@@ -2807,14 +2928,36 @@
* Skip this if the STA has already completed FT reassociation and the
* TK has been configured since the TX/RX PN must not be reset to 0 for
* the same key.
+ *
+ * FT-over-the-DS has a special case where the STA entry (and as such,
+ * the TK) has not yet been configured to the driver depending on which
+ * driver interface is used. For that case, allow add-STA operation to
+ * be used (instead of set-STA). This is needed to allow mac80211-based
+ * drivers to accept the STA parameter configuration. Since this is
+ * after a new FT-over-DS exchange, a new TK has been derived, so key
+ * reinstallation is not a concern for this case.
*/
+ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+ MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
+ sta->ft_over_ds, reassoc,
+ !!(sta->flags & WLAN_STA_AUTHORIZED),
+ wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
+ wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
+
if (!sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
(!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
!wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
hostapd_drv_sta_remove(hapd, sta->addr);
wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
set = 0;
+
+ /* Do not allow the FT-over-DS exception to be used more than
+ * once per authentication exchange to guarantee a new TK is
+ * used here */
+ sta->ft_over_ds = 0;
}
#ifdef CONFIG_IEEE80211N
@@ -2860,7 +3003,7 @@
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
- const u8 *ies, size_t ies_len)
+ const u8 *ies, size_t ies_len, int rssi)
{
int send_len;
u8 *buf;
@@ -2905,6 +3048,16 @@
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
+#ifdef CONFIG_MBO
+ if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
+ rssi != 0) {
+ int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
+
+ p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
+ delta);
+ }
+#endif /* CONFIG_MBO */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta && status_code == WLAN_STATUS_SUCCESS) {
/* IEEE 802.11r: Mobility Domain Information, Fast BSS
@@ -2922,7 +3075,8 @@
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_OWE
- if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
+ if (sta && status_code == WLAN_STATUS_SUCCESS &&
+ (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
buf + buflen - p,
ies, ies_len);
@@ -2995,6 +3149,9 @@
}
#endif /* CONFIG_WPS */
+ if (sta && (sta->flags & WLAN_STA_MULTI_AP))
+ p = hostapd_eid_multi_ap(hapd, p);
+
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
@@ -3069,7 +3226,7 @@
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
- sta && sta->owe_ecdh &&
+ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
struct wpabuf *pub;
@@ -3172,7 +3329,7 @@
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len);
+ sta->fils_pending_assoc_req_len, 0);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -3209,7 +3366,7 @@
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
- int reassoc)
+ int reassoc, int rssi)
{
u16 capab_info, listen_interval, seq_ctrl, fc;
u16 resp = WLAN_STATUS_SUCCESS, reply_res;
@@ -3392,6 +3549,14 @@
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
+
+ if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
+ rssi < hapd->iconf->rssi_reject_assoc_rssi &&
+ (sta->auth_rssi == 0 ||
+ sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
+ resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
+ goto fail;
+ }
#endif /* CONFIG_MBO */
/*
@@ -3549,10 +3714,24 @@
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
+ if (resp == WLAN_STATUS_SUCCESS && sta &&
+ add_associated_sta(hapd, sta, reassoc))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
#ifdef CONFIG_FILS
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
+ sta->fils_pending_assoc_req) {
+ /* Do not reschedule fils_hlp_timeout in case the station
+ * retransmits (Re)Association Request frame while waiting for
+ * the previously started FILS HLP wait, so that the timeout can
+ * be determined from the first pending attempt. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ os_free(tmp);
+ return;
+ }
if (sta) {
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
os_free(sta->fils_pending_assoc_req);
@@ -3577,7 +3756,7 @@
#endif /* CONFIG_FILS */
reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
- left);
+ left, rssi);
os_free(tmp);
/*
@@ -3732,9 +3911,7 @@
return 0;
}
- ieee802_11_sa_query_action(hapd, mgmt->sa,
- mgmt->u.action.u.sa_query_resp.action,
- mgmt->u.action.u.sa_query_resp.trans_id);
+ ieee802_11_sa_query_action(hapd, mgmt, len);
return 1;
}
@@ -3752,9 +3929,9 @@
unsigned int freq)
{
struct sta_info *sta;
- sta = ap_get_sta(hapd, mgmt->sa);
+ u8 *action __maybe_unused;
- if (len < IEEE80211_HDRLEN + 1) {
+ if (len < IEEE80211_HDRLEN + 2 + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
@@ -3762,6 +3939,14 @@
return 0;
}
+ action = (u8 *) &mgmt->u.action.u;
+ wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
+ " da " MACSTR " len %d freq %u",
+ mgmt->u.action.category, *action,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
+
+ sta = ap_get_sta(hapd, mgmt->sa);
+
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
(sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
@@ -4011,17 +4196,17 @@
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
- handle_auth(hapd, mgmt, len);
+ handle_auth(hapd, mgmt, len, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_ASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
- handle_assoc(hapd, mgmt, len, 0);
+ handle_assoc(hapd, mgmt, len, 0, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_REASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
- handle_assoc(hapd, mgmt, len, 1);
+ handle_assoc(hapd, mgmt, len, 1, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_DISASSOC:
@@ -4233,7 +4418,7 @@
sta->flags |= WLAN_STA_WDS;
}
- if (sta->flags & WLAN_STA_WDS) {
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2f3b4da..5082226 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -59,6 +59,7 @@
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_mu_edca_parameter_set(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,
@@ -80,6 +81,8 @@
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_capab);
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper);
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_opmode);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
@@ -91,8 +94,8 @@
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid);
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
- const u8 *sa, const u8 action_type,
- const u8 *trans_id);
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
@@ -120,6 +123,9 @@
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta);
+
#else /* CONFIG_MBO */
static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
@@ -166,4 +172,7 @@
char **identity, char **radius_cui,
int is_probe_req);
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 5cb7fb1..931d4d0 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -289,6 +289,9 @@
return HOSTAPD_ACL_ACCEPT;
};
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ vlan_id = NULL;
+
/* Check whether ACL cache has an entry for this station */
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
acct_interim_interval, vlan_id, psk,
@@ -516,7 +519,6 @@
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- int *untagged, *tagged, *notempty;
query = hapd->acl_queries;
prev = NULL;
@@ -574,12 +576,10 @@
cache->acct_interim_interval = 0;
}
- notempty = &cache->vlan_id.notempty;
- untagged = &cache->vlan_id.untagged;
- tagged = cache->vlan_id.tagged;
- *notempty = !!radius_msg_get_vlanid(msg, untagged,
- MAX_NUM_TAGGED_VLAN,
- tagged);
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
+ cache->vlan_id.notempty = !!radius_msg_get_vlanid(
+ msg, &cache->vlan_id.untagged,
+ MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
msg, req, cache);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 1a8d469..0721358 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -86,3 +86,34 @@
return pos;
}
+
+
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_mu_edca_parameter_set *edca;
+ u8 *pos;
+ size_t i;
+
+ pos = (u8 *) &hapd->iface->conf->he_mu_edca;
+ for (i = 0; i < sizeof(*edca); i++) {
+ if (pos[i])
+ break;
+ }
+ if (i == sizeof(*edca))
+ return eid; /* no MU EDCA Parameters configured */
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*edca);
+ *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
+
+ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
+ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ pos, sizeof(*edca));
+
+ pos += sizeof(*edca);
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index c481399..d70d6c1 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -10,10 +10,12 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wpa_auth.h"
#include "ieee802_11.h"
@@ -49,7 +51,12 @@
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
- struct ieee80211_mgmt mgmt;
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+#endif /* CONFIG_OCV */
+ struct ieee80211_mgmt *mgmt;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
@@ -57,19 +64,61 @@
wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Request");
+ return;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in SA Query Request");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
+ if (!mgmt) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
+ end = mgmt->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0)
wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+
+ os_free(mgmt);
+ os_free(oci_ie);
}
@@ -77,7 +126,9 @@
const u8 *sa, const u8 *trans_id)
{
struct sta_info *sta;
- struct ieee80211_mgmt resp;
+ struct ieee80211_mgmt *resp;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
@@ -92,30 +143,115 @@
return;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Response");
+ return;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for for OCI element in SA Query Response");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ resp = os_zalloc(sizeof(*resp) + oci_ie_len);
+ if (!resp) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(resp.da, sa, ETH_ALEN);
- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
- resp.u.action.category = WLAN_ACTION_SA_QUERY;
- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp->da, sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.action.category = WLAN_ACTION_SA_QUERY;
+ resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
+ end = resp->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0)
wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+
+ os_free(resp);
+ os_free(oci_ie);
}
-void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
- const u8 action_type, const u8 *trans_id)
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
struct sta_info *sta;
int i;
+ const u8 *sa = mgmt->sa;
+ const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
+ const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
+
+ sta = ap_get_sta(hapd, sa);
+
+#ifdef CONFIG_OCV
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct ieee802_11_elems elems;
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ size_t ies_len;
+ const u8 *ies;
+
+ ies = mgmt->u.action.u.sa_query_resp.variable;
+ ies_len = len - (ies - (u8 *) mgmt);
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "SA Query: Failed to parse elements");
+ return;
+ }
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in SA Query Action frame");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
if (action_type == WLAN_SA_QUERY_REQUEST) {
ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
@@ -135,7 +271,6 @@
/* MLME-SAQuery.confirm */
- sta = ap_get_sta(hapd, sa);
if (sta == NULL || sta->sa_query_trans_id == NULL) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
"pending SA Query request found");
@@ -237,6 +372,21 @@
*pos |= 0x01;
#endif /* CONFIG_FILS */
break;
+ case 10: /* Bits 80-87 */
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
+ int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+
+ if (in_use)
+ *pos |= 0x02; /* Bit 81 - SAE Password
+ * Identifiers In Use */
+ if (in_use == 2)
+ *pos |= 0x04; /* Bit 82 - SAE Password
+ * Identifiers Used Exclusively */
+ }
+#endif /* CONFIG_SAE */
+ break;
}
}
@@ -276,6 +426,12 @@
!wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
len = 10;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_SAE
+ if (len < 11 && hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ hostapd_sae_pw_id_in_use(hapd->conf))
+ len = 11;
+#endif /* CONFIG_SAE */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -547,12 +703,29 @@
#ifdef CONFIG_MBO
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta)
+{
+ u8 mbo[4];
+
+ mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
+ mbo[1] = 2;
+ /* Delta RSSI */
+ mbo[2] = delta;
+ /* Retry delay */
+ mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
+
+ return eid + mbo_add_ie(eid, len, mbo, 4);
+}
+
+
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 mbo[9], *mbo_pos = mbo;
u8 *pos = eid;
- if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return eid;
if (hapd->conf->mbo_enabled) {
@@ -568,12 +741,11 @@
*mbo_pos++ = hapd->mbo_assoc_disallow;
}
- if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) {
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) {
u8 ctrl;
ctrl = OCE_RELEASE;
- if ((hapd->enable_oce & (OCE_AP | OCE_STA_CFON)) ==
- OCE_STA_CFON)
+ if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
ctrl |= OCE_IS_STA_CFON;
*mbo_pos++ = OCE_ATTR_ID_CAPA_IND;
@@ -591,7 +763,8 @@
{
u8 len;
- if (!hapd->conf->mbo_enabled && !hapd->enable_oce)
+ if (!hapd->conf->mbo_enabled &&
+ !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd))
return 0;
/*
@@ -603,7 +776,7 @@
len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
/* OCE capability indication attribute (3) */
- if (hapd->enable_oce & (OCE_AP | OCE_STA_CFON))
+ if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd))
len += 3;
return len;
@@ -751,3 +924,71 @@
return pos;
}
+
+
+#ifdef CONFIG_OCV
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ int ht_40mhz = 0;
+ int vht_80p80 = 0;
+ int requested_bw;
+
+ if (sta->ht_capabilities)
+ ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+
+ if (sta->vht_operation) {
+ struct ieee80211_vht_operation *oper = sta->vht_operation;
+
+ /*
+ * If a VHT Operation element was present, use it to determine
+ * the supported channel bandwidth.
+ */
+ if (oper->vht_op_info_chwidth == 0) {
+ requested_bw = ht_40mhz ? 40 : 20;
+ } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
+ requested_bw = 80;
+ } else {
+ int diff;
+
+ requested_bw = 160;
+ diff = abs((int)
+ oper->vht_op_info_chan_center_freq_seg0_idx -
+ (int)
+ oper->vht_op_info_chan_center_freq_seg1_idx);
+ vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
+ != 0 && diff > 16;
+ }
+ } else if (sta->vht_capabilities) {
+ struct ieee80211_vht_capabilities *capab;
+ int vht_chanwidth;
+
+ capab = sta->vht_capabilities;
+
+ /*
+ * If only the VHT Capabilities element is present (e.g., for
+ * normal clients), use it to determine the supported channel
+ * bandwidth.
+ */
+ vht_chanwidth = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ vht_80p80 = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+
+ /* TODO: Also take into account Extended NSS BW Support field */
+ requested_bw = vht_chanwidth ? 160 : 80;
+ } else {
+ requested_bw = ht_40mhz ? 40 : 20;
+ }
+
+ *bandwidth = requested_bw < ap_max_chanwidth ?
+ requested_bw : ap_max_chanwidth;
+
+ *seg1_idx = 0;
+ if (ap_seg1_idx && vht_80p80)
+ *seg1_idx = ap_seg1_idx;
+
+ return 0;
+}
+#endif /* CONFIG_OCV */
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 8d06620..54ee080 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -357,6 +357,29 @@
}
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper)
+{
+ if (!vht_oper) {
+ os_free(sta->vht_operation);
+ sta->vht_operation = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->vht_operation) {
+ sta->vht_operation =
+ os_zalloc(sizeof(struct ieee80211_vht_operation));
+ if (!sta->vht_operation)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memcpy(sta->vht_operation, vht_oper,
+ sizeof(struct ieee80211_vht_operation));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ie, size_t len)
{
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 985f8b7..a56c82e 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -682,7 +682,8 @@
#ifdef CONFIG_HS20
if (hapd->conf->hs20) {
- u8 ver = 1; /* Release 2 */
+ u8 ver = hapd->conf->hs20_release - 1;
+
if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
&ver, 1)) {
@@ -1741,6 +1742,45 @@
}
+#ifndef CONFIG_NO_VLAN
+static int ieee802_1x_update_vlan(struct radius_msg *msg,
+ struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct vlan_description vlan_desc;
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
+ MAX_NUM_TAGGED_VLAN,
+ vlan_desc.tagged);
+
+ if (vlan_desc.notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ sta->eapol_sm->authFail = TRUE;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_desc.untagged,
+ vlan_desc.tagged[0] ? "+" : "");
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ ap_sta_set_vlan(hapd, sta, &vlan_desc);
+ return -1;
+ }
+
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+ !vlan_desc.notempty) {
+ sta->eapol_sm->authFail = TRUE;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "authentication server did not include required VLAN ID in Access-Accept");
+ return -1;
+ }
+
+ return ap_sta_set_vlan(hapd, sta, &vlan_desc);
+}
+#endif /* CONFIG_NO_VLAN */
+
+
/**
* ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
* @msg: RADIUS response message
@@ -1763,12 +1803,6 @@
struct eapol_state_machine *sm;
int override_eapReq = 0;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- struct vlan_description vlan_desc;
-#ifndef CONFIG_NO_VLAN
- int *untagged, *tagged, *notempty;
-#endif /* CONFIG_NO_VLAN */
-
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
if (sm == NULL) {
@@ -1833,56 +1867,21 @@
switch (hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
#ifndef CONFIG_NO_VLAN
- if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) {
- notempty = &vlan_desc.notempty;
- untagged = &vlan_desc.untagged;
- tagged = vlan_desc.tagged;
- *notempty = !!radius_msg_get_vlanid(msg, untagged,
- MAX_NUM_TAGGED_VLAN,
- tagged);
- }
-
- if (vlan_desc.notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
- sta->eapol_sm->authFail = TRUE;
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from RADIUS server",
- vlan_desc.untagged,
- vlan_desc.tagged[0] ? "+" : "");
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
- ap_sta_set_vlan(hapd, sta, &vlan_desc);
- break;
- }
-
- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
- !vlan_desc.notempty) {
- sta->eapol_sm->authFail = TRUE;
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE8021X,
- HOSTAPD_LEVEL_INFO, "authentication "
- "server did not include required VLAN "
- "ID in Access-Accept");
- break;
- }
-#endif /* CONFIG_NO_VLAN */
-
- if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0)
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+ ieee802_1x_update_vlan(msg, hapd, sta) < 0)
break;
-#ifndef CONFIG_NO_VLAN
if (sta->vlan_id > 0) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"VLAN ID %d", sta->vlan_id);
}
-#endif /* CONFIG_NO_VLAN */
if ((sta->flags & WLAN_STA_ASSOC) &&
ap_sta_bind_vlan(hapd, sta) < 0)
break;
+#endif /* CONFIG_NO_VLAN */
sta->session_timeout_set = !!session_timeout_set;
os_get_reltime(&sta->session_timeout);
@@ -2595,6 +2594,7 @@
struct os_reltime diff;
const char *name1;
const char *name2;
+ char *identity_buf = NULL;
if (sm == NULL)
return 0;
@@ -2710,6 +2710,14 @@
/* dot1xAuthSessionStatsTable */
os_reltime_age(&sta->acct_session_start, &diff);
+ if (sm->eap && !sm->identity) {
+ const u8 *id;
+ size_t id_len;
+
+ id = eap_get_identity(sm->eap, &id_len);
+ if (id)
+ identity_buf = dup_binstr(id, id_len);
+ }
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -2725,7 +2733,8 @@
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
(unsigned int) diff.sec,
- sm->identity);
+ sm->identity ? (char *) sm->identity : identity_buf);
+ os_free(identity_buf);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index b8fd592..2b6f727 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "hostapd.h"
+#include "ieee802_11.h"
#include "neighbor_db.h"
@@ -123,7 +124,7 @@
}
-void hostpad_free_neighbor_db(struct hostapd_data *hapd)
+void hostapd_free_neighbor_db(struct hostapd_data *hapd)
{
struct hostapd_neighbor_entry *nr, *prev;
@@ -134,3 +135,123 @@
os_free(nr);
}
}
+
+
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+ int ht, int vht)
+{
+ if (!ht && !vht)
+ return NR_CHAN_WIDTH_20;
+ if (!hapd->iconf->secondary_channel)
+ return NR_CHAN_WIDTH_20;
+ if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+ return NR_CHAN_WIDTH_40;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+ return NR_CHAN_WIDTH_80;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+ return NR_CHAN_WIDTH_160;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+ return NR_CHAN_WIDTH_80P80;
+ return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+ u16 capab = hostapd_own_capab_info(hapd);
+ int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+ int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+ struct wpa_ssid_value ssid;
+ u8 channel, op_class;
+ u8 center_freq1_idx = 0, center_freq2_idx = 0;
+ enum nr_chan_width width;
+ u32 bssid_info;
+ struct wpabuf *nr;
+
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+ return;
+
+ bssid_info = 3; /* AP is reachable */
+ bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+ bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+ bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+ if (hapd->conf->wmm_enabled) {
+ bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+ if (hapd->conf->wmm_uapsd &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+ bssid_info |= NEI_REP_BSSID_INFO_APSD;
+ }
+
+ if (ht) {
+ bssid_info |= NEI_REP_BSSID_INFO_HT |
+ NEI_REP_BSSID_INFO_DELAYED_BA;
+
+ /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+ if (vht)
+ bssid_info |= NEI_REP_BSSID_INFO_VHT;
+ }
+
+ /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+ 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;
+ width = hostapd_get_nr_chan_width(hapd, ht, vht);
+ if (vht) {
+ center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+ if (width == NR_CHAN_WIDTH_80P80)
+ center_freq2_idx =
+ hapd->iconf->vht_oper_centr_freq_seg1_idx;
+ } else if (ht) {
+ ieee80211_freq_to_chan(hapd->iface->freq +
+ 10 * hapd->iconf->secondary_channel,
+ ¢er_freq1_idx);
+ }
+
+ ssid.ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+ /*
+ * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+ * phy type + wide bandwidth channel subelement.
+ */
+ nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+ if (!nr)
+ return;
+
+ wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+ wpabuf_put_le32(nr, bssid_info);
+ wpabuf_put_u8(nr, op_class);
+ wpabuf_put_u8(nr, channel);
+ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+ /*
+ * Wide Bandwidth Channel subelement may be needed to allow the
+ * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+ * Figure 9-301.
+ */
+ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+ wpabuf_put_u8(nr, 3);
+ wpabuf_put_u8(nr, width);
+ wpabuf_put_u8(nr, center_freq1_idx);
+ wpabuf_put_u8(nr, center_freq2_idx);
+
+ hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+ hapd->iconf->civic, hapd->iconf->stationary_ap);
+
+ wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index ba46d88..9c8f4f2 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -17,8 +17,9 @@
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
const struct wpabuf *civic, int stationary);
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
-void hostpad_free_neighbor_db(struct hostapd_data *hapd);
+void hostapd_free_neighbor_db(struct hostapd_data *hapd);
#endif /* NEIGHBOR_DB_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 56ed29c..f2d5cd1 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -558,7 +558,7 @@
void hostapd_clean_rrm(struct hostapd_data *hapd)
{
- hostpad_free_neighbor_db(hapd);
+ hostapd_free_neighbor_db(hapd);
eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 179cf43..8858a34 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -166,7 +166,7 @@
/* just in case */
ap_sta_set_authorized(hapd, sta, 0);
- if (sta->flags & WLAN_STA_WDS)
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
if (sta->ipaddr)
@@ -328,6 +328,7 @@
os_free(sta->ht_capabilities);
os_free(sta->vht_capabilities);
+ os_free(sta->vht_operation);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -896,9 +897,6 @@
struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
int old_vlan_id, vlan_id = 0, ret = 0;
- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
- vlan_desc = NULL;
-
/* Check if there is something to do */
if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
/* This sta is lacking its own vif */
@@ -1165,6 +1163,32 @@
#endif /* CONFIG_IEEE80211W */
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_wpa_psk *psk;
+ struct hostapd_ssid *ssid;
+ const u8 *pmk;
+ int pmk_len;
+
+ ssid = &hapd->conf->ssid;
+
+ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+ if (!pmk || pmk_len != PMK_LEN)
+ return NULL;
+
+ for (psk = ssid->wpa_psk; psk; psk = psk->next)
+ if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
+ break;
+ if (!psk)
+ return NULL;
+ if (!psk || !psk->keyid[0])
+ return NULL;
+
+ return psk->keyid;
+}
+
+
void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
@@ -1203,7 +1227,11 @@
sta->addr, authorized, dev_addr);
if (authorized) {
+ const char *keyid;
+ char keyid_buf[100];
char ip_addr[100];
+
+ keyid_buf[0] = '\0';
ip_addr[0] = '\0';
#ifdef CONFIG_P2P
if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
@@ -1214,14 +1242,20 @@
}
#endif /* CONFIG_P2P */
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
- buf, ip_addr);
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ os_snprintf(keyid_buf, sizeof(keyid_buf),
+ " keyid=%s", keyid);
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
+ buf, ip_addr, keyid_buf);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s%s",
- buf, ip_addr);
+ AP_STA_CONNECTED "%s%s%s",
+ buf, ip_addr, keyid_buf);
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 9cac6f1..ee3f628 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -36,6 +36,7 @@
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
+#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -117,6 +118,7 @@
unsigned int power_capab:1;
unsigned int agreed_to_steer:1;
unsigned int hs20_t_c_filtering:1;
+ unsigned int ft_over_ds:1;
u16 auth_alg;
@@ -162,6 +164,7 @@
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
+ struct ieee80211_vht_operation *vht_operation;
u8 vht_opmode;
#ifdef CONFIG_IEEE80211W
@@ -215,6 +218,7 @@
u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
* enum mbo_cellular_capa values */
struct mbo_non_pref_chan_info *non_pref_chan;
+ int auth_rssi; /* Last Authentication frame RSSI */
#endif /* CONFIG_MBO */
u8 *supp_op_classes; /* Supported Operating Classes element, if
@@ -320,6 +324,8 @@
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
index aa42335..19aa3c6 100644
--- a/src/ap/vlan_full.c
+++ b/src/ap/vlan_full.c
@@ -16,6 +16,7 @@
#include "utils/common.h"
#include "drivers/priv_netlink.h"
+#include "drivers/linux_ioctl.h"
#include "common/linux_bridge.h"
#include "common/linux_vlan.h"
#include "utils/eloop.h"
@@ -143,6 +144,9 @@
return -1;
}
+ if (linux_br_del_if(fd, br_name, if_name) == 0)
+ goto done;
+
if_index = if_nametoindex(if_name);
if (if_index == 0) {
@@ -168,6 +172,7 @@
return -1;
}
+done:
close(fd);
return 0;
}
@@ -194,6 +199,14 @@
return -1;
}
+ if (linux_br_add_if(fd, br_name, if_name) == 0)
+ goto done;
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
if_index = if_nametoindex(if_name);
if (if_index == 0) {
@@ -224,6 +237,7 @@
return -1;
}
+done:
close(fd);
return 0;
}
@@ -241,6 +255,9 @@
return -1;
}
+ if (linux_br_del(fd, br_name) == 0)
+ goto done;
+
arg[0] = BRCTL_DEL_BRIDGE;
arg[1] = (unsigned long) br_name;
@@ -252,6 +269,7 @@
return -1;
}
+done:
close(fd);
return 0;
}
@@ -277,11 +295,19 @@
return -1;
}
+ if (linux_br_add(fd, br_name) == 0)
+ goto done;
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ }
+
arg[0] = BRCTL_ADD_BRIDGE;
arg[1] = (unsigned long) br_name;
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
- if (errno == EEXIST) {
+ if (errno == EEXIST) {
/* The bridge is already added. */
close(fd);
return 1;
@@ -294,6 +320,7 @@
}
}
+done:
/* Decrease forwarding delay to avoid EAPOL timeouts. */
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
@@ -363,12 +390,18 @@
{
char vlan_ifname[IFNAMSIZ];
int clean;
+ int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
else
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
clean = 0;
ifconfig_up(tagged_interface);
@@ -384,19 +417,28 @@
}
-static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan, int vid)
{
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int ret;
- if (hapd->conf->vlan_bridge[0]) {
- os_snprintf(br_name, IFNAMSIZ, "%s%d",
- hapd->conf->vlan_bridge, vid);
+ if (vlan->bridge[0]) {
+ os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
+ ret = 0;
+ } else if (hapd->conf->vlan_bridge[0]) {
+ ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
+ hapd->conf->vlan_bridge, vid);
} else if (tagged_interface) {
- os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+ tagged_interface, vid);
} else {
- os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+ ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
}
+ if (ret >= IFNAMSIZ)
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ br_name);
}
@@ -445,7 +487,7 @@
!br_addif(hapd->conf->bridge, ifname))
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
- vlan_bridge_name(br_name, hapd, untagged);
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
vlan_get_bridge(br_name, hapd, untagged);
@@ -458,7 +500,7 @@
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
- vlan_bridge_name(br_name, hapd, tagged[i]);
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_get_bridge(br_name, hapd, tagged[i]);
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
@@ -474,12 +516,19 @@
{
char vlan_ifname[IFNAMSIZ];
int clean;
+ int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
else
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
+
clean = dyn_iface_put(hapd, vlan_ifname);
@@ -543,7 +592,7 @@
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
- vlan_bridge_name(br_name, hapd, tagged[i]);
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
vlan_put_bridge(br_name, hapd, tagged[i]);
@@ -555,7 +604,7 @@
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
br_delif(hapd->conf->bridge, ifname);
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
- vlan_bridge_name(br_name, hapd, untagged);
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
br_delif(br_name, vlan->ifname);
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 01fecee..e293a00 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -187,6 +187,7 @@
{
struct hostapd_vlan *n;
char ifname[IFNAMSIZ + 1], *pos;
+ int ret;
if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
return NULL;
@@ -208,8 +209,13 @@
n->vlan_desc = *vlan_desc;
n->dynamic_vlan = 1;
- os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
- pos);
+ ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
+ ifname, vlan_id, pos);
+ if (os_snprintf_error(sizeof(n->ifname), ret)) {
+ os_free(n);
+ return NULL;
+ }
+ os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
n->next = hapd->conf->vlan;
hapd->conf->vlan = n;
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 61d2f65..27c69d3 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
+#include "common/ocv.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ap_config.h"
@@ -54,8 +55,8 @@
size_t gtk_elem_len = 0;
size_t igtk_elem_len = 0;
struct wnm_sleep_element wnmsleep_ie;
- u8 *wnmtfs_ie;
- u8 wnmsleep_ie_len;
+ u8 *wnmtfs_ie, *oci_ie;
+ u8 wnmsleep_ie_len, oci_ie_len;
u16 wnmtfs_ie_len;
u8 *pos;
struct sta_info *sta;
@@ -88,10 +89,42 @@
wnmtfs_ie = NULL;
}
+ oci_ie = NULL;
+ oci_ie_len = 0;
+#ifdef CONFIG_OCV
+ if (action_type == WNM_SLEEP_MODE_EXIT &&
+ wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(wnmtfs_ie);
+ os_free(oci_ie);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
+
#define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
- MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
+ oci_ie_len);
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame");
@@ -134,11 +167,18 @@
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
/* copy TFS IE here */
pos += wnmsleep_ie_len;
- if (wnmtfs_ie)
+ if (wnmtfs_ie) {
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+ pos += wnmtfs_ie_len;
+ }
+#ifdef CONFIG_OCV
+ /* copy OCV OCI here */
+ if (oci_ie_len > 0)
+ os_memcpy(pos, oci_ie, oci_ie_len);
+#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
- igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+ igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
/* In driver, response frame should be forced to sent when STA is in
* PS mode */
@@ -185,6 +225,7 @@
#undef MAX_IGTK_SUBELEM_LEN
fail:
os_free(wnmtfs_ie);
+ os_free(oci_ie);
os_free(mgmt);
return res;
}
@@ -201,6 +242,11 @@
u8 *tfsreq_ie_start = NULL;
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+ const u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
+#endif /* CONFIG_OCV */
if (!hapd->conf->wnm_sleep_mode) {
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
@@ -228,6 +274,12 @@
if (!tfsreq_ie_start)
tfsreq_ie_start = (u8 *) pos;
tfsreq_ie_end = (u8 *) pos;
+#ifdef CONFIG_OCV
+ } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+ pos[2] == WLAN_EID_EXT_OCV_OCI) {
+ oci_ie = pos + 3;
+ oci_ie_len = ie_len - 1;
+#endif /* CONFIG_OCV */
} else
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
*pos);
@@ -239,6 +291,27 @@
return;
}
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+ sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
+ return;
+ }
+
+ if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
tfsreq_ie_start && tfsreq_ie_end &&
tfsreq_ie_end - tfsreq_ie_start >= 0) {
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 34969e7..8e2b48e 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -13,6 +13,7 @@
#include "utils/state_machine.h"
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/aes_siv.h"
@@ -22,6 +23,7 @@
#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
+#include "drivers/driver.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
@@ -238,6 +240,17 @@
}
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
@@ -860,6 +873,8 @@
if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
data, data_len) == 0) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
ok = 1;
break;
}
@@ -2559,6 +2574,27 @@
wpabuf_put(plain, tmp2 - tmp);
*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "FILS: Failed to get channel info for OCI element");
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
+ if (ocv_insert_extended_oci(&ci, pos) < 0) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
+
return plain;
}
@@ -2624,6 +2660,21 @@
#endif /* CONFIG_FILS */
+#ifdef CONFIG_OCV
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+ if (!wpa_auth->cb->get_sta_tx_params)
+ return -1;
+ return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
+ ap_max_chanwidth, ap_seg1_idx,
+ bandwidth, seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
@@ -2675,6 +2726,8 @@
wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
ok = 1;
break;
}
@@ -2746,6 +2799,32 @@
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
@@ -2883,6 +2962,36 @@
#endif /* CONFIG_IEEE80211W */
+static int ocv_oci_len(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm))
+ return OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
+ return 0;
+}
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
+{
+#ifdef CONFIG_OCV
+ struct wpa_channel_info ci;
+
+ if (!wpa_auth_uses_ocv(sm))
+ return 0;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ return -1;
+ }
+
+ return ocv_insert_oci_kde(&ci, argpos);
+#else /* CONFIG_OCV */
+ return 0;
+#endif /* CONFIG_OCV */
+}
+
+
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
@@ -2966,7 +3075,7 @@
}
}
- kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -3011,6 +3120,10 @@
gtk, gtk_len);
}
pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde);
+ return;
+ }
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -3322,7 +3435,7 @@
}
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
- ieee80211w_kde_len(sm);
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (kde_buf == NULL)
return;
@@ -3333,6 +3446,10 @@
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde_buf);
+ return;
+ }
kde_len = pos - kde;
} else {
kde = gtk;
@@ -3353,8 +3470,67 @@
SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
{
+#ifdef CONFIG_OCV
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ const u8 *key_data, *mic;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ size_t mic_len;
+ u16 key_data_length;
+#endif /* CONFIG_OCV */
+
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
sm->EAPOLKeyReceived = FALSE;
+
+#ifdef CONFIG_OCV
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ return;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
+ return;
+ }
+
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (sm->GUpdateStationKeys)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
@@ -3963,6 +4139,15 @@
}
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
+{
+ if (!sm)
+ return NULL;
+ *len = sm->pmk_len;
+ return sm->PMK;
+}
+
+
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (sm == NULL)
@@ -4666,7 +4851,7 @@
}
}
- kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -4715,6 +4900,10 @@
os_memset(opos, 0, 6); /* clear PN */
}
#endif /* CONFIG_IEEE80211W */
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde);
+ return -1;
+ }
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -4796,7 +4985,7 @@
gtk = gsm->GTK[gsm->GN - 1];
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
- ieee80211w_kde_len(sm);
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (kde_buf == NULL)
return -1;
@@ -4816,6 +5005,10 @@
os_memset(opos, 0, 6); /* clear PN */
}
#endif /* CONFIG_IEEE80211W */
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde_buf);
+ return -1;
+ }
kde_len = pos - kde;
} else {
kde = gtk;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index fad5536..e61648d 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -145,6 +145,7 @@
struct rsn_pmksa_cache_entry;
struct eapol_state_machine;
struct ft_remote_seq;
+struct wpa_channel_info;
struct ft_remote_r0kh {
@@ -191,6 +192,9 @@
int group_mgmt_cipher;
int sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
@@ -265,6 +269,10 @@
size_t data_len);
int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
size_t data_len);
+ int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
+ int (*get_sta_tx_params)(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx);
#ifdef CONFIG_IEEE80211R_AP
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
int (*set_vlan)(void *ctx, const u8 *sta_addr,
@@ -316,6 +324,8 @@
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
struct wpa_state_machine *
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *p2p_dev_addr);
@@ -339,6 +349,7 @@
void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -449,6 +460,9 @@
int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
size_t ies_len);
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
u8 *buf, size_t len);
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e8d46ab..ac736f0 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -13,6 +13,8 @@
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "drivers/driver.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
@@ -727,6 +729,17 @@
}
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
@@ -1451,7 +1464,7 @@
now.sec;
else if (session_timeout && r1->session_timeout)
*session_timeout = 1;
- else
+ else if (session_timeout)
*session_timeout = 0;
return 0;
}
@@ -2430,6 +2443,35 @@
os_free(igtk);
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *nbuf, *ocipos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ os_free(subelem);
+ return NULL;
+ }
+
+ subelem_len += 2 + OCV_OCI_LEN;
+ nbuf = os_realloc(subelem, subelem_len);
+ if (!nbuf) {
+ os_free(subelem);
+ return NULL;
+ }
+ subelem = nbuf;
+
+ ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
+ *ocipos++ = FTIE_SUBELEM_OCI;
+ *ocipos++ = OCV_OCI_LEN;
+ if (ocv_insert_oci(&ci, &ocipos) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
} else {
r0kh_id = conf->r0_key_holder;
r0kh_id_len = conf->r0_key_holder_len;
@@ -2596,6 +2638,8 @@
os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
if (out_pairwise)
*out_pairwise = pairwise;
+ os_memcpy(sm->PMK, pmk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
if (out_vlan &&
wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
@@ -3178,6 +3222,32 @@
return WLAN_STATUS_INVALID_FTIE;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in (Re)Assoc Request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+#endif /* CONFIG_OCV */
+
return WLAN_STATUS_SUCCESS;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 8127403..9091f43 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -27,6 +27,7 @@
#include "tkip_countermeasures.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "ieee802_11.h"
#include "pmksa_cache_auth.h"
#include "wpa_auth.h"
#include "wpa_auth_glue.h"
@@ -55,6 +56,9 @@
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+#ifdef CONFIG_OCV
+ wconf->ocv = conf->ocv;
+#endif /* CONFIG_OCV */
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
@@ -776,6 +780,35 @@
}
+static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_channel_info(hapd, ci);
+}
+
+
+#ifdef CONFIG_OCV
+static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO,
+ "Failed to get STA info to validate received OCI");
+ return -1;
+ }
+
+ return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth,
+ seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
#ifdef CONFIG_IEEE80211R_AP
static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
@@ -814,12 +847,18 @@
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
+ " based on WPA authenticator callback",
+ MAC2STR(sta_addr));
if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
return NULL;
sta = ap_sta_add(hapd, sta_addr);
if (sta == NULL)
return NULL;
+ if (hapd->driver && hapd->driver->add_sta_node)
+ sta->added_unassoc = 1;
+ sta->ft_over_ds = 1;
if (sta->wpa_sm) {
sta->auth_alg = WLAN_AUTH_FT;
return sta->wpa_sm;
@@ -1189,6 +1228,10 @@
.for_each_auth = hostapd_wpa_auth_for_each_auth,
.send_ether = hostapd_wpa_auth_send_ether,
.send_oui = hostapd_wpa_auth_send_oui,
+ .channel_info = hostapd_channel_info,
+#ifdef CONFIG_OCV
+ .get_sta_tx_params = hostapd_get_sta_tx_params,
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
.send_ft_action = hostapd_wpa_auth_send_ft_action,
.add_sta = hostapd_wpa_auth_add_sta,
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index b1cea1b..a349304 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -92,6 +92,9 @@
#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1;
+#ifdef CONFIG_OCV
+ unsigned int ocv_enabled:1;
+#endif /* CONFIG_OCV */
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 253fe6e..3bcbef7 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -293,9 +293,13 @@
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing)
- capab |= BIT(8) | BIT(14) | BIT(15);
+ capab |= BIT(8) | BIT(15);
#endif /* CONFIG_RSN_TESTING */
WPA_PUT_LE16(pos, capab);
pos += 2;
@@ -414,6 +418,10 @@
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
WPA_PUT_LE16(eid, capab);
eid += 2;
@@ -553,6 +561,19 @@
if (version == WPA_PROTO_RSN) {
res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+ if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
+ !wpa_key_mgmt_only_ft(data.key_mgmt)) {
+ /* Workaround for some HP and Epson printers that seem
+ * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP
+ * advertised RSNE to Association Request frame. */
+ wpa_printf(MSG_DEBUG,
+ "RSN: FT set in RSNE AKM but MDE is missing from "
+ MACSTR
+ " - ignore FT AKM(s) because there's also a non-FT AKM",
+ MAC2STR(sm->addr));
+ data.key_mgmt &= ~WPA_KEY_MGMT_FT;
+ }
+
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (0) {
}
@@ -760,6 +781,17 @@
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OCV
+ if ((data.capabilities & WPA_CAPABILITY_OCVC) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with OCV, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+ wpa_auth_set_ocv(sm, wpa_auth->conf.ocv &&
+ (data.capabilities & WPA_CAPABILITY_OCVC));
+#endif /* CONFIG_OCV */
+
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
@@ -995,6 +1027,15 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
+ ie->oci = pos + 2 + RSN_SELECTOR_LEN;
+ ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_OCV */
+
return 0;
}
@@ -1062,13 +1103,34 @@
}
+#ifdef CONFIG_OCV
+
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
+{
+ if (sm)
+ sm->ocv_enabled = ocv;
+}
+
+
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
+{
+ return sm ? sm->ocv_enabled : 0;
+}
+
+#endif /* CONFIG_OCV */
+
+
#ifdef CONFIG_OWE
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
u8 *pos, size_t max_len,
const u8 *req_ies, size_t req_ies_len)
{
int res;
- struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ struct wpa_auth_config *conf;
+
+ if (!sm)
+ return pos;
+ conf = &sm->wpa_auth->conf;
#ifdef CONFIG_TESTING_OPTIONS
if (conf->own_ie_override_len) {
@@ -1082,7 +1144,7 @@
}
#endif /* CONFIG_TESTING_OPTIONS */
- res = wpa_write_rsn_ie(&sm->wpa_auth->conf, pos, max_len,
+ res = wpa_write_rsn_ie(conf, pos, max_len,
sm->pmksa ? sm->pmksa->pmkid : NULL);
if (res < 0)
return pos;
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 73e4333..a38b206 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -33,6 +33,10 @@
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ const u8 *oci;
+ size_t oci_len;
+#endif /* CONFIG_OCV */
const u8 *osen;
size_t osen_len;