summaryrefslogtreecommitdiff
path: root/src/ap
diff options
context:
space:
mode:
authorRui Paulo <rpaulo@FreeBSD.org>2015-04-18 05:04:12 +0000
committerRui Paulo <rpaulo@FreeBSD.org>2015-04-18 05:04:12 +0000
commitfbffd80fb2ba16c68f799da68a119d5e69643604 (patch)
tree139aabdc99568ca0bd6e4cbdcabdc8098f06fb22 /src/ap
parent5e2639d568f6bb660501a77cc83413c3412562e3 (diff)
Notes
Diffstat (limited to 'src/ap')
-rw-r--r--src/ap/Makefile2
-rw-r--r--src/ap/accounting.c68
-rw-r--r--src/ap/acs.c949
-rw-r--r--src/ap/acs.h27
-rw-r--r--src/ap/ap_config.c427
-rw-r--r--src/ap/ap_config.h164
-rw-r--r--src/ap/ap_drv_ops.c137
-rw-r--r--src/ap/ap_drv_ops.h137
-rw-r--r--src/ap/ap_list.c108
-rw-r--r--src/ap/ap_list.h22
-rw-r--r--src/ap/ap_mlme.c6
-rw-r--r--src/ap/authsrv.c17
-rw-r--r--src/ap/beacon.c487
-rw-r--r--src/ap/beacon.h19
-rw-r--r--src/ap/bss_load.c65
-rw-r--r--src/ap/bss_load.h17
-rw-r--r--src/ap/ctrl_iface_ap.c334
-rw-r--r--src/ap/ctrl_iface_ap.h7
-rw-r--r--src/ap/dfs.c1064
-rw-r--r--src/ap/dfs.h30
-rw-r--r--src/ap/dhcp_snoop.c178
-rw-r--r--src/ap/dhcp_snoop.h30
-rw-r--r--src/ap/drv_callbacks.c603
-rw-r--r--src/ap/eap_user_db.c16
-rw-r--r--src/ap/gas_serv.c494
-rw-r--r--src/ap/gas_serv.h19
-rw-r--r--src/ap/hostapd.c1804
-rw-r--r--src/ap/hostapd.h172
-rw-r--r--src/ap/hs20.c154
-rw-r--r--src/ap/hs20.h8
-rw-r--r--src/ap/hw_features.c801
-rw-r--r--src/ap/hw_features.h16
-rw-r--r--src/ap/iapp.c84
-rw-r--r--src/ap/ieee802_11.c1156
-rw-r--r--src/ap/ieee802_11.h32
-rw-r--r--src/ap/ieee802_11_auth.c38
-rw-r--r--src/ap/ieee802_11_ht.c278
-rw-r--r--src/ap/ieee802_11_shared.c136
-rw-r--r--src/ap/ieee802_11_vht.c207
-rw-r--r--src/ap/ieee802_1x.c692
-rw-r--r--src/ap/ieee802_1x.h1
-rw-r--r--src/ap/ndisc_snoop.c171
-rw-r--r--src/ap/ndisc_snoop.h36
-rw-r--r--src/ap/p2p_hostapd.c5
-rw-r--r--src/ap/peerkey_auth.c22
-rw-r--r--src/ap/pmksa_cache_auth.c180
-rw-r--r--src/ap/pmksa_cache_auth.h8
-rw-r--r--src/ap/sta_info.c329
-rw-r--r--src/ap/sta_info.h73
-rw-r--r--src/ap/tkip_countermeasures.c8
-rw-r--r--src/ap/vlan_init.c114
-rw-r--r--src/ap/vlan_init.h10
-rw-r--r--src/ap/wmm.c12
-rw-r--r--src/ap/wmm.h10
-rw-r--r--src/ap/wnm_ap.c357
-rw-r--r--src/ap/wnm_ap.h15
-rw-r--r--src/ap/wpa_auth.c590
-rw-r--r--src/ap/wpa_auth.h41
-rw-r--r--src/ap/wpa_auth_ft.c351
-rw-r--r--src/ap/wpa_auth_glue.c88
-rw-r--r--src/ap/wpa_auth_i.h39
-rw-r--r--src/ap/wpa_auth_ie.c161
-rw-r--r--src/ap/wpa_auth_ie.h7
-rw-r--r--src/ap/wps_hostapd.c508
-rw-r--r--src/ap/wps_hostapd.h9
-rw-r--r--src/ap/x_snoop.c123
-rw-r--r--src/ap/x_snoop.h56
67 files changed, 11946 insertions, 2383 deletions
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 9c41962fd7e16..adfd3dfd5b9be 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -2,7 +2,7 @@ all:
@echo Nothing to be made.
clean:
- rm -f *~ *.o *.d
+ rm -f *~ *.o *.d *.gcno *.gcda *.gcov
install:
@echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 954053131a7a6..7c55146b2c5c2 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -10,7 +10,8 @@
#include "utils/common.h"
#include "utils/eloop.h"
-#include "drivers/driver.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "hostapd.h"
@@ -44,19 +45,26 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
radius_client_get_id(hapd->radius));
if (msg == NULL) {
- printf("Could not create net RADIUS packet\n");
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
return NULL;
}
if (sta) {
radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
- os_snprintf(buf, sizeof(buf), "%08X-%08X",
- sta->acct_session_id_hi, sta->acct_session_id_lo);
- if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
- (u8 *) buf, os_strlen(buf))) {
- printf("Could not add Acct-Session-Id\n");
- goto fail;
+ if ((hapd->conf->wpa & 2) &&
+ !hapd->conf->disable_pmksa_caching &&
+ sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) {
+ os_snprintf(buf, sizeof(buf), "%08X+%08X",
+ sta->eapol_sm->acct_multi_session_id_hi,
+ sta->eapol_sm->acct_multi_session_id_lo);
+ if (!radius_msg_add_attr(
+ msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_INFO,
+ "Could not add Acct-Multi-Session-Id");
+ goto fail;
+ }
}
} else {
radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
@@ -64,7 +72,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
status_type)) {
- printf("Could not add Acct-Status-Type\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
goto fail;
}
@@ -74,7 +82,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
hapd->conf->ieee802_1x ?
RADIUS_ACCT_AUTHENTIC_RADIUS :
RADIUS_ACCT_AUTHENTIC_LOCAL)) {
- printf("Could not add Acct-Authentic\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
goto fail;
}
@@ -99,7 +107,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
len)) {
- printf("Could not add User-Name\n");
+ wpa_printf(MSG_INFO, "Could not add User-Name");
goto fail;
}
}
@@ -117,7 +125,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
val, len)) {
- printf("Could not add Class\n");
+ wpa_printf(MSG_INFO, "Could not add Class");
goto fail;
}
}
@@ -202,7 +210,6 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
{
struct radius_msg *msg;
- struct os_time t;
int interval;
if (sta->acct_session_started)
@@ -213,8 +220,7 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
"starting accounting session %08X-%08X",
sta->acct_session_id_hi, sta->acct_session_id_lo);
- os_get_time(&t);
- sta->acct_session_start = t.sec;
+ os_get_reltime(&sta->acct_session_start);
sta->last_rx_bytes = sta->last_tx_bytes = 0;
sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
hostapd_drv_sta_clear_stats(hapd, sta->addr);
@@ -244,6 +250,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
struct radius_msg *msg;
int cause = sta->acct_terminate_cause;
struct hostap_sta_driver_data data;
+ struct os_reltime now_r, diff;
struct os_time now;
u32 gigawords;
@@ -254,14 +261,16 @@ static void accounting_sta_report(struct hostapd_data *hapd,
stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
if (!msg) {
- printf("Could not create RADIUS Accounting message\n");
+ wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
return;
}
+ os_get_reltime(&now_r);
os_get_time(&now);
+ os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
- now.sec - sta->acct_session_start)) {
- printf("Could not add Acct-Session-Time\n");
+ diff.sec)) {
+ wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
goto fail;
}
@@ -269,19 +278,19 @@ static void accounting_sta_report(struct hostapd_data *hapd,
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_PACKETS,
data.rx_packets)) {
- printf("Could not add Acct-Input-Packets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
data.tx_packets)) {
- printf("Could not add Acct-Output-Packets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_INPUT_OCTETS,
data.rx_bytes)) {
- printf("Could not add Acct-Input-Octets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
goto fail;
}
gigawords = sta->acct_input_gigawords;
@@ -292,13 +301,13 @@ static void accounting_sta_report(struct hostapd_data *hapd,
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
gigawords)) {
- printf("Could not add Acct-Input-Gigawords\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
goto fail;
}
if (!radius_msg_add_attr_int32(msg,
RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
data.tx_bytes)) {
- printf("Could not add Acct-Output-Octets\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
goto fail;
}
gigawords = sta->acct_output_gigawords;
@@ -309,14 +318,14 @@ static void accounting_sta_report(struct hostapd_data *hapd,
!radius_msg_add_attr_int32(
msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
gigawords)) {
- printf("Could not add Acct-Output-Gigawords\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
goto fail;
}
}
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
now.sec)) {
- printf("Could not add Event-Timestamp\n");
+ wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
goto fail;
}
@@ -326,7 +335,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
if (stop && cause &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
cause)) {
- printf("Could not add Acct-Terminate-Cause\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
goto fail;
}
@@ -400,13 +409,12 @@ accounting_receive(struct radius_msg *msg, struct radius_msg *req,
void *data)
{
if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
- printf("Unknown RADIUS message code\n");
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
return RADIUS_RX_UNKNOWN;
}
if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
- printf("Incoming RADIUS packet did not have correct "
- "Authenticator - dropped\n");
+ wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
}
@@ -432,7 +440,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
{
- printf("Could not add Acct-Terminate-Cause\n");
+ wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
radius_msg_free(msg);
return;
}
diff --git a/src/ap/acs.c b/src/ap/acs.c
new file mode 100644
index 0000000000000..ae7f6c3092899
--- /dev/null
+++ b/src/ap/acs.c
@@ -0,0 +1,949 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <math.h>
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ * (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ * channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ * spend gathering survey data during hostapd startup. Short traffic bursts
+ * may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ * - BSS/RSSI based
+ * - spectral scan based
+ * (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ * guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ * new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ * when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ * 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS: * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS: * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS: * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS: * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS: * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS: * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS: * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS: * channel 1: total interference = 0.121432
+ * ACS: * channel 2: total interference = 0.137512
+ * ACS: * channel 3: total interference = 0.369757
+ * ACS: * channel 4: total interference = 0.546338
+ * ACS: * channel 5: total interference = 0.690538
+ * ACS: * channel 6: total interference = 0.762242
+ * ACS: * channel 7: total interference = 0.756092
+ * ACS: * channel 8: total interference = 0.537451
+ * ACS: * channel 9: total interference = 0.332313
+ * ACS: * channel 10: total interference = 0.152182
+ * ACS: * channel 11: total interference = 0.0916111
+ * ACS: * channel 12: total interference = 0.0816809
+ * ACS: * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey, *tmp;
+
+ if (dl_list_empty(&chan->survey_list))
+ return;
+
+ dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+ struct freq_survey, list) {
+ dl_list_del(&survey->list);
+ os_free(survey);
+ }
+}
+
+
+static void acs_cleanup(struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+ acs_clean_chan_surveys(chan);
+
+ dl_list_init(&chan->survey_list);
+ chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+ chan->min_nf = 0;
+ }
+
+ iface->chans_surveyed = 0;
+ iface->acs_num_completed_scans = 0;
+}
+
+
+static void acs_fail(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS: Failed to start");
+ acs_cleanup(iface);
+ hostapd_disable_iface(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+ long double factor, busy, total;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+ busy = survey->channel_time_busy;
+ else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+ busy = survey->channel_time_rx;
+ else {
+ /* This shouldn't really happen as survey data is checked in
+ * acs_sanity_check() */
+ wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+ return 0;
+ }
+
+ total = survey->channel_time;
+
+ if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+ busy -= survey->channel_time_tx;
+ total -= survey->channel_time_tx;
+ }
+
+ /* TODO: figure out the best multiplier for noise floor base */
+ factor = pow(10, survey->nf / 5.0L) +
+ (busy / total) *
+ pow(2, pow(10, (long double) survey->nf / 10.0L) -
+ pow(10, (long double) min_nf / 10.0L));
+
+ return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey;
+ unsigned int i = 0;
+ long double int_factor = 0;
+ unsigned count = 0;
+
+ if (dl_list_empty(&chan->survey_list))
+ return;
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
+
+ chan->interference_factor = 0;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+ {
+ i++;
+
+ if (!acs_survey_is_sufficient(survey)) {
+ wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+ continue;
+ }
+
+ count++;
+ int_factor = acs_survey_interference_factor(survey,
+ iface->lowest_nf);
+ chan->interference_factor += int_factor;
+ wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+ i, chan->min_nf, int_factor,
+ survey->nf, (unsigned long) survey->channel_time,
+ (unsigned long) survey->channel_time_busy,
+ (unsigned long) survey->channel_time_rx);
+ }
+
+ if (!count)
+ return;
+ chan->interference_factor /= count;
+}
+
+
+static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
+ 157, 184, 192 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
+ if (chan->chan == allowed[i])
+ return 1;
+
+ return 0;
+}
+
+
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+ 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)) {
+ wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+ return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+ wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+ return 0;
+ }
+
+ if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+ !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+ wpa_printf(MSG_INFO,
+ "ACS: Survey is missing RX and busy time (at least one is required)");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+ struct freq_survey *survey;
+ int ret = -1;
+
+ dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+ {
+ if (acs_survey_is_sufficient(survey)) {
+ ret = 1;
+ break;
+ }
+ ret = 0;
+ }
+
+ if (ret == -1)
+ ret = 1; /* no survey list entries */
+
+ if (!ret) {
+ wpa_printf(MSG_INFO,
+ "ACS: Channel %d has insufficient survey data",
+ chan->chan);
+ }
+
+ return ret;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+ int valid = 0;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ if (!acs_survey_list_is_sufficient(chan))
+ continue;
+
+ valid++;
+ }
+
+ /* We need at least survey data for one channel */
+ return !!valid;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+ if (dl_list_empty(&chan->survey_list))
+ return 0;
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return 0;
+ if (!acs_survey_list_is_sufficient(chan))
+ return 0;
+ return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ int *entry;
+
+ if (!iface->conf->chanlist)
+ return 1;
+
+ for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+ if (*entry == chan->chan)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void acs_survey_all_chans_intereference_factor(
+ struct hostapd_iface *iface)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (!acs_usable_chan(chan))
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+ chan->chan, chan->freq);
+
+ acs_survey_chan_interference_factor(iface, chan);
+
+ wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
+ chan->interference_factor);
+ }
+}
+
+
+static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
+ int freq)
+{
+ struct hostapd_channel_data *chan;
+ int i;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ if (chan->freq == freq)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+ return mode == HOSTAPD_MODE_IEEE80211B ||
+ mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+ return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#define ACS_24GHZ_PREFER_1_6_11 0.8
+#endif /* ACS_24GHZ_PREFER_1_6_11 */
+
+/*
+ * At this point it's assumed chan->interface_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
+ *rand_chan = NULL;
+ long double factor, ideal_factor = 0;
+ int i, j;
+ int n_chans = 1;
+ unsigned int k;
+
+ /* TODO: HT40- support */
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel == -1) {
+ wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+ return NULL;
+ }
+
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel)
+ n_chans = 2;
+
+ if (iface->conf->ieee80211ac &&
+ iface->conf->vht_oper_chwidth == 1)
+ n_chans = 4;
+
+ /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+
+ wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
+ n_chans == 1 ? 20 :
+ n_chans == 2 ? 40 :
+ n_chans == 4 ? 80 :
+ -1);
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ double total_weight;
+ struct acs_bias *bias, tmp_bias;
+
+ chan = &iface->current_mode->channels[i];
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ 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 &&
+ iface->conf->ieee80211n &&
+ iface->conf->secondary_channel &&
+ !acs_usable_ht40_chan(chan)) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
+ chan->chan);
+ continue;
+ }
+
+ 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;
+ }
+
+ factor = 0;
+ if (acs_usable_chan(chan))
+ factor = chan->interference_factor;
+ total_weight = 1;
+
+ for (j = 1; j < n_chans; j++) {
+ adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+ if (!adj_chan)
+ break;
+
+ if (acs_usable_chan(adj_chan)) {
+ factor += adj_chan->interference_factor;
+ total_weight += 1;
+ }
+ }
+
+ if (j != n_chans) {
+ wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+ chan->chan);
+ continue;
+ }
+
+ /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+ * channel interference factor. */
+ if (is_24ghz_mode(iface->current_mode->mode)) {
+ for (j = 0; j < n_chans; j++) {
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) - 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_NEXT_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 5);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_ADJ_WEIGHT;
+ }
+
+ adj_chan = acs_find_chan(iface, chan->freq +
+ (j * 20) + 10);
+ if (adj_chan && acs_usable_chan(adj_chan)) {
+ factor += ACS_NEXT_ADJ_WEIGHT *
+ adj_chan->interference_factor;
+ total_weight += ACS_NEXT_ADJ_WEIGHT;
+ }
+ }
+ }
+
+ factor /= total_weight;
+
+ bias = NULL;
+ if (iface->conf->acs_chan_bias) {
+ for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+ bias = &iface->conf->acs_chan_bias[k];
+ if (bias->channel == chan->chan)
+ break;
+ bias = NULL;
+ }
+ } else if (is_24ghz_mode(iface->current_mode->mode) &&
+ is_common_24ghz_chan(chan->chan)) {
+ tmp_bias.channel = chan->chan;
+ tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+ bias = &tmp_bias;
+ }
+
+ if (bias) {
+ factor *= bias->bias;
+ wpa_printf(MSG_DEBUG,
+ "ACS: * channel %d: total interference = %Lg (%f bias)",
+ chan->chan, factor, bias->bias);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "ACS: * channel %d: total interference = %Lg",
+ chan->chan, factor);
+ }
+
+ if (acs_usable_chan(chan) &&
+ (!ideal_chan || factor < ideal_factor)) {
+ ideal_factor = factor;
+ ideal_chan = chan;
+ }
+
+ /* This channel would at least be usable */
+ if (!rand_chan)
+ rand_chan = chan;
+ }
+
+ if (ideal_chan) {
+ wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
+ ideal_chan->chan, ideal_chan->freq, ideal_factor);
+ return ideal_chan;
+ }
+
+ return rand_chan;
+}
+
+
+static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
+{
+ int offset;
+
+ wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ offset = 2 * iface->conf->secondary_channel;
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ offset = 6;
+ 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");
+ return;
+ }
+
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ iface->conf->channel + offset;
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+ if (!iface->chans_surveyed) {
+ wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+ return -1;
+ }
+
+ if (!acs_surveys_are_sufficient(iface)) {
+ wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+ return -1;
+ }
+
+ acs_survey_all_chans_intereference_factor(iface);
+ return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+ int err;
+
+ err = acs_study_survey_based(iface);
+ if (err == 0)
+ return 0;
+
+ /* TODO: If no surveys are available/sufficient this is a good
+ * place to fallback to BSS-based ACS */
+
+ return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *ideal_chan;
+ int err;
+
+ err = acs_study_options(iface);
+ if (err < 0) {
+ wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+ goto fail;
+ }
+
+ ideal_chan = acs_find_ideal_chan(iface);
+ if (!ideal_chan) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+ err = -1;
+ goto fail;
+ }
+
+ iface->conf->channel = ideal_chan->chan;
+
+ if (iface->conf->ieee80211ac)
+ acs_adjust_vht_center_freq(iface);
+
+ err = 0;
+fail:
+ /*
+ * hostapd_setup_interface_complete() will return -1 on failure,
+ * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
+ */
+ if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
+ acs_cleanup(iface);
+ return;
+ }
+
+ /* This can possibly happen if channel parameters (secondary
+ * channel, center frequencies) are misconfigured */
+ wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
+ acs_fail(iface);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+ int err;
+
+ iface->scan_cb = NULL;
+
+ wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+ iface->conf->acs_num_scans);
+
+ err = hostapd_drv_get_survey(iface->bss[0], 0);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+ goto fail;
+ }
+
+ if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
+ err = acs_request_scan(iface);
+ if (err) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
+ goto fail;
+ }
+
+ return;
+ }
+
+ acs_study(iface);
+ return;
+fail:
+ hostapd_acs_completed(iface, 1);
+ acs_fail(iface);
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+ struct wpa_driver_scan_params params;
+ struct hostapd_channel_data *chan;
+ int i, *freq;
+
+ os_memset(&params, 0, sizeof(params));
+ params.freqs = os_calloc(iface->current_mode->num_channels + 1,
+ sizeof(params.freqs[0]));
+ if (params.freqs == NULL)
+ return -1;
+
+ freq = params.freqs;
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ *freq++ = chan->freq;
+ }
+ *freq = 0;
+
+ iface->scan_cb = acs_scan_complete;
+
+ wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+ iface->acs_num_completed_scans + 1,
+ iface->conf->acs_num_scans);
+
+ if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+ wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
+ acs_cleanup(iface);
+ os_free(params.freqs);
+ return -1;
+ }
+
+ os_free(params.freqs);
+ return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ int err;
+
+ wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+ wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+ err = hostapd_drv_do_acs(iface->bss[0]);
+ if (err)
+ return HOSTAPD_CHAN_INVALID;
+ return HOSTAPD_CHAN_ACS;
+ }
+
+ acs_cleanup(iface);
+
+ err = acs_request_scan(iface);
+ if (err < 0)
+ return HOSTAPD_CHAN_INVALID;
+
+ hostapd_set_state(iface, HAPD_IFACE_ACS);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
+ return HOSTAPD_CHAN_ACS;
+}
diff --git a/src/ap/acs.h b/src/ap/acs.h
new file mode 100644
index 0000000000000..fc85259e85d53
--- /dev/null
+++ b/src/ap/acs.h
@@ -0,0 +1,27 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+ return HOSTAPD_CHAN_INVALID;
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 25d26e5e77a70..76011dc07fd4b 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -1,6 +1,6 @@
/*
* hostapd / Configuration helper functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -73,6 +73,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
#ifdef CONFIG_IEEE80211W
bss->assoc_sa_query_max_timeout = 1000;
bss->assoc_sa_query_retry_timeout = 201;
+ bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
#endif /* CONFIG_IEEE80211W */
#ifdef EAP_SERVER_FAST
/* both anonymous and authenticated provisioning */
@@ -89,6 +90,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
#endif /* CONFIG_IEEE80211R */
bss->radius_das_time_window = 300;
+
+ bss->sae_anti_clogging_threshold = 5;
}
@@ -104,9 +107,9 @@ struct hostapd_config * hostapd_config_defaults(void)
const struct hostapd_wmm_ac_params ac_be =
{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
- { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+ { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
- { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+ { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
const struct hostapd_tx_queue_params txq_bk =
{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
const struct hostapd_tx_queue_params txq_be =
@@ -128,9 +131,17 @@ struct hostapd_config * hostapd_config_defaults(void)
os_free(bss);
return NULL;
}
+ conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
+ if (conf->bss == NULL) {
+ os_free(conf);
+ os_free(bss);
+ return NULL;
+ }
+ conf->bss[0] = bss;
bss->radius = os_zalloc(sizeof(*bss->radius));
if (bss->radius == NULL) {
+ os_free(conf->bss);
os_free(conf);
os_free(bss);
return NULL;
@@ -139,12 +150,13 @@ struct hostapd_config * hostapd_config_defaults(void)
hostapd_config_defaults_bss(bss);
conf->num_bss = 1;
- conf->bss = bss;
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;
conf->wmm_ac_params[0] = ac_be;
conf->wmm_ac_params[1] = ac_bk;
@@ -161,6 +173,18 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->ap_table_max_size = 255;
conf->ap_table_expiration_time = 60;
+#ifdef CONFIG_TESTING_OPTIONS
+ conf->ignore_probe_probability = 0.0;
+ conf->ignore_auth_probability = 0.0;
+ conf->ignore_assoc_probability = 0.0;
+ conf->ignore_reassoc_probability = 0.0;
+ conf->corrupt_gtk_rekey_mic_probability = 0.0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+ conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
return conf;
}
@@ -315,20 +339,6 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
}
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b)
-{
- int i;
-
- if (a->idx != b->idx || a->default_len != b->default_len)
- return 1;
- for (i = 0; i < NUM_WEP_KEYS; i++)
- if (a->len[i] != b->len[i] ||
- os_memcmp(a->key[i], b->key[i], a->len[i]) != 0)
- return 1;
- return 0;
-}
-
-
static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
int num_servers)
{
@@ -365,10 +375,11 @@ static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
}
-static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
{
+ hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity);
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
os_free(user);
}
@@ -377,28 +388,35 @@ static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
{
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
- os_free(keys->key[i]);
+ bin_clear_free(keys->key[i], keys->len[i]);
keys->key[i] = NULL;
}
}
-static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+ struct hostapd_wpa_psk *psk, *tmp;
+
+ for (psk = *l; psk;) {
+ tmp = psk;
+ psk = psk->next;
+ bin_clear_free(tmp, sizeof(*tmp));
+ }
+ *l = NULL;
+}
+
+
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
- struct hostapd_wpa_psk *psk, *prev;
struct hostapd_eap_user *user, *prev_user;
if (conf == NULL)
return;
- psk = conf->ssid.wpa_psk;
- while (psk) {
- prev = psk;
- psk = psk->next;
- os_free(prev);
- }
+ hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
- os_free(conf->ssid.wpa_passphrase);
+ str_clear_free(conf->ssid.wpa_passphrase);
os_free(conf->ssid.wpa_psk_file);
hostapd_config_free_wep(&conf->ssid.wep);
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -413,15 +431,17 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
}
os_free(conf->eap_user_sqlite);
- os_free(conf->dump_log_name);
os_free(conf->eap_req_id_text);
+ os_free(conf->erp_domain);
os_free(conf->accept_mac);
os_free(conf->deny_mac);
os_free(conf->nas_identifier);
- hostapd_config_free_radius(conf->radius->auth_servers,
- conf->radius->num_auth_servers);
- hostapd_config_free_radius(conf->radius->acct_servers,
- conf->radius->num_acct_servers);
+ if (conf->radius) {
+ hostapd_config_free_radius(conf->radius->auth_servers,
+ conf->radius->num_auth_servers);
+ hostapd_config_free_radius(conf->radius->acct_servers,
+ conf->radius->num_acct_servers);
+ }
hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
os_free(conf->rsn_preauth_interfaces);
@@ -430,29 +450,17 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->server_cert);
os_free(conf->private_key);
os_free(conf->private_key_passwd);
+ os_free(conf->ocsp_stapling_response);
os_free(conf->dh_file);
+ os_free(conf->openssl_ciphers);
os_free(conf->pac_opaque_encr_key);
os_free(conf->eap_fast_a_id);
os_free(conf->eap_fast_a_id_info);
os_free(conf->eap_sim_db);
os_free(conf->radius_server_clients);
- os_free(conf->test_socket);
os_free(conf->radius);
os_free(conf->radius_das_shared_secret);
hostapd_config_free_vlan(conf);
- if (conf->ssid.dyn_vlan_keys) {
- struct hostapd_ssid *ssid = &conf->ssid;
- size_t i;
- for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
- if (ssid->dyn_vlan_keys[i] == NULL)
- continue;
- hostapd_config_free_wep(ssid->dyn_vlan_keys[i]);
- os_free(ssid->dyn_vlan_keys[i]);
- }
- os_free(ssid->dyn_vlan_keys);
- ssid->dyn_vlan_keys = NULL;
- }
-
os_free(conf->time_zone);
#ifdef CONFIG_IEEE80211R
@@ -495,6 +503,12 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->model_description);
os_free(conf->model_url);
os_free(conf->upc);
+ {
+ unsigned int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+ wpabuf_free(conf->wps_vendor_ext[i]);
+ }
wpabuf_free(conf->wps_nfc_dh_pubkey);
wpabuf_free(conf->wps_nfc_dh_privkey);
wpabuf_free(conf->wps_nfc_dev_pw);
@@ -516,9 +530,36 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->hs20_wan_metrics);
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
+ os_free(conf->hs20_icons);
+ if (conf->hs20_osu_providers) {
+ size_t i;
+ for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+ struct hs20_osu_provider *p;
+ size_t j;
+ p = &conf->hs20_osu_providers[i];
+ os_free(p->friendly_name);
+ os_free(p->server_uri);
+ os_free(p->method_list);
+ for (j = 0; j < p->icons_count; j++)
+ os_free(p->icons[j]);
+ os_free(p->icons);
+ os_free(p->osu_nai);
+ os_free(p->service_desc);
+ }
+ os_free(conf->hs20_osu_providers);
+ }
+ os_free(conf->subscr_remediation_url);
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
+
+ os_free(conf->sae_groups);
+
+ os_free(conf->wowlan_triggers);
+
+ os_free(conf->server_id);
+
+ os_free(conf);
}
@@ -534,10 +575,15 @@ void hostapd_config_free(struct hostapd_config *conf)
return;
for (i = 0; i < conf->num_bss; i++)
- hostapd_config_free_bss(&conf->bss[i]);
+ hostapd_config_free_bss(conf->bss[i]);
os_free(conf->bss);
os_free(conf->supported_rates);
os_free(conf->basic_rates);
+ os_free(conf->chanlist);
+ os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+ os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
os_free(conf);
}
@@ -594,11 +640,23 @@ int hostapd_rate_found(int *list, int rate)
}
-const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
{
struct hostapd_vlan *v = vlan;
while (v) {
if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+ return 1;
+ v = v->next;
+ }
+ return 0;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+ struct hostapd_vlan *v = vlan;
+ while (v) {
+ if (v->vlan_id == vlan_id)
return v->ifname;
v = v->next;
}
@@ -607,14 +665,30 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
- const u8 *addr, const u8 *prev_psk)
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
{
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
+ if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " p2p_dev_addr=" MACSTR " prev_psk=%p",
+ MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
+ addr = NULL; /* Use P2P Device Address for matching */
+ } else {
+ wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+ " prev_psk=%p",
+ MAC2STR(addr), prev_psk);
+ }
+
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
- (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0))
+ (psk->group ||
+ (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+ (!addr && p2p_dev_addr &&
+ os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+ 0)))
return psk->psk;
if (psk->psk == prev_psk)
@@ -623,3 +697,250 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
return NULL;
}
+
+
+static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+ struct hostapd_config *conf,
+ int full_config)
+{
+ if (full_config && bss->ieee802_1x && !bss->eap_server &&
+ !bss->radius->auth_servers) {
+ wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
+ "EAP authenticator configured).");
+ return -1;
+ }
+
+ if (bss->wpa) {
+ int wep, i;
+
+ wep = bss->default_wep_key_len > 0 ||
+ bss->individual_wep_key_len > 0;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ if (bss->ssid.wep.keys_set) {
+ wep = 1;
+ break;
+ }
+ }
+
+ if (wep) {
+ wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+ return -1;
+ }
+ }
+
+ if (full_config && bss->wpa &&
+ bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+ wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+ "RADIUS checking (macaddr_acl=2) enabled.");
+ return -1;
+ }
+
+ if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+ bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
+ bss->ssid.wpa_psk_file == NULL &&
+ (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+ bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
+ wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
+ "is not configured.");
+ return -1;
+ }
+
+ if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) {
+ size_t i;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (conf->bss[i] != bss &&
+ (hostapd_mac_comp(conf->bss[i]->bssid,
+ bss->bssid) == 0)) {
+ wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
+ " on interface '%s' and '%s'.",
+ MAC2STR(bss->bssid),
+ conf->bss[i]->iface, bss->iface);
+ return -1;
+ }
+ }
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+ (bss->nas_identifier == NULL ||
+ os_strlen(bss->nas_identifier) < 1 ||
+ os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
+ wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
+ "nas_identifier to be configured as a 1..48 octet "
+ "string");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+ if (full_config && conf->ieee80211n &&
+ conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+ bss->disable_11n = 1;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+ "allowed, disabling HT capabilities");
+ }
+
+ if (full_config && conf->ieee80211n &&
+ bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+ bss->disable_11n = 1;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+ "allowed, disabling HT capabilities");
+ }
+
+ if (full_config && conf->ieee80211n && bss->wpa &&
+ !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+ {
+ bss->disable_11n = 1;
+ wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
+ "requires CCMP/GCMP to be enabled, disabling HT "
+ "capabilities");
+ }
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_WPS
+ if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+ wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+ "configuration forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+
+ if (full_config && bss->wps_state &&
+ bss->ssid.wep.keys_set && bss->wpa == 0) {
+ wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+ "disabled");
+ bss->wps_state = 0;
+ }
+
+ if (full_config && bss->wps_state && bss->wpa &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+ wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+ "WPA2/CCMP forced WPS to be disabled");
+ bss->wps_state = 0;
+ }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+ if (full_config && bss->hs20 &&
+ (!(bss->wpa & 2) ||
+ !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+ WPA_CIPHER_CCMP_256 |
+ WPA_CIPHER_GCMP_256)))) {
+ wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+ "configuration is required for Hotspot 2.0 "
+ "functionality");
+ return -1;
+ }
+#endif /* CONFIG_HS20 */
+
+ return 0;
+}
+
+
+int hostapd_config_check(struct hostapd_config *conf, int full_config)
+{
+ size_t i;
+
+ if (full_config && conf->ieee80211d &&
+ (!conf->country[0] || !conf->country[1])) {
+ wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
+ "setting the country_code");
+ return -1;
+ }
+
+ if (full_config && conf->ieee80211h && !conf->ieee80211d) {
+ wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+ "IEEE 802.11d enabled");
+ return -1;
+ }
+
+ if (full_config && conf->local_pwr_constraint != -1 &&
+ !conf->ieee80211d) {
+ wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+ return -1;
+ }
+
+ if (full_config && conf->spectrum_mgmt_required &&
+ conf->local_pwr_constraint == -1) {
+ wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+ return -1;
+ }
+
+ for (i = 0; i < conf->num_bss; i++) {
+ if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config)
+{
+ if (bss->individual_wep_key_len == 0) {
+ /* individual keys are not use; can use key idx0 for
+ * broadcast keys */
+ bss->broadcast_key_idx_min = 0;
+ }
+
+ if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+ bss->rsn_pairwise = bss->wpa_pairwise;
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+ bss->rsn_pairwise);
+
+ if (full_config) {
+ bss->radius->auth_server = bss->radius->auth_servers;
+ bss->radius->acct_server = bss->radius->acct_servers;
+ }
+
+ if (bss->wpa && bss->ieee802_1x) {
+ bss->ssid.security_policy = SECURITY_WPA;
+ } else if (bss->wpa) {
+ bss->ssid.security_policy = SECURITY_WPA_PSK;
+ } else if (bss->ieee802_1x) {
+ int cipher = WPA_CIPHER_NONE;
+ bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+ bss->ssid.wep.default_len = bss->default_wep_key_len;
+ if (full_config && bss->default_wep_key_len) {
+ cipher = bss->default_wep_key_len >= 13 ?
+ WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+ } else if (full_config && bss->ssid.wep.keys_set) {
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
+ else
+ cipher = WPA_CIPHER_WEP40;
+ }
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ if (full_config)
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+ } else if (bss->ssid.wep.keys_set) {
+ int cipher = WPA_CIPHER_WEP40;
+ if (bss->ssid.wep.len[0] >= 13)
+ cipher = WPA_CIPHER_WEP104;
+ bss->ssid.security_policy = SECURITY_STATIC_WEP;
+ bss->wpa_group = cipher;
+ bss->wpa_pairwise = cipher;
+ bss->rsn_pairwise = cipher;
+ if (full_config)
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+ } else if (bss->osen) {
+ bss->ssid.security_policy = SECURITY_OSEN;
+ bss->wpa_group = WPA_CIPHER_CCMP;
+ bss->wpa_pairwise = 0;
+ bss->rsn_pairwise = WPA_CIPHER_CCMP;
+ } else {
+ bss->ssid.security_policy = SECURITY_PLAINTEXT;
+ bss->wpa_group = WPA_CIPHER_NONE;
+ bss->wpa_pairwise = WPA_CIPHER_NONE;
+ bss->rsn_pairwise = WPA_CIPHER_NONE;
+ if (full_config)
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+ }
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index a1d2b048b5131..961d2dd389f87 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -15,6 +15,34 @@
#include "common/ieee802_11_common.h"
#include "wps/wps.h"
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+ u8 meshid[32];
+ u8 meshid_len;
+ /* Active Path Selection Protocol Identifier */
+ u8 mesh_pp_id;
+ /* Active Path Selection Metric Identifier */
+ u8 mesh_pm_id;
+ /* Congestion Control Mode Identifier */
+ u8 mesh_cc_id;
+ /* Synchronization Protocol Identifier */
+ u8 mesh_sp_id;
+ /* Authentication Protocol Identifier */
+ u8 mesh_auth_id;
+ u8 *ies;
+ int ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+ unsigned int security;
+ int dot11MeshMaxRetries;
+ int dot11MeshRetryTimeout; /* msec */
+ int dot11MeshConfirmTimeout; /* msec */
+ int dot11MeshHoldingTimeout; /* msec */
+};
+
#define MAX_STA_COUNT 2007
#define MAX_VLAN_ID 4094
@@ -45,7 +73,8 @@ typedef enum hostap_security_policy {
SECURITY_STATIC_WEP = 1,
SECURITY_IEEE_802_1X = 2,
SECURITY_WPA_PSK = 3,
- SECURITY_WPA = 4
+ SECURITY_WPA = 4,
+ SECURITY_OSEN = 5
} secpolicy;
struct hostapd_ssid {
@@ -53,6 +82,8 @@ struct hostapd_ssid {
size_t ssid_len;
unsigned int ssid_set:1;
unsigned int utf8_ssid:1;
+ unsigned int wpa_passphrase_set:1;
+ unsigned int wpa_psk_set:1;
char vlan[IFNAMSIZ + 1];
secpolicy security_policy;
@@ -74,8 +105,6 @@ struct hostapd_ssid {
#ifdef CONFIG_FULL_DYNAMIC_VLAN
char *vlan_tagged_interface;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
- struct hostapd_wep_keys **dyn_vlan_keys;
- size_t max_dyn_vlan_keys;
};
@@ -107,6 +136,7 @@ struct hostapd_wpa_psk {
int group;
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
};
struct hostapd_eap_user {
@@ -124,7 +154,10 @@ struct hostapd_eap_user {
unsigned int wildcard_prefix:1;
unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */
+ unsigned int remediation:1;
+ unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+ struct hostapd_radius_attr *accept_attr;
};
struct hostapd_radius_attr {
@@ -180,6 +213,7 @@ struct hostapd_nai_realm_data {
struct hostapd_bss_config {
char iface[IFNAMSIZ + 1];
char bridge[IFNAMSIZ + 1];
+ char vlan_bridge[IFNAMSIZ + 1];
char wds_bridge[IFNAMSIZ + 1];
enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
@@ -187,11 +221,10 @@ struct hostapd_bss_config {
unsigned int logger_syslog; /* module bitfield */
unsigned int logger_stdout; /* module bitfield */
- char *dump_log_name; /* file name for state dump (SIGUSR1) */
-
int max_num_sta; /* maximum number of STAs in station table */
int dtim_period;
+ int bss_load_update_period;
int ieee802_1x; /* use IEEE 802.1X */
int eapol_version;
@@ -200,6 +233,7 @@ struct hostapd_bss_config {
struct hostapd_eap_user *eap_user;
char *eap_user_sqlite;
char *eap_sim_db;
+ int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;
@@ -226,6 +260,8 @@ struct hostapd_bss_config {
int wep_rekeying_period;
int broadcast_key_idx_min, broadcast_key_idx_max;
int eap_reauth_period;
+ int erp_send_reauth_start;
+ char *erp_domain;
int ieee802_11f; /* use IEEE 802.11f (IAPP) */
char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
@@ -242,6 +278,7 @@ struct hostapd_bss_config {
int num_deny_mac;
int wds_sta;
int isolate;
+ int start_disabled;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
@@ -250,6 +287,7 @@ struct hostapd_bss_config {
int wpa_key_mgmt;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
unsigned int assoc_sa_query_max_timeout;
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
@@ -294,7 +332,9 @@ struct hostapd_bss_config {
char *private_key;
char *private_key_passwd;
int check_crl;
+ char *ocsp_stapling_response;
char *dh_file;
+ char *openssl_ciphers;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@@ -309,10 +349,9 @@ struct hostapd_bss_config {
char *radius_server_clients;
int radius_server_auth_port;
+ int radius_server_acct_port;
int radius_server_ipv6;
- char *test_socket; /* UNIX domain socket path for driver_test */
-
int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
* address instead of individual address
* (for driver_wired.c).
@@ -324,7 +363,7 @@ struct hostapd_bss_config {
int wmm_enabled;
int wmm_uapsd;
- struct hostapd_vlan *vlan, *vlan_tail;
+ struct hostapd_vlan *vlan;
macaddr bssid;
@@ -340,6 +379,7 @@ struct hostapd_bss_config {
int wps_state;
#ifdef CONFIG_WPS
+ int wps_independent;
int ap_setup_locked;
u8 uuid[16];
char *wps_pin_requests;
@@ -356,6 +396,7 @@ struct hostapd_bss_config {
u8 *extra_cred;
size_t extra_cred_len;
int wps_cred_processing;
+ int force_per_enrollee_psk;
u8 *ap_settings;
size_t ap_settings_len;
char *upnp_iface;
@@ -365,12 +406,14 @@ struct hostapd_bss_config {
char *model_url;
char *upc;
struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
+ int wps_nfc_pw_from_config;
int wps_nfc_dev_pw_id;
struct wpabuf *wps_nfc_dh_pubkey;
struct wpabuf *wps_nfc_dh_privkey;
struct wpabuf *wps_nfc_dev_pw;
#endif /* CONFIG_WPS */
int pbc_in_m1;
+ char *server_id;
#define P2P_ENABLED BIT(0)
#define P2P_GROUP_OWNER BIT(1)
@@ -378,6 +421,12 @@ struct hostapd_bss_config {
#define P2P_MANAGE BIT(3)
#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
int p2p;
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
int disassoc_low_ack;
int skip_inactivity_poll;
@@ -436,9 +485,15 @@ struct hostapd_bss_config {
u16 gas_comeback_delay;
int gas_frag_limit;
+ u8 qos_map_set[16 + 2 * 21];
+ unsigned int qos_map_set_len;
+
+ int osen;
+ int proxy_arp;
#ifdef CONFIG_HS20
int hs20;
int disable_dgaf;
+ u16 anqp_domain_id;
unsigned int hs20_oper_friendly_name_count;
struct hostapd_lang_string *hs20_oper_friendly_name;
u8 *hs20_wan_metrics;
@@ -446,6 +501,32 @@ struct hostapd_bss_config {
size_t hs20_connection_capability_len;
u8 *hs20_operating_class;
u8 hs20_operating_class_len;
+ struct hs20_icon {
+ u16 width;
+ u16 height;
+ char language[3];
+ char type[256];
+ char name[256];
+ char file[256];
+ } *hs20_icons;
+ size_t hs20_icons_count;
+ u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+ size_t osu_ssid_len;
+ struct hs20_osu_provider {
+ unsigned int friendly_name_count;
+ struct hostapd_lang_string *friendly_name;
+ char *server_uri;
+ int *method_list;
+ char **icons;
+ size_t icons_count;
+ char *osu_nai;
+ unsigned int service_desc_count;
+ struct hostapd_lang_string *service_desc;
+ } *hs20_osu_providers, *last_osu;
+ size_t hs20_osu_providers_count;
+ unsigned int hs20_deauth_req_timeout;
+ char *subscr_remediation_url;
+ u8 subscr_remediation_method;
#endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -455,6 +536,23 @@ struct hostapd_bss_config {
#endif /* CONFIG_RADIUS_TEST */
struct wpabuf *vendor_elements;
+
+ unsigned int sae_anti_clogging_threshold;
+ int *sae_groups;
+
+ char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 bss_load_test[5];
+ u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+ int mesh;
+
+ int radio_measurements;
+
+ int vendor_vht;
};
@@ -462,7 +560,7 @@ struct hostapd_bss_config {
* struct hostapd_config - Per-radio interface configuration
*/
struct hostapd_config {
- struct hostapd_bss_config *bss, *last_bss;
+ struct hostapd_bss_config **bss, *last_bss;
size_t num_bss;
u16 beacon_int;
@@ -470,6 +568,7 @@ struct hostapd_config {
int fragm_threshold;
u8 send_probe_response;
u8 channel;
+ int *chanlist;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
enum {
LONG_PREAMBLE = 0,
@@ -480,6 +579,7 @@ struct hostapd_config {
int *basic_rates;
const struct wpa_driver_ops *driver;
+ char *driver_params;
int ap_table_max_size;
int ap_table_expiration_time;
@@ -493,6 +593,18 @@ struct hostapd_config {
int ieee80211d;
+ int ieee80211h; /* DFS */
+
+ /*
+ * Local power constraint is an octet encoded as an unsigned integer in
+ * units of decibels. Invalid value -1 indicates that Power Constraint
+ * element will not be added.
+ */
+ int local_pwr_constraint;
+
+ /* Control Spectrum Management bit */
+ int spectrum_mgmt_required;
+
struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
/*
@@ -509,12 +621,34 @@ struct hostapd_config {
int ieee80211n;
int secondary_channel;
int require_ht;
+ int obss_interval;
u32 vht_capab;
int ieee80211ac;
int require_vht;
u8 vht_oper_chwidth;
u8 vht_oper_centr_freq_seg0_idx;
u8 vht_oper_centr_freq_seg1_idx;
+
+#ifdef CONFIG_P2P
+ u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ double ignore_probe_probability;
+ double ignore_auth_probability;
+ double ignore_assoc_probability;
+ double ignore_reassoc_probability;
+ double corrupt_gtk_rekey_mic_probability;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_scans;
+ struct acs_bias {
+ int channel;
+ double bias;
+ } *acs_chan_bias;
+ unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
};
@@ -522,18 +656,24 @@ int hostapd_mac_comp(const void *a, const void *b);
int hostapd_mac_comp_empty(const void *a);
struct hostapd_config * hostapd_config_defaults(void);
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, int *vlan_id);
int hostapd_rate_found(int *list, int rate);
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
- struct hostapd_wep_keys *b);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
- const u8 *addr, const u8 *prev_psk);
+ const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
int vlan_id);
struct hostapd_radius_attr *
hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+ int full_config);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 02da25b71483d..e16306c4e1065 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -9,8 +9,8 @@
#include "utils/includes.h"
#include "utils/common.h"
-#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "wps/wps.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -129,14 +129,14 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P_MANAGER */
-#ifdef CONFIG_WPS2
+#ifdef CONFIG_WPS
if (hapd->conf->wps_state) {
struct wpabuf *a = wps_build_assoc_resp_ie();
if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
wpabuf_put_buf(assocresp, a);
wpabuf_free(a);
}
-#endif /* CONFIG_WPS2 */
+#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P_MANAGER
if (hapd->conf->p2p & P2P_MANAGE) {
@@ -171,8 +171,27 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
goto fail;
wpabuf_put_data(proberesp, buf, pos - buf);
}
+
+ pos = hostapd_eid_osen(hapd, buf);
+ if (pos != buf) {
+ if (wpabuf_resize(&beacon, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(beacon, buf, pos - buf);
+
+ if (wpabuf_resize(&proberesp, pos - buf) != 0)
+ goto fail;
+ wpabuf_put_data(proberesp, buf, pos - buf);
+ }
#endif /* CONFIG_HS20 */
+ if (hapd->conf->vendor_elements) {
+ size_t add = wpabuf_len(hapd->conf->vendor_elements);
+ if (wpabuf_resize(&beacon, add) == 0)
+ wpabuf_put_buf(beacon, hapd->conf->vendor_elements);
+ if (wpabuf_resize(&proberesp, add) == 0)
+ wpabuf_put_buf(proberesp, hapd->conf->vendor_elements);
+ }
+
*beacon_ret = beacon;
*proberesp_ret = proberesp;
*assocresp_ret = assocresp;
@@ -262,7 +281,8 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
params.wpa = hapd->conf->wpa;
params.ieee802_1x = hapd->conf->ieee802_1x;
params.wpa_group = hapd->conf->wpa_group;
- params.wpa_pairwise = hapd->conf->wpa_pairwise;
+ params.wpa_pairwise = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
params.rsn_preauth = hapd->conf->rsn_preauth;
#ifdef CONFIG_IEEE80211W
@@ -278,7 +298,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
- NULL, NULL, force_ifname, if_addr, NULL);
+ NULL, NULL, force_ifname, if_addr, NULL, 0);
}
@@ -288,19 +308,19 @@ int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
}
-int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
- int val)
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val)
{
const char *bridge = NULL;
if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
- return 0;
+ return -1;
if (hapd->conf->wds_bridge[0])
bridge = hapd->conf->wds_bridge;
else if (hapd->conf->bridge[0])
bridge = hapd->conf->bridge;
return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
- bridge);
+ bridge, ifname_wds);
}
@@ -338,7 +358,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
const u8 *supp_rates, size_t supp_rates_len,
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
- u32 flags, u8 qosinfo)
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode)
{
struct hostapd_sta_add_params params;
@@ -355,6 +376,9 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.supp_rates_len = supp_rates_len;
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
+ params.vht_capabilities = vht_capab;
+ params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+ params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
params.qosinfo = qosinfo;
return hapd->driver->sta_add(hapd->drv_priv, &params);
@@ -407,20 +431,21 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
void **drv_priv, char *force_ifname, u8 *if_addr,
- const char *bridge)
+ const char *bridge, int use_existing)
{
if (hapd->driver == NULL || hapd->driver->if_add == NULL)
return -1;
return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
bss_ctx, drv_priv, force_ifname, if_addr,
- bridge);
+ bridge, use_existing);
}
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname)
{
- if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->if_remove == NULL)
return -1;
return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
}
@@ -453,20 +478,25 @@ int hostapd_flush(struct hostapd_data *hapd)
}
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
- int channel, int ht_enabled, int sec_channel_offset)
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
{
struct hostapd_freq_params data;
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth,
+ center_segment0, center_segment1,
+ hapd->iface->current_mode ?
+ hapd->iface->current_mode->vht_capab : 0))
+ return -1;
+
if (hapd->driver == NULL)
return 0;
if (hapd->driver->set_freq == NULL)
return 0;
- os_memset(&data, 0, sizeof(data));
- data.mode = mode;
- data.freq = freq;
- data.channel = channel;
- data.ht_enabled = ht_enabled;
- data.sec_channel_offset = sec_channel_offset;
return hapd->driver->set_freq(hapd->drv_priv, &data);
}
@@ -616,7 +646,7 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
const u8 *peer, u8 *buf, u16 *buf_len)
{
if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
- return 0;
+ return -1;
return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
buf_len);
}
@@ -632,3 +662,66 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
hapd->own_addr, hapd->own_addr, data,
len, 0);
}
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_freq_params data;
+ int res;
+
+ if (!hapd->driver || !hapd->driver->start_dfs_cac)
+ return 0;
+
+ if (!iface->conf->ieee80211h) {
+ wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+ "is not enabled");
+ return -1;
+ }
+
+ if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+ vht_enabled, sec_channel_offset,
+ vht_oper_chwidth, center_segment0,
+ center_segment1,
+ iface->current_mode->vht_capab)) {
+ wpa_printf(MSG_ERROR, "Can't set freq params");
+ return -1;
+ }
+
+ res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+ if (!res) {
+ iface->cac_started = 1;
+ os_get_reltime(&iface->dfs_cac_start);
+ }
+
+ return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+ const u8 *qos_map_set, u8 qos_map_set_len)
+{
+ if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+ return 0;
+ return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+ qos_map_set_len);
+}
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+ struct drv_acs_params params;
+
+ if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+ return 0;
+ os_memset(&params, 0, sizeof(params));
+ params.hw_mode = hapd->iface->conf->hw_mode;
+ params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+ params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+ return hapd->driver->do_acs(hapd->drv_priv, &params);
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 9c53b99d82a26..5d07e71f1bf13 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -1,6 +1,6 @@
/*
* hostapd - Driver operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,6 +13,8 @@ enum wpa_driver_if_type;
struct wpa_bss_params;
struct wpa_driver_scan_params;
struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
u32 hostapd_sta_flags_to_drv(u32 flags);
int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
@@ -30,14 +32,15 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
int enabled);
int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
-int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
- int val);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+ const u8 *addr, int aid, int val);
int hostapd_sta_add(struct hostapd_data *hapd,
const u8 *addr, u16 aid, u16 capability,
const u8 *supp_rates, size_t supp_rates_len,
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
- u32 flags, u8 qosinfo);
+ const struct ieee80211_vht_capabilities *vht_capab,
+ u32 flags, u8 qosinfo, u8 vht_opmode);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
size_t elem_len);
@@ -46,7 +49,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
void **drv_priv, char *force_ifname, u8 *if_addr,
- const char *bridge);
+ const char *bridge, int use_existing);
int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
const char *ifname);
int hostapd_set_ieee8021x(struct hostapd_data *hapd,
@@ -54,8 +57,10 @@ int hostapd_set_ieee8021x(struct hostapd_data *hapd,
int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
const u8 *addr, int idx, u8 *seq);
int hostapd_flush(struct hostapd_data *hapd);
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
- int channel, int ht_enabled, int sec_channel_offset);
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+ int freq, int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1);
int hostapd_set_rts(struct hostapd_data *hapd, int rts);
int hostapd_set_frag(struct hostapd_data *hapd, int frag);
int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
@@ -97,6 +102,12 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
int reassoc, u16 status, const u8 *ie, size_t len);
int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+ enum hostapd_hw_mode mode, int freq,
+ int channel, int ht_enabled, int vht_enabled,
+ int sec_channel_offset, int vht_oper_chwidth,
+ int center_segment0, int center_segment1);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
#include "drivers/driver.h"
@@ -105,6 +116,9 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
enum wnm_oper oper, const u8 *peer,
u8 *buf, u16 *buf_len);
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+ u8 qos_map_set_len);
+
static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
int enabled)
{
@@ -169,6 +183,14 @@ static inline int hostapd_drv_sta_clear_stats(struct hostapd_data *hapd,
return hapd->driver->sta_clear_stats(hapd->drv_priv, addr);
}
+static inline int hostapd_drv_set_acl(struct hostapd_data *hapd,
+ struct hostapd_acl_params *params)
+{
+ if (hapd->driver == NULL || hapd->driver->set_acl == NULL)
+ return 0;
+ return hapd->driver->set_acl(hapd->drv_priv, params);
+}
+
static inline int hostapd_drv_set_ap(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
@@ -213,4 +235,105 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
}
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+ unsigned int freq)
+{
+ if (hapd->driver == NULL)
+ return -1;
+ if (!hapd->driver->get_survey)
+ return -1;
+ return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+ if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+ return -1;
+ return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->get_radio_name == NULL)
+ return NULL;
+ return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
+ return -ENOTSUP;
+
+ return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ if (hapd->driver == NULL || hapd->driver->status == NULL)
+ return -1;
+ return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+ int version, const u8 *ipaddr,
+ int prefixlen, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_add_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+ prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+ u8 version, const u8 *ipaddr)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_delete_ip_neigh == NULL)
+ return -1;
+ return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+ ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+ enum drv_br_port_attr attr,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_port_set_attr == NULL)
+ return -1;
+ return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+ enum drv_br_net_param param,
+ unsigned int val)
+{
+ if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+ hapd->driver->br_set_net_param == NULL)
+ return -1;
+ return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+ int vendor_id, int subcmd,
+ const u8 *data, size_t data_len,
+ struct wpabuf *buf)
+{
+ if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+ return -1;
+ return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+ data_len, buf);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+ if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+ return 0;
+ return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 18090ca18c0b9..04a56a95efd91 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -14,7 +14,6 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ieee802_11.h"
@@ -33,7 +32,8 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
{
int i;
- if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+ if (iface->current_mode == NULL ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
iface->conf->channel != ap->channel)
return 0;
@@ -50,7 +50,7 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
}
-struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
+static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
{
struct ap_info *s;
@@ -87,34 +87,6 @@ static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
}
-static void ap_ap_iter_list_add(struct hostapd_iface *iface,
- struct ap_info *ap)
-{
- if (iface->ap_iter_list) {
- ap->iter_prev = iface->ap_iter_list->iter_prev;
- iface->ap_iter_list->iter_prev = ap;
- } else
- ap->iter_prev = ap;
- ap->iter_next = iface->ap_iter_list;
- iface->ap_iter_list = ap;
-}
-
-
-static void ap_ap_iter_list_del(struct hostapd_iface *iface,
- struct ap_info *ap)
-{
- if (iface->ap_iter_list == ap)
- iface->ap_iter_list = ap->iter_next;
- else
- ap->iter_prev->iter_next = ap->iter_next;
-
- if (ap->iter_next)
- ap->iter_next->iter_prev = ap->iter_prev;
- else if (iface->ap_iter_list)
- iface->ap_iter_list->iter_prev = ap->iter_prev;
-}
-
-
static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
{
ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
@@ -139,8 +111,8 @@ static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
if (s->hnext != NULL)
s->hnext = s->hnext->hnext;
else
- printf("AP: could not remove AP " MACSTR " from hash table\n",
- MAC2STR(ap->addr));
+ wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+ " from hash table", MAC2STR(ap->addr));
}
@@ -148,7 +120,6 @@ static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
{
ap_ap_hash_del(iface, ap);
ap_ap_list_del(iface, ap);
- ap_ap_iter_list_del(iface, ap);
iface->num_ap--;
os_free(ap);
@@ -171,25 +142,6 @@ static void hostapd_free_aps(struct hostapd_iface *iface)
}
-int ap_ap_for_each(struct hostapd_iface *iface,
- int (*func)(struct ap_info *s, void *data), void *data)
-{
- struct ap_info *s;
- int ret = 0;
-
- s = iface->ap_list;
-
- while (s) {
- ret = func(s, data);
- if (ret)
- break;
- s = s->next;
- }
-
- return ret;
-}
-
-
static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
{
struct ap_info *ap;
@@ -203,7 +155,6 @@ static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
ap_ap_list_add(iface, ap);
iface->num_ap++;
ap_ap_hash_add(iface, ap);
- ap_ap_iter_list_add(iface, ap);
if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
@@ -221,9 +172,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
struct hostapd_frame_info *fi)
{
struct ap_info *ap;
- struct os_time now;
int new_ap = 0;
- size_t len;
int set_beacon = 0;
if (iface->conf->ap_table_max_size < 1)
@@ -233,30 +182,17 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
if (!ap) {
ap = ap_ap_add(iface, mgmt->bssid);
if (!ap) {
- printf("Failed to allocate AP information entry\n");
+ wpa_printf(MSG_INFO,
+ "Failed to allocate AP information entry");
return;
}
new_ap = 1;
}
- ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
- ap->capability = le_to_host16(mgmt->u.beacon.capab_info);
-
- if (elems->ssid) {
- len = elems->ssid_len;
- if (len >= sizeof(ap->ssid))
- len = sizeof(ap->ssid) - 1;
- os_memcpy(ap->ssid, elems->ssid, len);
- ap->ssid[len] = '\0';
- ap->ssid_len = len;
- }
-
merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
elems->supp_rates, elems->supp_rates_len,
elems->ext_supp_rates, elems->ext_supp_rates_len);
- ap->wpa = elems->wpa_ie != NULL;
-
if (elems->erp_info && elems->erp_info_len == 1)
ap->erp = elems->erp_info[0];
else
@@ -264,6 +200,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
if (elems->ds_params && elems->ds_params_len == 1)
ap->channel = elems->ds_params[0];
+ else if (elems->ht_operation && elems->ht_operation_len >= 1)
+ ap->channel = elems->ht_operation[0];
else if (fi)
ap->channel = fi->channel;
@@ -272,11 +210,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
else
ap->ht_support = 0;
- ap->num_beacons++;
- os_get_time(&now);
- ap->last_beacon = now.sec;
- if (fi)
- ap->datarate = fi->datarate;
+ os_get_reltime(&ap->last_beacon);
if (!new_ap && ap != iface->ap_list) {
/* move AP entry into the beginning of the list so that the
@@ -288,17 +222,23 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
if (!iface->olbc &&
ap_list_beacon_olbc(iface, ap)) {
iface->olbc = 1;
- wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
- "protection", MAC2STR(ap->addr));
+ wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
+ " (channel %d) - enable protection",
+ MAC2STR(ap->addr), ap->channel);
set_beacon++;
}
#ifdef CONFIG_IEEE80211N
- if (!iface->olbc_ht && !ap->ht_support) {
+ if (!iface->olbc_ht && !ap->ht_support &&
+ (ap->channel == 0 ||
+ ap->channel == iface->conf->channel ||
+ ap->channel == iface->conf->channel +
+ iface->conf->secondary_channel * 4)) {
iface->olbc_ht = 1;
hostapd_ht_operation_update(iface);
wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
- " - enable protection", MAC2STR(ap->addr));
+ " (channel %d) - enable protection",
+ MAC2STR(ap->addr), ap->channel);
set_beacon++;
}
#endif /* CONFIG_IEEE80211N */
@@ -311,7 +251,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_iface *iface = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
struct ap_info *ap;
int set_beacon = 0;
@@ -320,12 +260,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
if (!iface->ap_list)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
while (iface->ap_list) {
ap = iface->ap_list->prev;
- if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
- now.sec)
+ if (!os_reltime_expired(&now, &ap->last_beacon,
+ iface->conf->ap_table_expiration_time))
break;
ap_free_ap(iface, ap);
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index f0b41259bda6e..93dc0eda88d33 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -14,42 +14,24 @@
struct ap_info {
/* Note: next/prev pointers are updated whenever a new beacon is
* received because these are used to find the least recently used
- * entries. iter_next/iter_prev are updated only when adding new BSSes
- * and when removing old ones. These should be used when iterating
- * through the table in a manner that allows beacons to be received
- * during the iteration. */
+ * entries. */
struct ap_info *next; /* next entry in AP list */
struct ap_info *prev; /* previous entry in AP list */
struct ap_info *hnext; /* next entry in hash table list */
- struct ap_info *iter_next; /* next entry in AP iteration list */
- struct ap_info *iter_prev; /* previous entry in AP iteration list */
u8 addr[6];
- u16 beacon_int;
- u16 capability;
u8 supported_rates[WLAN_SUPP_RATES_MAX];
- u8 ssid[33];
- size_t ssid_len;
- int wpa;
int erp; /* ERP Info or -1 if ERP info element not present */
int channel;
- int datarate; /* in 100 kbps */
int ht_support;
- unsigned int num_beacons; /* number of beacon frames received */
- os_time_t last_beacon;
-
- int already_seen; /* whether API call AP-NEW has already fetched
- * information about this AP */
+ struct os_reltime last_beacon;
};
struct ieee802_11_elems;
struct hostapd_frame_info;
-struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta);
-int ap_ap_for_each(struct hostapd_iface *iface,
- int (*func)(struct ap_info *s, void *data), void *data);
void ap_list_process_beacon(struct hostapd_iface *iface,
const struct ieee80211_mgmt *mgmt,
struct ieee802_11_elems *elems,
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index a9596947fafb2..13604edc49406 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -16,6 +16,7 @@
#include "wpa_auth.h"
#include "sta_info.h"
#include "ap_mlme.h"
+#include "hostapd.h"
#ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -80,7 +81,8 @@ void mlme_deauthenticate_indication(struct hostapd_data *hapd,
HOSTAPD_LEVEL_DEBUG,
"MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
MAC2STR(sta->addr), reason_code);
- mlme_deletekeys_request(hapd, sta);
+ if (!hapd->iface->driver_ap_teardown)
+ mlme_deletekeys_request(hapd, sta);
}
@@ -118,8 +120,6 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
* reassociation procedure that was initiated by that specific peer MAC entity.
*
* PeerSTAAddress = sta->addr
- *
- * sta->previous_ap contains the "Current AP" information from ReassocReq.
*/
void mlme_reassociate_indication(struct hostapd_data *hapd,
struct sta_info *sta)
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index d66d97e4a0db4..bd1778e418651 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -79,7 +79,10 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
+ user->accept_attr = eap_user->accept_attr;
return 0;
}
@@ -92,6 +95,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
os_memset(&srv, 0, sizeof(srv));
srv.client_file = conf->radius_server_clients;
srv.auth_port = conf->radius_server_auth_port;
+ srv.acct_port = conf->radius_server_acct_port;
srv.conf_ctx = hapd;
srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
srv.ssl_ctx = hapd->ssl_ctx;
@@ -111,9 +115,17 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
srv.eap_req_id_text = conf->eap_req_id_text;
srv.eap_req_id_text_len = conf->eap_req_id_text_len;
srv.pwd_group = conf->pwd_group;
+ srv.server_id = conf->server_id ? conf->server_id : "hostapd";
+ srv.sqlite_file = conf->eap_user_sqlite;
#ifdef CONFIG_RADIUS_TEST
srv.dump_msk_file = conf->dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+ srv.subscr_remediation_url = conf->subscr_remediation_url;
+ srv.subscr_remediation_method = conf->subscr_remediation_method;
+#endif /* CONFIG_HS20 */
+ srv.erp = conf->eap_server_erp;
+ srv.erp_domain = conf->erp_domain;
hapd->radius_srv = radius_server_init(&srv);
if (hapd->radius_srv == NULL) {
@@ -132,7 +144,7 @@ int authsrv_init(struct hostapd_data *hapd)
#ifdef EAP_TLS_FUNCS
if (hapd->conf->eap_server &&
(hapd->conf->ca_cert || hapd->conf->server_cert ||
- hapd->conf->dh_file)) {
+ hapd->conf->private_key || hapd->conf->dh_file)) {
struct tls_connection_params params;
hapd->ssl_ctx = tls_init(NULL);
@@ -148,6 +160,9 @@ int authsrv_init(struct hostapd_data *hapd)
params.private_key = hapd->conf->private_key;
params.private_key_passwd = hapd->conf->private_key_passwd;
params.dh_file = hapd->conf->dh_file;
+ params.openssl_ciphers = hapd->conf->openssl_ciphers;
+ params.ocsp_stapling_response =
+ hapd->conf->ocsp_stapling_response;
if (tls_global_set_params(hapd->ssl_ctx, &params)) {
wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4c47c75841c51..e575b65cbf3ac 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -4,14 +4,8 @@
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -21,7 +15,7 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/hw_features_common.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
@@ -34,10 +28,56 @@
#include "ap_drv_ops.h"
#include "beacon.h"
#include "hs20.h"
+#include "dfs.h"
#ifdef NEED_AP_MLME
+static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+ size_t len)
+{
+ if (!hapd->conf->radio_measurements || len < 2 + 4)
+ return eid;
+
+ *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+ *eid++ = 5;
+ *eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
+ *eid++ = 0x00;
+ *eid++ = 0x00;
+ *eid++ = 0x00;
+ *eid++ = 0x00;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+ if (len < 2 + 5)
+ return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->conf->bss_load_test_set) {
+ *eid++ = WLAN_EID_BSS_LOAD;
+ *eid++ = 5;
+ os_memcpy(eid, hapd->conf->bss_load_test, 5);
+ eid += 5;
+ return eid;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (hapd->conf->bss_load_update_period) {
+ *eid++ = WLAN_EID_BSS_LOAD;
+ *eid++ = 5;
+ WPA_PUT_LE16(eid, hapd->num_sta);
+ eid += 2;
+ *eid++ = hapd->iface->channel_utilization;
+ WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+ eid += 2;
+ }
+ return eid;
+}
+
+
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
{
u8 erp = 0;
@@ -93,6 +133,74 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
}
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 local_pwr_constraint = 0;
+ int dfs;
+
+ if (hapd->iface->current_mode == NULL ||
+ hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return eid;
+
+ /* Let host drivers add this IE if DFS support is offloaded */
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return eid;
+
+ /*
+ * There is no DFS support and power constraint was not directly
+ * requested by config option.
+ */
+ if (!hapd->iconf->ieee80211h &&
+ hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /* Check if DFS is required by regulatory. */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
+
+ if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+ return eid;
+
+ /*
+ * ieee80211h (DFS) is enabled so Power Constraint element shall
+ * be added when running on DFS channel whenever local_pwr_constraint
+ * is configured or not. In order to meet regulations when TPC is not
+ * implemented using a transmit power that is below the legal maximum
+ * (including any mitigation factor) should help. In this case,
+ * indicate 3 dB below maximum allowed transmit power.
+ */
+ if (hapd->iconf->local_pwr_constraint == -1)
+ local_pwr_constraint = 3;
+
+ /*
+ * A STA that is not an AP shall use a transmit power less than or
+ * equal to the local maximum transmit power level for the channel.
+ * The local maximum transmit power can be calculated from the formula:
+ * local max TX pwr = max TX pwr - local pwr constraint
+ * Where max TX pwr is maximum transmit power level specified for
+ * channel in Country element and local pwr constraint is specified
+ * for channel in this Power Constraint element.
+ */
+
+ /* Element ID */
+ *pos++ = WLAN_EID_PWR_CONSTRAINT;
+ /* Length */
+ *pos++ = 1;
+ /* Local Power Constraint */
+ if (local_pwr_constraint)
+ *pos++ = local_pwr_constraint;
+ else
+ *pos++ = hapd->iconf->local_pwr_constraint;
+
+ return pos;
+}
+
+
static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
struct hostapd_channel_data *start,
struct hostapd_channel_data *prev)
@@ -146,7 +254,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
continue; /* can use same entry */
}
- if (start) {
+ if (start && prev) {
pos = hostapd_eid_country_add(pos, end, chan_spacing,
start, prev);
start = NULL;
@@ -187,6 +295,70 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
}
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 chan;
+
+ if (!hapd->cs_freq_params.freq)
+ return eid;
+
+ if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
+
+ *eid++ = WLAN_EID_CHANNEL_SWITCH;
+ *eid++ = 3;
+ *eid++ = hapd->cs_block_tx;
+ *eid++ = chan;
+ *eid++ = hapd->cs_count;
+
+ return eid;
+}
+
+
+static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 sec_ch;
+
+ if (!hapd->cs_freq_params.sec_channel_offset)
+ return eid;
+
+ if (hapd->cs_freq_params.sec_channel_offset == -1)
+ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+ else if (hapd->cs_freq_params.sec_channel_offset == 1)
+ sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+ else
+ return eid;
+
+ *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+ *eid++ = 1;
+ *eid++ = sec_ch;
+
+ return eid;
+}
+
+
+static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
+ u8 *start, unsigned int *csa_counter_off)
+{
+ u8 *old_pos = pos;
+
+ if (!csa_counter_off)
+ return pos;
+
+ *csa_counter_off = 0;
+ pos = hostapd_eid_csa(hapd, pos);
+
+ if (pos != old_pos) {
+ /* save an offset to the counter - should be last byte */
+ *csa_counter_off = pos - start - 1;
+ pos = hostapd_eid_secondary_channel(hapd, pos);
+ }
+
+ return pos;
+}
+
+
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
struct sta_info *sta,
const struct ieee80211_mgmt *req,
@@ -208,6 +380,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
#endif /* CONFIG_P2P */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
+ if (hapd->conf->vendor_vht) {
+ buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
resp = os_zalloc(buflen);
if (resp == NULL)
return NULL;
@@ -242,6 +418,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
pos = hostapd_eid_country(hapd, pos, epos - pos);
+ /* Power Constraint element */
+ pos = hostapd_eid_pwr_constraint(hapd, pos);
+
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
@@ -251,6 +430,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
/* RSN, MDIE, WPA */
pos = hostapd_eid_wpa(hapd, pos, epos - pos);
+ pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
+ pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+
#ifdef CONFIG_IEEE80211N
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
@@ -265,9 +448,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
pos = hostapd_eid_adv_proto(hapd, pos);
pos = hostapd_eid_roaming_consortium(hapd, pos);
+ pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
+ &hapd->cs_c_off_proberesp);
#ifdef CONFIG_IEEE80211AC
- pos = hostapd_eid_vht_capabilities(hapd, pos);
- pos = hostapd_eid_vht_operation(hapd, pos);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ pos = hostapd_eid_vht_capabilities(hapd, pos);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+ }
+ if (hapd->conf->vendor_vht)
+ pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
/* Wi-Fi Alliance WMM */
@@ -297,6 +486,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
pos = hostapd_eid_hs20_indication(hapd, pos);
+ pos = hostapd_eid_osen(hapd, pos);
#endif /* CONFIG_HS20 */
if (hapd->conf->vendor_elements) {
@@ -390,6 +580,27 @@ void handle_probe_req(struct hostapd_data *hapd,
return;
}
+ /*
+ * No need to reply if the Probe Request frame was sent on an adjacent
+ * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+ * AP with dot11RadioMeasurementActivated set to true, but strictly
+ * speaking does not allow such ignoring of Probe Request frames if
+ * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+ * number of unnecessary Probe Response frames for cases where the STA
+ * is less likely to see them (Probe Request frame sent on a
+ * neighboring, but partially overlapping, channel).
+ */
+ if (elems.ds_params && elems.ds_params_len == 1 &&
+ hapd->iface->current_mode &&
+ (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+ hapd->iconf->channel != elems.ds_params[0]) {
+ wpa_printf(MSG_DEBUG,
+ "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+ hapd->iconf->channel, elems.ds_params[0]);
+ return;
+ }
+
#ifdef CONFIG_P2P
if (hapd->p2p && elems.wps_ie) {
struct wpabuf *wps;
@@ -443,12 +654,10 @@ void handle_probe_req(struct hostapd_data *hapd,
sta->ssid_probe = &hapd->conf->ssid;
} else {
if (!(mgmt->da[0] & 0x01)) {
- char ssid_txt[33];
- ieee802_11_print_ssid(ssid_txt, elems.ssid,
- elems.ssid_len);
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for foreign SSID '%s' (DA " MACSTR ")%s",
- MAC2STR(mgmt->sa), ssid_txt,
+ MAC2STR(mgmt->sa),
+ wpa_ssid_txt(elems.ssid, elems.ssid_len),
MAC2STR(mgmt->da),
elems.ssid_list ? " (SSID list)" : "");
}
@@ -456,7 +665,8 @@ void handle_probe_req(struct hostapd_data *hapd,
}
#ifdef CONFIG_INTERWORKING
- if (elems.interworking && elems.interworking_len >= 1) {
+ if (hapd->conf->interworking &&
+ elems.interworking && elems.interworking_len >= 1) {
u8 ant = elems.interworking[0] & 0x0f;
if (ant != INTERWORKING_ANT_WILDCARD &&
ant != hapd->conf->access_network_type) {
@@ -467,7 +677,7 @@ void handle_probe_req(struct hostapd_data *hapd,
}
}
- if (elems.interworking &&
+ if (hapd->conf->interworking && elems.interworking &&
(elems.interworking_len == 7 || elems.interworking_len == 9)) {
const u8 *hessid;
if (elems.interworking_len == 7)
@@ -485,9 +695,30 @@ void handle_probe_req(struct hostapd_data *hapd,
}
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_P2P
+ if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
+ supp_rates_11b_only(&elems)) {
+ /* Indicates support for 11b rates only */
+ wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
+ MACSTR " with only 802.11b rates",
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_P2P */
+
/* TODO: verify that supp_rates contains at least one matching rate
* with AP configuration */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->ignore_probe_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_probe_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring probe request from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL,
&resp_len);
if (resp == NULL)
@@ -501,7 +732,7 @@ void handle_probe_req(struct hostapd_data *hapd,
is_broadcast_ether_addr(mgmt->da));
if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
- perror("handle_probe_req: send");
+ wpa_printf(MSG_INFO, "handle_probe_req: send failed");
os_free(resp);
@@ -549,23 +780,17 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
#endif /* NEED_AP_MLME */
-void ieee802_11_set_beacon(struct hostapd_data *hapd)
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params)
{
struct ieee80211_mgmt *head = NULL;
u8 *tail = NULL;
size_t head_len = 0, tail_len = 0;
u8 *resp = NULL;
size_t resp_len = 0;
- struct wpa_driver_ap_params params;
- struct wpabuf *beacon, *proberesp, *assocresp;
#ifdef NEED_AP_MLME
u16 capab_info;
u8 *pos, *tailpos;
-#endif /* NEED_AP_MLME */
-
- hapd->beacon_set_done = 1;
-
-#ifdef NEED_AP_MLME
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
@@ -581,12 +806,20 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
#endif /* CONFIG_P2P */
if (hapd->conf->vendor_elements)
tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht) {
+ tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation);
+ }
+#endif /* CONFIG_IEEE80211AC */
+
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
os_free(head);
os_free(tail);
- return;
+ return -1;
}
head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -631,6 +864,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_country(hapd, tailpos,
tail + BEACON_TAIL_BUF_SIZE - tailpos);
+ /* Power Constraint element */
+ tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);
@@ -641,6 +877,13 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
tailpos);
+ tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE -
+ tailpos);
+
+ tailpos = hostapd_eid_bss_load(hapd, tailpos,
+ tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
#ifdef CONFIG_IEEE80211N
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
@@ -657,10 +900,15 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
tailpos = hostapd_eid_interworking(hapd, tailpos);
tailpos = hostapd_eid_adv_proto(hapd, tailpos);
tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
-
+ tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
+ &hapd->cs_c_off_beacon);
#ifdef CONFIG_IEEE80211AC
- tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
- tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+ tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+ }
+ if (hapd->conf->vendor_vht)
+ tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
/* Wi-Fi Alliance WMM */
@@ -689,6 +937,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
#ifdef CONFIG_HS20
tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+ tailpos = hostapd_eid_osen(hapd, tailpos);
#endif /* CONFIG_HS20 */
if (hapd->conf->vendor_elements) {
@@ -702,94 +951,168 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
- os_memset(&params, 0, sizeof(params));
- params.head = (u8 *) head;
- params.head_len = head_len;
- params.tail = tail;
- params.tail_len = tail_len;
- params.proberesp = resp;
- params.proberesp_len = resp_len;
- params.dtim_period = hapd->conf->dtim_period;
- params.beacon_int = hapd->iconf->beacon_int;
- params.basic_rates = hapd->iface->basic_rates;
- params.ssid = hapd->conf->ssid.ssid;
- params.ssid_len = hapd->conf->ssid.ssid_len;
- params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
- hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
- params.group_cipher = hapd->conf->wpa_group;
- params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
- params.auth_algs = hapd->conf->auth_algs;
- params.wpa_version = hapd->conf->wpa;
- params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+ os_memset(params, 0, sizeof(*params));
+ params->head = (u8 *) head;
+ params->head_len = head_len;
+ params->tail = tail;
+ params->tail_len = tail_len;
+ params->proberesp = resp;
+ params->proberesp_len = resp_len;
+ params->dtim_period = hapd->conf->dtim_period;
+ params->beacon_int = hapd->iconf->beacon_int;
+ params->basic_rates = hapd->iface->basic_rates;
+ params->ssid = hapd->conf->ssid.ssid;
+ params->ssid_len = hapd->conf->ssid.ssid_len;
+ params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+ hapd->conf->rsn_pairwise;
+ params->group_cipher = hapd->conf->wpa_group;
+ params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+ params->auth_algs = hapd->conf->auth_algs;
+ params->wpa_version = hapd->conf->wpa;
+ params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
(hapd->conf->ieee802_1x &&
(hapd->conf->default_wep_key_len ||
hapd->conf->individual_wep_key_len));
switch (hapd->conf->ignore_broadcast_ssid) {
case 0:
- params.hide_ssid = NO_SSID_HIDING;
+ params->hide_ssid = NO_SSID_HIDING;
break;
case 1:
- params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+ params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
break;
case 2:
- params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+ params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
break;
}
- hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
- params.beacon_ies = beacon;
- params.proberesp_ies = proberesp;
- params.assocresp_ies = assocresp;
- params.isolate = hapd->conf->isolate;
+ params->isolate = hapd->conf->isolate;
+ params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK;
#ifdef NEED_AP_MLME
- params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+ params->cts_protect = !!(ieee802_11_erp_info(hapd) &
ERP_INFO_USE_PROTECTION);
- params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+ params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
hapd->iconf->preamble == SHORT_PREAMBLE;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
- params.short_slot_time =
+ params->short_slot_time =
hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
else
- params.short_slot_time = -1;
+ params->short_slot_time = -1;
if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
- params.ht_opmode = -1;
+ params->ht_opmode = -1;
else
- params.ht_opmode = hapd->iface->ht_op_mode;
+ params->ht_opmode = hapd->iface->ht_op_mode;
#endif /* NEED_AP_MLME */
- params.interworking = hapd->conf->interworking;
+ params->interworking = hapd->conf->interworking;
if (hapd->conf->interworking &&
!is_zero_ether_addr(hapd->conf->hessid))
- params.hessid = hapd->conf->hessid;
- params.access_network_type = hapd->conf->access_network_type;
- params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
+ params->hessid = hapd->conf->hessid;
+ params->access_network_type = hapd->conf->access_network_type;
+ params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_P2P
+ params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
- params.disable_dgaf = hapd->conf->disable_dgaf;
+ params->disable_dgaf = hapd->conf->disable_dgaf;
+ if (hapd->conf->osen) {
+ params->privacy = 1;
+ params->osen = 1;
+ }
#endif /* CONFIG_HS20 */
- if (hostapd_drv_set_ap(hapd, &params))
- wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
- hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+ return 0;
+}
- os_free(tail);
- os_free(head);
- os_free(resp);
+
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+{
+ os_free(params->tail);
+ params->tail = NULL;
+ os_free(params->head);
+ params->head = NULL;
+ os_free(params->proberesp);
+ params->proberesp = NULL;
}
-void ieee802_11_set_beacons(struct hostapd_iface *iface)
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+ struct wpa_driver_ap_params params;
+ struct hostapd_freq_params freq;
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_config *iconf = iface->conf;
+ struct wpabuf *beacon, *proberesp, *assocresp;
+ int res, ret = -1;
+
+ if (hapd->csa_in_progress) {
+ wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
+ return -1;
+ }
+
+ hapd->beacon_set_done = 1;
+
+ if (ieee802_11_build_ap_params(hapd, &params) < 0)
+ return -1;
+
+ if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+ 0)
+ goto fail;
+
+ params.beacon_ies = beacon;
+ params.proberesp_ies = proberesp;
+ params.assocresp_ies = assocresp;
+ params.reenable = hapd->reenable_beacon;
+ hapd->reenable_beacon = 0;
+
+ if (iface->current_mode &&
+ hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+ iconf->channel, iconf->ieee80211n,
+ iconf->ieee80211ac,
+ iconf->secondary_channel,
+ iconf->vht_oper_chwidth,
+ iconf->vht_oper_centr_freq_seg0_idx,
+ iconf->vht_oper_centr_freq_seg1_idx,
+ iface->current_mode->vht_capab) == 0)
+ params.freq = &freq;
+
+ res = hostapd_drv_set_ap(hapd, &params);
+ hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+ if (res)
+ wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+ else
+ ret = 0;
+fail:
+ ieee802_11_free_ap_params(&params);
+ return ret;
+}
+
+
+int ieee802_11_set_beacons(struct hostapd_iface *iface)
{
size_t i;
- for (i = 0; i < iface->num_bss; i++)
- ieee802_11_set_beacon(iface->bss[i]);
+ int ret = 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->started &&
+ ieee802_11_set_beacon(iface->bss[i]) < 0)
+ ret = -1;
+ }
+
+ return ret;
}
/* only update beacons if started */
-void ieee802_11_update_beacons(struct hostapd_iface *iface)
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
{
size_t i;
- for (i = 0; i < iface->num_bss; i++)
- if (iface->bss[i]->beacon_set_done)
- ieee802_11_set_beacon(iface->bss[i]);
+ int ret = 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
+ ieee802_11_set_beacon(iface->bss[i]) < 0)
+ ret = -1;
+ }
+
+ return ret;
}
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 37f10d2f587a5..722159a7500c6 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -3,14 +3,8 @@
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef BEACON_H
@@ -21,8 +15,11 @@ struct ieee80211_mgmt;
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal);
-void ieee802_11_set_beacon(struct hostapd_data *hapd);
-void ieee802_11_set_beacons(struct hostapd_iface *iface);
-void ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_set_beacon(struct hostapd_data *hapd);
+int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+ struct wpa_driver_ap_params *params);
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
#endif /* BEACON_H */
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
new file mode 100644
index 0000000000000..fb639423230c0
--- /dev/null
+++ b/src/ap/bss_load.c
@@ -0,0 +1,65 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+ struct hostapd_data *hapd = eloop_data;
+ unsigned int sec, usec;
+ int err;
+
+ if (!(hapd->beacon_set_done && hapd->started))
+ return;
+
+ err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+ if (err) {
+ wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+ return;
+ }
+
+ ieee802_11_set_beacon(hapd);
+
+ sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+ usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct hostapd_config *iconf = hapd->iconf;
+ unsigned int sec, usec;
+
+ if (!conf->bss_load_update_period || !iconf->beacon_int)
+ return -1;
+
+ hapd->bss_load_update_timeout = conf->bss_load_update_period *
+ iconf->beacon_int;
+ sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+ usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+ eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+ NULL);
+ return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+ eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h
new file mode 100644
index 0000000000000..ac3c793c90b17
--- /dev/null
+++ b/src/ap/bss_load.h
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index c55d3fe32a616..41ab988277bbd 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
/*
* Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,8 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "eapol_auth/eapol_auth_sm.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
@@ -21,25 +23,61 @@
#include "ap_drv_ops.h"
+static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
+{
+ struct hostap_sta_driver_data data;
+ int ret;
+
+ if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+ return 0;
+
+ ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+ "rx_bytes=%lu\ntx_bytes=%lu\n",
+ data.rx_packets, data.tx_packets,
+ data.rx_bytes, data.tx_bytes);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+}
+
+
static int hostapd_get_sta_conn_time(struct sta_info *sta,
char *buf, size_t buflen)
{
- struct os_time now, age;
- int len = 0, ret;
+ struct os_reltime age;
+ int ret;
if (!sta->connected_time.sec)
return 0;
- os_get_time(&now);
- os_time_sub(&now, &sta->connected_time, &age);
+ os_reltime_age(&sta->connected_time, &age);
- ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
+ ret = os_snprintf(buf, buflen, "connected_time=%u\n",
(unsigned int) age.sec);
- if (ret < 0 || (size_t) ret >= buflen - len)
- return len;
- len += ret;
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+}
- return len;
+
+static const char * timeout_next_str(int val)
+{
+ switch (val) {
+ case STA_NULLFUNC:
+ return "NULLFUNC POLL";
+ case STA_DISASSOC:
+ return "DISASSOC";
+ case STA_DEAUTH:
+ return "DEAUTH";
+ case STA_REMOVE:
+ return "REMOVE";
+ case STA_DISASSOC_FROM_CLI:
+ return "DISASSOC_FROM_CLI";
+ }
+
+ return "?";
}
@@ -47,19 +85,42 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
{
- int len, res, ret;
+ int len, res, ret, i;
- if (sta == NULL) {
- ret = os_snprintf(buf, buflen, "FAIL\n");
- if (ret < 0 || (size_t) ret >= buflen)
- return 0;
- return ret;
- }
+ if (!sta)
+ return 0;
len = 0;
- ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+ ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
MAC2STR(sta->addr));
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
+ if (ret < 0)
+ return len;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
+ "listen_interval=%d\nsupported_rates=",
+ sta->aid, sta->capability, sta->listen_interval);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (i = 0; i < sta->supported_rates_len; i++) {
+ ret = os_snprintf(buf + len, buflen - len, "%02x%s",
+ sta->supported_rates[i],
+ i + 1 < sta->supported_rates_len ? " " : "");
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+ timeout_next_str(sta->timeout_next));
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -80,9 +141,17 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
if (res >= 0)
len += res;
- res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
- if (res >= 0)
- len += res;
+ len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
+ len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
+
+#ifdef CONFIG_SAE
+ if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+ res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+ sta->sae->group);
+ if (!os_snprintf_error(buflen - len, res))
+ len += res;
+ }
+#endif /* CONFIG_SAE */
return len;
}
@@ -100,15 +169,37 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
{
u8 addr[ETH_ALEN];
int ret;
+ const char *pos;
+ struct sta_info *sta;
if (hwaddr_aton(txtaddr, addr)) {
ret = os_snprintf(buf, buflen, "FAIL\n");
- if (ret < 0 || (size_t) ret >= buflen)
+ if (os_snprintf_error(buflen, ret))
return 0;
return ret;
}
- return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
- buf, buflen);
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta == NULL)
+ return -1;
+
+ pos = os_strchr(txtaddr, ' ');
+ if (pos) {
+ pos++;
+
+#ifdef HOSTAPD_DUMP_STATE
+ if (os_strcmp(pos, "eapol") == 0) {
+ if (sta->eapol_sm == NULL)
+ return -1;
+ return eapol_auth_dump_state(sta->eapol_sm, buf,
+ buflen);
+ }
+#endif /* HOSTAPD_DUMP_STATE */
+
+ return -1;
+ }
+
+ return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
}
@@ -122,10 +213,14 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
if (hwaddr_aton(txtaddr, addr) ||
(sta = ap_get_sta(hapd, addr)) == NULL) {
ret = os_snprintf(buf, buflen, "FAIL\n");
- if (ret < 0 || (size_t) ret >= buflen)
+ if (os_snprintf_error(buflen, ret))
return 0;
return ret;
- }
+ }
+
+ if (!sta->next)
+ return 0;
+
return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
}
@@ -145,11 +240,12 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
if (mgmt == NULL)
return -1;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
- " with minor reason code %u (stype=%u)",
- MAC2STR(addr), minor_reason_code, stype);
+ " with minor reason code %u (stype=%u (%s))",
+ MAC2STR(addr), minor_reason_code, stype,
+ fc2str(mgmt->frame_control));
- mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
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);
@@ -165,9 +261,8 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 3 + 1;
- WPA_PUT_BE24(pos, OUI_WFA);
- pos += 3;
- *pos++ = P2P_OUI_TYPE;
+ WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+ pos += 4;
*pos++ = P2P_ATTR_MINOR_REASON_CODE;
WPA_PUT_LE16(pos, 1);
@@ -189,6 +284,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
u8 addr[ETH_ALEN];
struct sta_info *sta;
const char *pos;
+ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
txtaddr);
@@ -196,6 +292,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
if (hwaddr_aton(txtaddr, addr))
return -1;
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
@@ -210,8 +310,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
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.deauth.reason_code =
- host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ mgmt.u.deauth.reason_code = host_to_le16(reason);
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
@@ -228,11 +327,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P_MANAGER */
- hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_deauth(hapd, addr, reason);
sta = ap_get_sta(hapd, addr);
if (sta)
- ap_sta_deauthenticate(hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ ap_sta_deauthenticate(hapd, sta, reason);
else if (addr[0] == 0xff)
hostapd_free_stas(hapd);
@@ -246,6 +344,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
u8 addr[ETH_ALEN];
struct sta_info *sta;
const char *pos;
+ u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
txtaddr);
@@ -253,6 +352,10 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
if (hwaddr_aton(txtaddr, addr))
return -1;
+ pos = os_strstr(txtaddr, " reason=");
+ if (pos)
+ reason = atoi(pos + 8);
+
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
@@ -267,8 +370,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
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.disassoc.reason_code =
- host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+ mgmt.u.disassoc.reason_code = host_to_le16(reason);
if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
@@ -285,13 +387,159 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
}
#endif /* CONFIG_P2P_MANAGER */
- hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+ hostapd_drv_sta_disassoc(hapd, addr, reason);
sta = ap_get_sta(hapd, addr);
if (sta)
- ap_sta_disassociate(hapd, sta,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ ap_sta_disassociate(hapd, sta, reason);
else if (addr[0] == 0xff)
hostapd_free_stas(hapd);
return 0;
}
+
+
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ int len = 0, ret;
+ size_t i;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "state=%s\n"
+ "phy=%s\n"
+ "freq=%d\n"
+ "num_sta_non_erp=%d\n"
+ "num_sta_no_short_slot_time=%d\n"
+ "num_sta_no_short_preamble=%d\n"
+ "olbc=%d\n"
+ "num_sta_ht_no_gf=%d\n"
+ "num_sta_no_ht=%d\n"
+ "num_sta_ht_20_mhz=%d\n"
+ "num_sta_ht40_intolerant=%d\n"
+ "olbc_ht=%d\n"
+ "ht_op_mode=0x%x\n",
+ hostapd_state_text(iface->state),
+ iface->phy,
+ iface->freq,
+ iface->num_sta_non_erp,
+ iface->num_sta_no_short_slot_time,
+ iface->num_sta_no_short_preamble,
+ iface->olbc,
+ iface->num_sta_ht_no_gf,
+ iface->num_sta_no_ht,
+ iface->num_sta_ht_20mhz,
+ iface->num_sta_ht40_intolerant,
+ iface->olbc_ht,
+ iface->ht_op_mode);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ if (!iface->cac_started || !iface->dfs_cac_ms) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%d\n"
+ "cac_time_left_seconds=N/A\n",
+ iface->dfs_cac_ms / 1000);
+ } else {
+ /* CAC started and CAC time set - calculate remaining time */
+ struct os_reltime now;
+ unsigned int left_time;
+
+ os_reltime_age(&iface->dfs_cac_start, &now);
+ left_time = iface->dfs_cac_ms / 1000 - now.sec;
+ ret = os_snprintf(buf + len, buflen - len,
+ "cac_time_seconds=%u\n"
+ "cac_time_left_seconds=%u\n",
+ iface->dfs_cac_ms / 1000,
+ left_time);
+ }
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ ret = os_snprintf(buf + len, buflen - len,
+ "channel=%u\n"
+ "secondary_channel=%d\n"
+ "ieee80211n=%d\n"
+ "ieee80211ac=%d\n"
+ "vht_oper_chwidth=%d\n"
+ "vht_oper_centr_freq_seg0_idx=%d\n"
+ "vht_oper_centr_freq_seg1_idx=%d\n",
+ iface->conf->channel,
+ iface->conf->secondary_channel,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->vht_oper_chwidth,
+ iface->conf->vht_oper_centr_freq_seg0_idx,
+ iface->conf->vht_oper_centr_freq_seg1_idx);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *bss = iface->bss[i];
+ ret = os_snprintf(buf + len, buflen - len,
+ "bss[%d]=%s\n"
+ "bssid[%d]=" MACSTR "\n"
+ "ssid[%d]=%s\n"
+ "num_sta[%d]=%d\n",
+ (int) i, bss->conf->iface,
+ (int) i, MAC2STR(bss->own_addr),
+ (int) i,
+ wpa_ssid_txt(bss->conf->ssid.ssid,
+ bss->conf->ssid.ssid_len),
+ (int) i, bss->num_sta);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ return len;
+}
+
+
+int hostapd_parse_csa_settings(const char *pos,
+ struct csa_settings *settings)
+{
+ char *end;
+
+ os_memset(settings, 0, sizeof(*settings));
+ settings->cs_count = strtol(pos, &end, 10);
+ if (pos == end) {
+ wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+ return -1;
+ }
+
+ settings->freq_params.freq = atoi(end);
+ if (settings->freq_params.freq == 0) {
+ wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+ return -1;
+ }
+
+#define SET_CSA_SETTING(str) \
+ do { \
+ const char *pos2 = os_strstr(pos, " " #str "="); \
+ if (pos2) { \
+ pos2 += sizeof(" " #str "=") - 1; \
+ settings->freq_params.str = atoi(pos2); \
+ } \
+ } while (0)
+
+ SET_CSA_SETTING(center_freq1);
+ SET_CSA_SETTING(center_freq2);
+ SET_CSA_SETTING(bandwidth);
+ SET_CSA_SETTING(sec_channel_offset);
+ settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+ settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+ settings->block_tx = !!os_strstr(pos, " blocktx");
+#undef SET_CSA_SETTING
+
+ return 0;
+}
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+ return hostapd_drv_stop_ap(hapd);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index e83f894144c55..e5297d03e810a 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -1,6 +1,6 @@
/*
* Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,5 +19,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
const char *txtaddr);
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
const char *txtaddr);
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+ size_t buflen);
+int hostapd_parse_csa_settings(const char *pos,
+ struct csa_settings *settings);
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644
index 0000000000000..da6fd46467101
--- /dev/null
+++ b/src/ap/dfs.c
@@ -0,0 +1,1064 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+
+
+static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+{
+ int n_chans = 1;
+
+ *seg1 = 0;
+
+ if (iface->conf->ieee80211n && iface->conf->secondary_channel)
+ n_chans = 2;
+
+ if (iface->conf->ieee80211ac) {
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ case VHT_CHANWIDTH_80P80MHZ:
+ n_chans = 4;
+ *seg1 = 4;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return n_chans;
+}
+
+
+static int dfs_channel_available(struct hostapd_channel_data *chan,
+ int skip_radar)
+{
+ /*
+ * When radar detection happens, CSA is performed. However, there's no
+ * time for CAC, so radar channels must be skipped when finding a new
+ * channel for CSA, unless they are available for immediate use.
+ */
+ if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE))
+ return 0;
+
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ return 0;
+ if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+ ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE))
+ return 0;
+ return 1;
+}
+
+
+static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
+{
+ /*
+ * The tables contain first valid channel number based on channel width.
+ * We will also choose this first channel as the control one.
+ */
+ int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+ 184, 192 };
+ /*
+ * VHT80, valid channels based on center frequency:
+ * 42, 58, 106, 122, 138, 155
+ */
+ int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
+ /*
+ * VHT160 valid channels based on center frequency:
+ * 50, 114
+ */
+ int allowed_160[] = { 36, 100 };
+ int *allowed = allowed_40;
+ unsigned int i, allowed_no = 0;
+
+ switch (n_chans) {
+ case 2:
+ allowed = allowed_40;
+ allowed_no = ARRAY_SIZE(allowed_40);
+ break;
+ case 4:
+ allowed = allowed_80;
+ allowed_no = ARRAY_SIZE(allowed_80);
+ break;
+ case 8:
+ allowed = allowed_160;
+ allowed_no = ARRAY_SIZE(allowed_160);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
+ break;
+ }
+
+ for (i = 0; i < allowed_no; i++) {
+ if (chan->chan == allowed[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+ int first_chan_idx, int num_chans,
+ int skip_radar)
+{
+ struct hostapd_channel_data *first_chan, *chan;
+ int i;
+
+ if (first_chan_idx + num_chans >= mode->num_channels)
+ return 0;
+
+ first_chan = &mode->channels[first_chan_idx];
+
+ for (i = 0; i < num_chans; i++) {
+ chan = &mode->channels[first_chan_idx + i];
+
+ if (first_chan->freq + i * 20 != chan->freq)
+ return 0;
+
+ if (!dfs_channel_available(chan, skip_radar))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan)
+{
+ int *entry;
+
+ if (!iface->conf->chanlist)
+ return 1;
+
+ for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+ if (*entry == chan->chan)
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * The function assumes HT40+ operation.
+ * Make sure to adjust the following variables after calling this:
+ * - hapd->secondary_channel
+ * - hapd->vht_oper_centr_freq_seg0_idx
+ * - hapd->vht_oper_centr_freq_seg1_idx
+ */
+static int dfs_find_channel(struct hostapd_iface *iface,
+ struct hostapd_channel_data **ret_chan,
+ int idx, int skip_radar)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int i, channel_idx = 0, n_chans, n_chans1;
+
+ mode = iface->current_mode;
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+
+ /* Skip HT40/VHT incompatible channels */
+ if (iface->conf->ieee80211n &&
+ iface->conf->secondary_channel &&
+ !dfs_is_chan_allowed(chan, n_chans))
+ continue;
+
+ /* Skip incompatible chandefs */
+ if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
+ continue;
+
+ if (!is_in_chanlist(iface, chan))
+ continue;
+
+ if (ret_chan && idx == channel_idx) {
+ wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+ *ret_chan = chan;
+ return idx;
+ }
+ wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
+ channel_idx++;
+ }
+ return channel_idx;
+}
+
+
+static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ int secondary_channel,
+ u8 *vht_oper_centr_freq_seg0_idx,
+ u8 *vht_oper_centr_freq_seg1_idx)
+{
+ if (!iface->conf->ieee80211ac)
+ return;
+
+ if (!chan)
+ return;
+
+ *vht_oper_centr_freq_seg1_idx = 0;
+
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ if (secondary_channel == 1)
+ *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+ else if (secondary_channel == -1)
+ *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
+ else
+ *vht_oper_centr_freq_seg0_idx = chan->chan;
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
+ *vht_oper_centr_freq_seg0_idx = 0;
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
+ *vht_oper_centr_freq_seg0_idx,
+ *vht_oper_centr_freq_seg1_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int channel_no = iface->conf->channel;
+ int res = -1, i;
+ int chan_seg1 = -1;
+
+ *seg1_start = -1;
+
+ /* HT40- */
+ if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
+ channel_no -= 4;
+
+ /* VHT */
+ if (iface->conf->ieee80211ac) {
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_USE_HT:
+ break;
+ case VHT_CHANWIDTH_80MHZ:
+ channel_no =
+ iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ channel_no =
+ iface->conf->vht_oper_centr_freq_seg0_idx - 14;
+ break;
+ case VHT_CHANWIDTH_80P80MHZ:
+ channel_no =
+ iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+ chan_seg1 =
+ iface->conf->vht_oper_centr_freq_seg1_idx - 6;
+ break;
+ default:
+ wpa_printf(MSG_INFO,
+ "DFS only VHT20/40/80/160/80+80 is supported now");
+ channel_no = -1;
+ break;
+ }
+ }
+
+ /* Get idx */
+ mode = iface->current_mode;
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == channel_no) {
+ res = i;
+ break;
+ }
+ }
+
+ if (res != -1 && chan_seg1 > -1) {
+ int found = 0;
+
+ /* Get idx for seg1 */
+ mode = iface->current_mode;
+ for (i = 0; i < mode->num_channels; i++) {
+ chan = &mode->channels[i];
+ if (chan->chan == chan_seg1) {
+ *seg1_start = i;
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ res = -1;
+ }
+
+ if (res == -1) {
+ wpa_printf(MSG_DEBUG,
+ "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+ mode->num_channels, channel_no, iface->conf->channel,
+ iface->conf->ieee80211n,
+ iface->conf->secondary_channel,
+ iface->conf->vht_oper_chwidth);
+
+ for (i = 0; i < mode->num_channels; i++) {
+ wpa_printf(MSG_DEBUG, "Available channel: %d",
+ mode->channels[i].chan);
+ }
+ }
+
+ return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_RADAR)
+ res++;
+ }
+
+ return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+
+ if (channel->flag & HOSTAPD_CHAN_DISABLED)
+ break;
+
+ if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+ HOSTAPD_CHAN_DFS_AVAILABLE)
+ break;
+ }
+
+ return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
+ int start_chan_idx,
+ int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i, res = 0;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (channel->flag & HOSTAPD_CHAN_DISABLED)
+ res++;
+ if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+ HOSTAPD_CHAN_DFS_UNAVAILABLE)
+ res++;
+ }
+
+ return res;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_valid_channel(struct hostapd_iface *iface,
+ int *secondary_channel,
+ u8 *vht_oper_centr_freq_seg0_idx,
+ u8 *vht_oper_centr_freq_seg1_idx,
+ int skip_radar)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ int num_available_chandefs;
+ int chan_idx;
+ u32 _rand;
+
+ wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+ *secondary_channel = 0;
+ *vht_oper_centr_freq_seg0_idx = 0;
+ *vht_oper_centr_freq_seg1_idx = 0;
+
+ if (iface->current_mode == NULL)
+ return NULL;
+
+ mode = iface->current_mode;
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return NULL;
+
+ /* Get the count first */
+ num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+ if (num_available_chandefs == 0)
+ return NULL;
+
+ if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+ _rand = os_random();
+ chan_idx = _rand % num_available_chandefs;
+ dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+
+ /* dfs_find_channel() calculations assume HT40+ */
+ if (iface->conf->secondary_channel)
+ *secondary_channel = 1;
+ else
+ *secondary_channel = 0;
+
+ dfs_adjust_vht_center_freq(iface, chan,
+ *secondary_channel,
+ vht_oper_centr_freq_seg0_idx,
+ vht_oper_centr_freq_seg1_idx);
+
+ return chan;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan = NULL;
+ int i;
+
+ mode = iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->freq == freq) {
+ if (chan->flag & HOSTAPD_CHAN_RADAR) {
+ chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+ chan->flag |= state;
+ return 1; /* Channel found */
+ }
+ }
+ }
+ wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+ return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
+ int chan_offset, int chan_width, int cf1,
+ int cf2, u32 state)
+{
+ int n_chans = 1, i;
+ struct hostapd_hw_modes *mode;
+ int frequency = freq;
+ int ret = 0;
+
+ mode = iface->current_mode;
+ if (mode == NULL)
+ return 0;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+ wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+ return 0;
+ }
+
+ /* Seems cf1 and chan_width is enough here */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ n_chans = 1;
+ if (frequency == 0)
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+ n_chans);
+ for (i = 0; i < n_chans; i++) {
+ ret += set_dfs_state_freq(iface, frequency, state);
+ frequency = frequency + 20;
+ }
+
+ return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+ int chan_width, int cf1, int cf2)
+{
+ int start_chan_idx, start_chan_idx1;
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+ int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
+ u8 radar_chan;
+ int res = 0;
+
+ /* Our configuration */
+ mode = iface->current_mode;
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check we are on DFS channel(s) */
+ if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
+ return 0;
+
+ /* Reported via radar event */
+ switch (chan_width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ radar_n_chans = 1;
+ if (frequency == 0)
+ frequency = cf1;
+ break;
+ case CHAN_WIDTH_40:
+ radar_n_chans = 2;
+ frequency = cf1 - 10;
+ break;
+ case CHAN_WIDTH_80:
+ radar_n_chans = 4;
+ frequency = cf1 - 30;
+ break;
+ case CHAN_WIDTH_160:
+ radar_n_chans = 8;
+ frequency = cf1 - 70;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+ chan_width);
+ break;
+ }
+
+ ieee80211_freq_to_chan(frequency, &radar_chan);
+
+ for (i = 0; i < n_chans; i++) {
+ chan = &mode->channels[start_chan_idx + i];
+ if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+ for (j = 0; j < radar_n_chans; j++) {
+ wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+ chan->chan, radar_chan + j * 4);
+ if (chan->chan == radar_chan + j * 4)
+ res++;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+ return res;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+ int start_chan_idx, int n_chans)
+{
+ struct hostapd_channel_data *channel;
+ struct hostapd_hw_modes *mode;
+ int i;
+ unsigned int cac_time_ms = 0;
+
+ mode = iface->current_mode;
+
+ for (i = 0; i < n_chans; i++) {
+ channel = &mode->channels[start_chan_idx + i];
+ if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+ continue;
+ if (channel->dfs_cac_ms > cac_time_ms)
+ cac_time_ms = channel->dfs_cac_ms;
+ }
+
+ return cac_time_ms;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *channel;
+ int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+ int skip_radar = 0;
+
+ if (!iface->current_mode) {
+ /*
+ * This can happen with drivers that do not provide mode
+ * information and as such, cannot really use hostapd for DFS.
+ */
+ wpa_printf(MSG_DEBUG,
+ "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+ return 1;
+ }
+
+ iface->cac_started = 0;
+
+ do {
+ /* Get start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface,
+ &start_chan_idx1);
+ if (start_chan_idx == -1)
+ return -1;
+
+ /* Get number of used channels, depend on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Setup CAC time */
+ iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+ n_chans);
+
+ /* Check if any of configured channels require DFS */
+ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS %d channels required radar detection",
+ res);
+ if (!res)
+ return 1;
+
+ /* Check if all channels are DFS available */
+ res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
+ wpa_printf(MSG_DEBUG,
+ "DFS all channels available, (SKIP CAC): %s",
+ res ? "yes" : "no");
+ if (res)
+ return 1;
+
+ /* Check if any of configured channels is unavailable */
+ res = dfs_check_chans_unavailable(iface, start_chan_idx,
+ n_chans);
+ wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+ res, res ? "yes": "no");
+ if (res) {
+ int sec = 0;
+ u8 cf1 = 0, cf2 = 0;
+
+ channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+ skip_radar);
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "could not get valid channel");
+ return -1;
+ }
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = sec;
+ iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
+ iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
+ }
+ } while (res);
+
+ /* Finally start CAC */
+ hostapd_set_state(iface, HAPD_IFACE_DFS);
+ wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+ iface->freq,
+ iface->conf->channel, iface->conf->secondary_channel,
+ iface->conf->vht_oper_chwidth,
+ iface->conf->vht_oper_centr_freq_seg0_idx,
+ iface->conf->vht_oper_centr_freq_seg1_idx,
+ iface->dfs_cac_ms / 1000);
+
+ res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+ iface->freq,
+ iface->conf->channel,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ iface->conf->secondary_channel,
+ iface->conf->vht_oper_chwidth,
+ iface->conf->vht_oper_centr_freq_seg0_idx,
+ iface->conf->vht_oper_centr_freq_seg1_idx);
+
+ if (res) {
+ wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+ "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ if (success) {
+ /* Complete iface/ap configuration */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ /* Complete AP configuration for the first bring up. */
+ if (iface->state != HAPD_IFACE_ENABLED)
+ hostapd_setup_interface_complete(iface, 0);
+ else
+ iface->cac_started = 0;
+ } else {
+ set_dfs_state(iface, freq, ht_enabled, chan_offset,
+ chan_width, cf1, cf2,
+ HOSTAPD_CHAN_DFS_AVAILABLE);
+ iface->cac_started = 0;
+ hostapd_setup_interface_complete(iface, 0);
+ }
+ }
+
+ return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *channel;
+ int secondary_channel;
+ u8 vht_oper_centr_freq_seg0_idx = 0;
+ u8 vht_oper_centr_freq_seg1_idx = 0;
+ int skip_radar = 0;
+ int err = 1;
+
+ /* Radar detected during active CAC */
+ iface->cac_started = 0;
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &vht_oper_centr_freq_seg0_idx,
+ &vht_oper_centr_freq_seg1_idx,
+ skip_radar);
+
+ if (!channel) {
+ wpa_printf(MSG_ERROR, "No valid channel available");
+ hostapd_setup_interface_complete(iface, err);
+ return err;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+ channel->chan);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+ "freq=%d chan=%d sec_chan=%d", channel->freq,
+ channel->chan, secondary_channel);
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = secondary_channel;
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ vht_oper_centr_freq_seg0_idx;
+ iface->conf->vht_oper_centr_freq_seg1_idx =
+ vht_oper_centr_freq_seg1_idx;
+ err = 0;
+
+ hostapd_setup_interface_complete(iface, err);
+ return err;
+}
+
+
+static int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+ unsigned int i;
+ for (i = 0; i < iface->num_bss; i++)
+ if (iface->bss[i]->csa_in_progress)
+ return 1;
+ return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+{
+ struct hostapd_channel_data *channel;
+ int secondary_channel;
+ u8 vht_oper_centr_freq_seg0_idx;
+ u8 vht_oper_centr_freq_seg1_idx;
+ int skip_radar = 1;
+ struct csa_settings csa_settings;
+ unsigned int i;
+ int err = 1;
+
+ wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+ __func__, iface->cac_started ? "yes" : "no",
+ hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+ /* Check if CSA in progress */
+ if (hostapd_csa_in_progress(iface))
+ return 0;
+
+ /* Check if active CAC */
+ if (iface->cac_started)
+ return hostapd_dfs_start_channel_switch_cac(iface);
+
+ /* Perform channel switch/CSA */
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &vht_oper_centr_freq_seg0_idx,
+ &vht_oper_centr_freq_seg1_idx,
+ skip_radar);
+
+ if (!channel) {
+ /*
+ * If there is no channel to switch immediately to, check if
+ * there is another channel where we can switch even if it
+ * requires to perform a CAC first.
+ */
+ skip_radar = 0;
+ channel = dfs_get_valid_channel(iface, &secondary_channel,
+ &vht_oper_centr_freq_seg0_idx,
+ &vht_oper_centr_freq_seg1_idx,
+ skip_radar);
+ if (!channel) {
+ /* FIXME: Wait for channel(s) to become available */
+ hostapd_disable_iface(iface);
+ return err;
+ }
+
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = secondary_channel;
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ vht_oper_centr_freq_seg0_idx;
+ iface->conf->vht_oper_centr_freq_seg1_idx =
+ vht_oper_centr_freq_seg1_idx;
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+ channel->chan);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+ "freq=%d chan=%d sec_chan=%d", channel->freq,
+ channel->chan, secondary_channel);
+
+ /* Setup CSA request */
+ os_memset(&csa_settings, 0, sizeof(csa_settings));
+ csa_settings.cs_count = 5;
+ csa_settings.block_tx = 1;
+ err = hostapd_set_freq_params(&csa_settings.freq_params,
+ iface->conf->hw_mode,
+ channel->freq,
+ channel->chan,
+ iface->conf->ieee80211n,
+ iface->conf->ieee80211ac,
+ secondary_channel,
+ iface->conf->vht_oper_chwidth,
+ vht_oper_centr_freq_seg0_idx,
+ vht_oper_centr_freq_seg1_idx,
+ iface->current_mode->vht_capab);
+
+ if (err) {
+ wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+ hostapd_disable_iface(iface);
+ return err;
+ }
+
+ for (i = 0; i < iface->num_bss; i++) {
+ err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+ if (err)
+ break;
+ }
+
+ if (err) {
+ wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+ err);
+ iface->freq = channel->freq;
+ iface->conf->channel = channel->chan;
+ iface->conf->secondary_channel = secondary_channel;
+ iface->conf->vht_oper_centr_freq_seg0_idx =
+ vht_oper_centr_freq_seg0_idx;
+ iface->conf->vht_oper_centr_freq_seg1_idx =
+ vht_oper_centr_freq_seg1_idx;
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+ return 0;
+ }
+
+ /* Channel configuration will be updated once CSA completes and
+ * ch_switch_notify event is received */
+
+ wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+ return 0;
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ int res;
+
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ if (!iface->conf->ieee80211h)
+ return 0;
+
+ /* mark radar frequency as invalid */
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+ /* Skip if reported radar event not overlapped our channels */
+ res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
+ if (!res)
+ return 0;
+
+ /* radar detected while operating, switch the channel. */
+ res = hostapd_dfs_start_channel_switch(iface);
+
+ return res;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
+ "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+ freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+ /* Proceed only if DFS is not offloaded to the driver */
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+ return 0;
+
+ /* TODO add correct implementation here */
+ set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+ cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+ return 0;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+ int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+
+ if (!iface->conf->ieee80211h || !iface->current_mode ||
+ iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ /* Get start (first) channel for current configuration */
+ start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+ if (start_chan_idx == -1)
+ return -1;
+
+ /* Get number of used channels, depend on width */
+ n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+ /* Check if any of configured channels require DFS */
+ res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+ if (res)
+ return res;
+ if (start_chan_idx1 >= 0 && n_chans1 > 0)
+ res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
+ return res;
+}
+
+
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2)
+{
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+ "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+ "seg1=%d cac_time=%ds",
+ freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
+ iface->cac_started = 1;
+ return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+ wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+ __func__, iface->cac_started);
+
+ /*
+ * If DFS has already been started, then we are being called from a
+ * callback to continue AP/channel setup. Reset the CAC start flag and
+ * return.
+ */
+ if (iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+ __func__, iface->cac_started);
+ iface->cac_started = 0;
+ return 1;
+ }
+
+ if (ieee80211_is_dfs(iface->freq)) {
+ wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
+ __func__, iface->freq);
+ return 0;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+ __func__, iface->freq);
+ return 2;
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644
index 0000000000000..be8c0e6001c95
--- /dev/null
+++ b/src/ap/dfs.h
@@ -0,0 +1,30 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+ int ht_enabled,
+ int chan_offset, int chan_width, int cf1, int cf2);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+ int ht_enabled, int chan_offset, int chan_width,
+ int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+
+#endif /* DFS_H */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000000000..3a77225f380e3
--- /dev/null
+++ b/src/ap/dhcp_snoop.c
@@ -0,0 +1,178 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+struct bootp_pkt {
+ struct iphdr iph;
+ struct udphdr udph;
+ u8 op;
+ u8 htype;
+ u8 hlen;
+ u8 hops;
+ be32 xid;
+ be16 secs;
+ be16 flags;
+ be32 client_ip;
+ be32 your_ip;
+ be32 server_ip;
+ be32 relay_ip;
+ u8 hw_addr[16];
+ u8 serv_name[64];
+ u8 boot_file[128];
+ u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCPACK 5
+static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+
+
+static const char * ipaddr_str(u32 addr)
+{
+ static char buf[17];
+
+ os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ const struct bootp_pkt *b;
+ struct sta_info *sta;
+ int exten_len;
+ const u8 *end, *pos;
+ int res, msgtype = 0, prefixlen = 32;
+ u32 subnet_mask = 0;
+ u16 tot_len;
+
+ exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+ if (exten_len < 4)
+ return;
+
+ b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+ tot_len = ntohs(b->iph.tot_len);
+ if (tot_len > (unsigned int) (len - ETH_HLEN))
+ return;
+
+ if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+ return;
+
+ /* Parse DHCP options */
+ end = (const u8 *) b + tot_len;
+ pos = &b->exten[4];
+ while (pos < end && *pos != 0xff) {
+ const u8 *opt = pos++;
+
+ if (*opt == 0) /* padding */
+ continue;
+
+ pos += *pos + 1;
+ if (pos >= end)
+ break;
+
+ switch (*opt) {
+ case 1: /* subnet mask */
+ if (opt[1] == 4)
+ subnet_mask = WPA_GET_BE32(&opt[2]);
+ if (subnet_mask == 0)
+ return;
+ while (!(subnet_mask & 0x1)) {
+ subnet_mask >>= 1;
+ prefixlen--;
+ }
+ break;
+ case 53: /* message type */
+ if (opt[1])
+ msgtype = opt[2];
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (msgtype == DHCPACK) {
+ if (b->your_ip == 0)
+ return;
+
+ /* DHCPACK for DHCPREQUEST */
+ sta = ap_get_sta(hapd, b->hw_addr);
+ if (!sta)
+ return;
+
+ wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+ " @ IPv4 address %s/%d",
+ MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+ prefixlen);
+
+ if (sta->ipaddr == b->your_ip)
+ return;
+
+ if (sta->ipaddr != 0) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+ ipaddr_str(be_to_host32(sta->ipaddr)));
+ hostapd_drv_br_delete_ip_neigh(hapd, 4,
+ (u8 *) &sta->ipaddr);
+ }
+
+ res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+ prefixlen, sta->addr);
+ if (res) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Adding ip neigh table failed: %d",
+ res);
+ return;
+ }
+ sta->ipaddr = b->your_ip;
+ }
+
+ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+ (u8 *) buf, len);
+ }
+ }
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+ hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+ L2_PACKET_FILTER_DHCP);
+ if (hapd->sock_dhcp == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+ l2_packet_deinit(hapd->sock_dhcp);
+}
diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000000000..93d0050fac247
--- /dev/null
+++ b/src/ap/dhcp_snoop.h
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 8980bec03f8b1..a0adc67db430b 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1,6 +1,6 @@
/*
* hostapd / Callback functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,10 +9,12 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
#include "radius/radius.h"
#include "drivers/driver.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
@@ -28,6 +30,8 @@
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -44,6 +48,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
#endif /* CONFIG_IEEE80211R */
u16 reason = WLAN_REASON_UNSPECIFIED;
u16 status = WLAN_STATUS_SUCCESS;
+ const u8 *p2p_dev_addr = NULL;
if (addr == NULL) {
/*
@@ -75,6 +80,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
ie = elems.wpa_ie - 2;
ielen = elems.wpa_ie_len + 2;
wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+ } else if (elems.osen) {
+ ie = elems.osen - 2;
+ ielen = elems.osen_len + 2;
+ wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
} else {
ie = NULL;
ielen = 0;
@@ -84,6 +95,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta = ap_get_sta(hapd, addr);
if (sta) {
+ ap_sta_no_session_timeout(hapd, sta);
accounting_sta_stop(hapd, sta);
/*
@@ -106,9 +118,36 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
wpabuf_free(sta->p2p_ie);
sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ if (elems.ht_capabilities &&
+ elems.ht_capabilities_len >=
+ sizeof(struct ieee80211_ht_capabilities) &&
+ (hapd->iface->conf->ht_capab &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+ struct ieee80211_ht_capabilities *ht_cap =
+ (struct ieee80211_ht_capabilities *)
+ elems.ht_capabilities;
+
+ if (le_to_host16(ht_cap->ht_capabilities_info) &
+ HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
+ }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_INTERWORKING
+ if (elems.ext_capab && elems.ext_capab_len > 4) {
+ if (elems.ext_capab[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
@@ -154,7 +193,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr,
+ p2p_dev_addr);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
"machine");
@@ -267,6 +307,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
sta->flags |= WLAN_STA_MAYBE_WPS;
wpabuf_free(wps);
#endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (elems.osen == NULL) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems.osen - 2, elems.osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
}
#ifdef CONFIG_WPS
skip_wpa_check:
@@ -277,6 +340,9 @@ skip_wpa_check:
sta->auth_alg, req_ies, req_ies_len);
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+ if (sta->auth_alg == WLAN_AUTH_FT)
+ ap_sta_set_authorized(hapd, sta, 1);
#else /* CONFIG_IEEE80211R */
/* Keep compiler silent about unused variables */
if (status) {
@@ -285,6 +351,9 @@ skip_wpa_check:
new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+ hostapd_set_sta_flags(hapd, sta);
if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -367,14 +436,16 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- int offset)
+ int offset, int width, int cf1, int cf2)
{
#ifdef NEED_AP_MLME
- int channel;
+ int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_INFO, "driver had channel switch: "
- "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+ HOSTAPD_LEVEL_INFO,
+ "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+ freq, ht, offset, width, channel_width_to_string(width),
+ cf1, cf2);
hapd->iface->freq = freq;
@@ -386,13 +457,124 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
return;
}
+ switch (width) {
+ case CHAN_WIDTH_80:
+ chwidth = VHT_CHANWIDTH_80MHZ;
+ break;
+ case CHAN_WIDTH_80P80:
+ chwidth = VHT_CHANWIDTH_80P80MHZ;
+ break;
+ case CHAN_WIDTH_160:
+ chwidth = VHT_CHANWIDTH_160MHZ;
+ break;
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ case CHAN_WIDTH_40:
+ default:
+ chwidth = VHT_CHANWIDTH_USE_HT;
+ break;
+ }
+
+ switch (hapd->iface->current_mode->mode) {
+ case HOSTAPD_MODE_IEEE80211A:
+ if (cf1 > 5000)
+ seg0_idx = (cf1 - 5000) / 5;
+ if (cf2 > 5000)
+ seg1_idx = (cf2 - 5000) / 5;
+ break;
+ default:
+ seg0_idx = hostapd_hw_get_channel(hapd, cf1);
+ seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+ break;
+ }
+
hapd->iconf->channel = channel;
hapd->iconf->ieee80211n = ht;
+ if (!ht)
+ hapd->iconf->ieee80211ac = 0;
hapd->iconf->secondary_channel = offset;
+ hapd->iconf->vht_oper_chwidth = chwidth;
+ hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
+ hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+
+ is_dfs = ieee80211_is_dfs(freq);
+
+ if (hapd->csa_in_progress &&
+ freq == hapd->cs_freq_params.freq) {
+ hostapd_cleanup_cs_params(hapd);
+ ieee802_11_set_beacon(hapd);
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ "freq=%d dfs=%d", freq, is_dfs);
+ } else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+ "freq=%d dfs=%d", freq, is_dfs);
+ }
#endif /* NEED_AP_MLME */
}
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code)
+{
+ switch (reason_code) {
+ case MAX_CLIENT_REACHED:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_MAX_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ case BLOCKED_CLIENT:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_REJECTED_BLOCKED_STA MACSTR,
+ MAC2STR(addr));
+ break;
+ }
+}
+
+
+#ifdef CONFIG_ACS
+static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+ u8 pri_channel, u8 sec_channel)
+{
+ int channel;
+ int ret;
+
+ if (hapd->iconf->channel) {
+ wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+ hapd->iconf->channel);
+ return;
+ }
+
+ hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
+
+ channel = pri_channel;
+ if (!channel) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "driver switched to bad channel");
+ return;
+ }
+
+ hapd->iconf->channel = channel;
+
+ if (sec_channel == 0)
+ hapd->iconf->secondary_channel = 0;
+ else if (sec_channel < pri_channel)
+ hapd->iconf->secondary_channel = -1;
+ else if (sec_channel > pri_channel)
+ hapd->iconf->secondary_channel = 1;
+ else {
+ wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+ return;
+ }
+
+ ret = hostapd_acs_completed(hapd->iface, 0);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "ACS: Possibly channel configuration is invalid");
+ }
+}
+#endif /* CONFIG_ACS */
+
+
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal)
@@ -452,7 +634,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
if (!sta) {
sta = ap_sta_add(hapd, rx_auth->peer);
if (sta == NULL) {
- status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ status = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
@@ -463,7 +645,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
"state machine");
@@ -484,39 +666,48 @@ fail:
static void hostapd_action_rx(struct hostapd_data *hapd,
- struct rx_action *action)
+ struct rx_mgmt *drv_mgmt)
{
+ struct ieee80211_mgmt *mgmt;
struct sta_info *sta;
+ size_t plen __maybe_unused;
+ u16 fc;
+
+ if (drv_mgmt->frame_len < 24 + 1)
+ return;
+
+ plen = drv_mgmt->frame_len - 24 - 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",
- action->category, (int) action->len);
+ mgmt->u.action.category, (int) plen);
- sta = ap_get_sta(hapd, action->sa);
+ sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
return;
}
#ifdef CONFIG_IEEE80211R
- if (action->category == WLAN_ACTION_FT) {
- wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
- __func__, (int) action->len);
- wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+ if (mgmt->u.action.category == WLAN_ACTION_FT) {
+ const u8 *payload = drv_mgmt->frame + 24 + 1;
+ wpa_ft_action_rx(sta->wpa_sm, payload, plen);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
- if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
- wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
- __func__, (int) action->len);
- ieee802_11_sa_query_action(hapd, action->sa,
- *(action->data + 1),
- action->data + 2);
+ 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);
}
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM
- if (action->category == WLAN_ACTION_WNM) {
- wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
- __func__, (int) action->len);
- ieee802_11_rx_wnm_action_ap(hapd, action);
+ if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
}
#endif /* CONFIG_WNM */
}
@@ -558,17 +749,32 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
}
-static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
{
struct hostapd_iface *iface = hapd->iface;
const struct ieee80211_hdr *hdr;
const u8 *bssid;
struct hostapd_frame_info fi;
+ int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+ size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+ char *hex = os_malloc(hex_len);
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+ rx_mgmt->frame_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+ os_free(hex);
+ }
+ return 1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
if (bssid == NULL)
- return;
+ return 0;
hapd = get_hapd_bssid(iface, bssid);
if (hapd == NULL) {
@@ -583,7 +789,7 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
hapd = iface->bss[0];
else
- return;
+ return 0;
}
os_memset(&fi, 0, sizeof(fi));
@@ -592,53 +798,25 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
if (hapd == HAPD_BROADCAST) {
size_t i;
- for (i = 0; i < iface->num_bss; i++)
- ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
- rx_mgmt->frame_len, &fi);
+ ret = 0;
+ for (i = 0; i < iface->num_bss; i++) {
+ /* if bss is set, driver will call this function for
+ * each bss individually. */
+ if (rx_mgmt->drv_priv &&
+ (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+ continue;
+
+ if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+ rx_mgmt->frame_len, &fi) > 0)
+ ret = 1;
+ }
} else
- ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+ ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+ &fi);
random_add_randomness(&fi, sizeof(fi));
-}
-
-static void hostapd_rx_action(struct hostapd_data *hapd,
- struct rx_action *rx_action)
-{
- struct rx_mgmt rx_mgmt;
- u8 *buf;
- struct ieee80211_hdr *hdr;
-
- wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
- " BSSID=" MACSTR " category=%u",
- MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
- MAC2STR(rx_action->bssid), rx_action->category);
- wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
- rx_action->data, rx_action->len);
-
- buf = os_zalloc(24 + 1 + rx_action->len);
- if (buf == NULL)
- return;
- hdr = (struct ieee80211_hdr *) buf;
- hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- if (rx_action->category == WLAN_ACTION_SA_QUERY) {
- /*
- * Assume frame was protected; it would have been dropped if
- * not.
- */
- hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
- }
- os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
- os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
- os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
- buf[24] = rx_action->category;
- os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
- os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
- rx_mgmt.frame = buf;
- rx_mgmt.frame_len = 24 + 1 + rx_action->len;
- hostapd_mgmt_rx(hapd, &rx_mgmt);
- os_free(buf);
+ return ret;
}
@@ -697,6 +875,181 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
}
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+ struct hostapd_iface *iface, unsigned int freq)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (!chan)
+ return NULL;
+ if ((unsigned int) chan->freq == freq)
+ return chan;
+ }
+
+ return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+ struct hostapd_channel_data *chan,
+ struct freq_survey *survey)
+{
+ if (!iface->chans_surveyed) {
+ chan->min_nf = survey->nf;
+ iface->lowest_nf = survey->nf;
+ } else {
+ if (dl_list_empty(&chan->survey_list))
+ chan->min_nf = survey->nf;
+ else if (survey->nf < chan->min_nf)
+ chan->min_nf = survey->nf;
+ if (survey->nf < iface->lowest_nf)
+ iface->lowest_nf = survey->nf;
+ }
+}
+
+
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+ struct survey_results *survey_res)
+{
+ struct hostapd_channel_data *chan;
+ struct freq_survey *survey;
+ u64 divisor, dividend;
+
+ survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+ list);
+ if (!survey || !survey->freq)
+ return;
+
+ chan = hostapd_get_mode_channel(iface, survey->freq);
+ if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+ survey->freq,
+ (unsigned long int) survey->channel_time,
+ (unsigned long int) survey->channel_time_busy);
+
+ if (survey->channel_time > iface->last_channel_time &&
+ survey->channel_time > survey->channel_time_busy) {
+ dividend = survey->channel_time_busy -
+ iface->last_channel_time_busy;
+ divisor = survey->channel_time - iface->last_channel_time;
+
+ iface->channel_utilization = dividend * 255 / divisor;
+ wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+ iface->channel_utilization);
+ }
+ iface->last_channel_time = survey->channel_time;
+ iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
+static void hostapd_event_get_survey(struct hostapd_data *hapd,
+ struct survey_results *survey_results)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct freq_survey *survey, *tmp;
+ struct hostapd_channel_data *chan;
+
+ if (dl_list_empty(&survey_results->survey_list)) {
+ wpa_printf(MSG_DEBUG, "No survey data received");
+ return;
+ }
+
+ if (survey_results->freq_filter) {
+ hostapd_single_channel_get_survey(iface, survey_results);
+ return;
+ }
+
+ dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+ struct freq_survey, list) {
+ chan = hostapd_get_mode_channel(iface, survey->freq);
+ if (!chan)
+ continue;
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+
+ dl_list_del(&survey->list);
+ dl_list_add_tail(&chan->survey_list, &survey->list);
+
+ hostapd_update_nf(iface, chan, survey);
+
+ iface->chans_surveyed++;
+ }
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+ hapd->conf->iface);
+
+ if (hapd->csa_in_progress) {
+ wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+ hapd->conf->iface);
+ hostapd_switch_channel_fallback(hapd->iface,
+ &hapd->cs_freq_params);
+ }
+}
+
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+ hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+ hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+ hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+ struct dfs_event *radar)
+{
+ wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+ hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+ radar->chan_offset, radar->chan_width,
+ radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
@@ -713,6 +1066,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
level = MSG_EXCESSIVE;
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+ level = MSG_EXCESSIVE;
}
wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
@@ -727,12 +1083,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (hapd->iface->scan_cb)
hapd->iface->scan_cb(hapd->iface);
break;
-#ifdef CONFIG_IEEE80211R
- case EVENT_FT_RRB_RX:
- wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
- data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
- break;
-#endif /* CONFIG_IEEE80211R */
case EVENT_WPS_BUTTON_PUSHED:
hostapd_wps_button_pushed(hapd, NULL);
break;
@@ -767,10 +1117,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
break;
+#endif /* NEED_AP_MLME */
case EVENT_RX_MGMT:
- hostapd_mgmt_rx(hapd, &data->rx_mgmt);
- break;
+ if (!data->rx_mgmt.frame)
+ break;
+#ifdef NEED_AP_MLME
+ if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
+ break;
#endif /* NEED_AP_MLME */
+ hostapd_action_rx(hapd, &data->rx_mgmt);
+ break;
case EVENT_RX_PROBE_REQ:
if (data->rx_probe_req.sa == NULL ||
data->rx_probe_req.ie == NULL)
@@ -791,6 +1147,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
data->eapol_rx.data_len);
break;
case EVENT_ASSOC:
+ if (!data)
+ return;
hostapd_notif_assoc(hapd, data->assoc_info.addr,
data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
@@ -809,15 +1167,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
break;
- case EVENT_RX_ACTION:
- if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
- data->rx_action.bssid == NULL)
- break;
-#ifdef NEED_AP_MLME
- hostapd_rx_action(hapd, &data->rx_action);
-#endif /* NEED_AP_MLME */
- hostapd_action_rx(hapd, &data->rx_action);
- break;
case EVENT_AUTH:
hostapd_notif_auth(hapd, &data->auth);
break;
@@ -826,8 +1175,84 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
hostapd_event_ch_switch(hapd, data->ch_switch.freq,
data->ch_switch.ht_enabled,
- data->ch_switch.ch_offset);
+ data->ch_switch.ch_offset,
+ data->ch_switch.ch_width,
+ data->ch_switch.cf1,
+ data->ch_switch.cf2);
+ break;
+ case EVENT_CONNECT_FAILED_REASON:
+ if (!data)
+ break;
+ hostapd_event_connect_failed_reason(
+ hapd, data->connect_failed_reason.addr,
+ data->connect_failed_reason.code);
+ break;
+ case EVENT_SURVEY:
+ hostapd_event_get_survey(hapd, &data->survey_results);
+ break;
+#ifdef NEED_AP_MLME
+ case EVENT_INTERFACE_UNAVAILABLE:
+ hostapd_event_iface_unavailable(hapd);
+ break;
+ case EVENT_DFS_RADAR_DETECTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_CAC_ABORTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+ break;
+ case EVENT_DFS_NOP_FINISHED:
+ if (!data)
+ break;
+ hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+ break;
+ case EVENT_CHANNEL_LIST_CHANGED:
+ /* channel list changed (regulatory?), update channel list */
+ /* TODO: check this. hostapd_get_hw_features() initializes
+ * too much stuff. */
+ /* hostapd_get_hw_features(hapd->iface); */
+ hostapd_channel_list_updated(
+ hapd->iface, data->channel_list_changed.initiator);
+ break;
+ case EVENT_DFS_CAC_STARTED:
+ if (!data)
+ break;
+ hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+ break;
+#endif /* NEED_AP_MLME */
+ case EVENT_INTERFACE_ENABLED:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+ if (hapd->disabled && hapd->started) {
+ hapd->disabled = 0;
+ /*
+ * Try to re-enable interface if the driver stopped it
+ * when the interface got disabled.
+ */
+ wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+ hapd->reenable_beacon = 1;
+ ieee802_11_set_beacon(hapd);
+ }
+ break;
+ case EVENT_INTERFACE_DISABLED:
+ hostapd_free_stas(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+ hapd->disabled = 1;
+ break;
+#ifdef CONFIG_ACS
+ case EVENT_ACS_CHANNEL_SELECTED:
+ hostapd_acs_channel_selected(
+ hapd, data->acs_selected_channels.pri_channel,
+ data->acs_selected_channels.sec_channel);
break;
+#endif /* CONFIG_ACS */
default:
wpa_printf(MSG_DEBUG, "Unknown event %d", event);
break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 79d50e51694e6..559d77f9ef2b8 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -83,12 +83,14 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
for (i = 0; i < argc; i++) {
if (os_strcmp(col[i], "password") == 0 && argv[i]) {
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
user->password_len = os_strlen(argv[i]);
user->password = (u8 *) os_strdup(argv[i]);
user->next = (void *) 1;
} else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
set_user_methods(user, argv[i]);
+ } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+ user->remediation = strlen(argv[i]) > 0;
}
}
@@ -116,7 +118,7 @@ static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
if (len <= user->identity_len &&
os_memcmp(argv[id], user->identity, len) == 0 &&
(user->password == NULL || len > user->password_len)) {
- os_free(user->password);
+ bin_clear_free(user->password, user->password_len);
user->password_len = os_strlen(argv[id]);
user->password = (u8 *) os_strdup(argv[id]);
user->next = (void *) 1;
@@ -156,8 +158,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
return NULL;
}
- os_free(hapd->tmp_eap_user.identity);
- os_free(hapd->tmp_eap_user.password);
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
hapd->tmp_eap_user.phase2 = phase2;
hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
@@ -173,8 +177,8 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
}
os_snprintf(cmd, sizeof(cmd),
- "SELECT password,methods FROM users WHERE "
- "identity='%s' AND phase2=%d;", id_str, phase2);
+ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+ id_str, phase2);
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
SQLITE_OK) {
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 851c183e97f89..9d19f98d0b7c7 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -1,6 +1,6 @@
/*
* Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +19,13 @@
#include "gas_serv.h"
+static void convert_to_protected_dual(struct wpabuf *msg)
+{
+ u8 *categ = wpabuf_mhead_u8(msg);
+ *categ = WLAN_ACTION_PROTECTED_DUAL;
+}
+
+
static struct gas_dialog_info *
gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
{
@@ -46,10 +53,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
* it to be that long.
*/
ap_sta_session_timeout(hapd, sta, 5);
+ } else {
+ ap_sta_replenish_timeout(hapd, sta, 5);
}
if (sta->gas_dialog == NULL) {
- sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+ sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
sizeof(struct gas_dialog_info));
if (sta->gas_dialog == NULL)
return NULL;
@@ -62,7 +71,6 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
continue;
dia = &sta->gas_dialog[i];
dia->valid = 1;
- dia->index = i;
dia->dialog_token = dialog_token;
sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
return dia;
@@ -150,6 +158,10 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
if (hapd->conf->hs20_operating_class)
wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+ if (hapd->conf->hs20_osu_providers_count)
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ if (hapd->conf->hs20_icons_count)
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
gas_anqp_set_element_len(buf, len);
}
#endif /* CONFIG_HS20 */
@@ -402,7 +414,7 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
gas_anqp_set_element_len(buf, realm_data_len);
}
gas_anqp_set_element_len(buf, len);
- } else if (nai_home_realm && hapd->conf->nai_realm_data) {
+ } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
home_realm_len);
}
@@ -505,18 +517,188 @@ static void anqp_add_operating_class(struct hostapd_data *hapd,
}
}
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+ struct hostapd_bss_config *bss,
+ struct hs20_osu_provider *p)
+{
+ u8 *len, *len2, *count;
+ unsigned int i;
+
+ len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+ /* OSU Friendly Name Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->friendly_name_count; i++) {
+ struct hostapd_lang_string *s = &p->friendly_name[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU Server URI */
+ if (p->server_uri) {
+ wpabuf_put_u8(buf, os_strlen(p->server_uri));
+ wpabuf_put_str(buf, p->server_uri);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Method List */
+ count = wpabuf_put(buf, 1);
+ for (i = 0; p->method_list[i] >= 0; i++)
+ wpabuf_put_u8(buf, p->method_list[i]);
+ *count = i;
+
+ /* Icons Available */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->icons_count; i++) {
+ size_t j;
+ struct hs20_icon *icon = NULL;
+
+ for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+ if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+ 0)
+ icon = &bss->hs20_icons[j];
+ }
+ if (!icon)
+ continue; /* icon info not found */
+
+ wpabuf_put_le16(buf, icon->width);
+ wpabuf_put_le16(buf, icon->height);
+ wpabuf_put_data(buf, icon->language, 3);
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_u8(buf, os_strlen(icon->name));
+ wpabuf_put_str(buf, icon->name);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ /* OSU_NAI */
+ if (p->osu_nai) {
+ wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+ wpabuf_put_str(buf, p->osu_nai);
+ } else
+ wpabuf_put_u8(buf, 0);
+
+ /* OSU Service Description Duples */
+ len2 = wpabuf_put(buf, 2);
+ for (i = 0; i < p->service_desc_count; i++) {
+ struct hostapd_lang_string *s = &p->service_desc[i];
+ wpabuf_put_u8(buf, 3 + s->name_len);
+ wpabuf_put_data(buf, s->lang, 3);
+ wpabuf_put_data(buf, s->name, s->name_len);
+ }
+ WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+ struct wpabuf *buf)
+{
+ if (hapd->conf->hs20_osu_providers_count) {
+ size_t i;
+ u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ /* OSU SSID */
+ wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+ wpabuf_put_data(buf, hapd->conf->osu_ssid,
+ hapd->conf->osu_ssid_len);
+
+ /* Number of OSU Providers */
+ wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+ for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+ anqp_add_osu_provider(
+ buf, hapd->conf,
+ &hapd->conf->hs20_osu_providers[i]);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+ }
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+ struct wpabuf *buf,
+ const u8 *name, size_t name_len)
+{
+ struct hs20_icon *icon;
+ size_t i;
+ u8 *len;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+ name, name_len);
+ for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+ icon = &hapd->conf->hs20_icons[i];
+ if (name_len == os_strlen(icon->name) &&
+ os_memcmp(name, icon->name, name_len) == 0)
+ break;
+ }
+
+ if (i < hapd->conf->hs20_icons_count)
+ icon = &hapd->conf->hs20_icons[i];
+ else
+ icon = NULL;
+
+ len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+ wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+ wpabuf_put_u8(buf, 0); /* Reserved */
+
+ if (icon) {
+ char *data;
+ size_t data_len;
+
+ data = os_readfile(icon->file, &data_len);
+ if (data == NULL || data_len > 65535) {
+ wpabuf_put_u8(buf, 2); /* Download Status:
+ * Unspecified file error */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ } else {
+ wpabuf_put_u8(buf, 0); /* Download Status: Success */
+ wpabuf_put_u8(buf, os_strlen(icon->type));
+ wpabuf_put_str(buf, icon->type);
+ wpabuf_put_le16(buf, data_len);
+ wpabuf_put_data(buf, data, data_len);
+ }
+ os_free(data);
+ } else {
+ wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0);
+ }
+
+ gas_anqp_set_element_len(buf, len);
+}
+
#endif /* CONFIG_HS20 */
static struct wpabuf *
gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
unsigned int request,
- struct gas_dialog_info *di,
- const u8 *home_realm, size_t home_realm_len)
+ const u8 *home_realm, size_t home_realm_len,
+ const u8 *icon_name, size_t icon_name_len)
{
struct wpabuf *buf;
+ size_t len;
+
+ len = 1400;
+ if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+ len += 1000;
+ if (request & ANQP_REQ_ICON_REQUEST)
+ len += 65536;
- buf = wpabuf_alloc(1400);
+ buf = wpabuf_alloc(len);
if (buf == NULL)
return NULL;
@@ -550,44 +732,32 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
anqp_add_connection_capability(hapd, buf);
if (request & ANQP_REQ_OPERATING_CLASS)
anqp_add_operating_class(hapd, buf);
+ if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+ anqp_add_osu_providers_list(hapd, buf);
+ if (request & ANQP_REQ_ICON_REQUEST)
+ anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
#endif /* CONFIG_HS20 */
return buf;
}
-static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
-{
- struct gas_dialog_info *dia = eloop_data;
-
- wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
- "dialog token %d", dia->dialog_token);
-
- gas_serv_dialog_clear(dia);
-}
-
-
struct anqp_query_info {
unsigned int request;
- unsigned int remote_request;
const u8 *home_realm_query;
size_t home_realm_query_len;
- u16 remote_delay;
+ const u8 *icon_name;
+ size_t icon_name_len;
+ int p2p_sd;
};
static void set_anqp_req(unsigned int bit, const char *name, int local,
- unsigned int remote, u16 remote_delay,
struct anqp_query_info *qi)
{
qi->request |= bit;
if (local) {
wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
- } else if (bit & remote) {
- wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
- qi->remote_request |= bit;
- if (remote_delay > qi->remote_delay)
- qi->remote_delay = remote_delay;
} else {
wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
}
@@ -599,43 +769,38 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
{
switch (info_id) {
case ANQP_CAPABILITY_LIST:
- set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
- 0, qi);
+ set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+ qi);
break;
case ANQP_VENUE_NAME:
set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
- hapd->conf->venue_name != NULL, 0, 0, qi);
+ hapd->conf->venue_name != NULL, qi);
break;
case ANQP_NETWORK_AUTH_TYPE:
set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
- hapd->conf->network_auth_type != NULL,
- 0, 0, qi);
+ hapd->conf->network_auth_type != NULL, qi);
break;
case ANQP_ROAMING_CONSORTIUM:
set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
- hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+ hapd->conf->roaming_consortium != NULL, qi);
break;
case ANQP_IP_ADDR_TYPE_AVAILABILITY:
set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
"IP Addr Type Availability",
- hapd->conf->ipaddr_type_configured,
- 0, 0, qi);
+ hapd->conf->ipaddr_type_configured, qi);
break;
case ANQP_NAI_REALM:
set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
- hapd->conf->nai_realm_data != NULL,
- 0, 0, qi);
+ hapd->conf->nai_realm_data != NULL, qi);
break;
case ANQP_3GPP_CELLULAR_NETWORK:
set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
"3GPP Cellular Network",
- hapd->conf->anqp_3gpp_cell_net != NULL,
- 0, 0, qi);
+ hapd->conf->anqp_3gpp_cell_net != NULL, qi);
break;
case ANQP_DOMAIN_NAME:
set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
- hapd->conf->domain_name != NULL,
- 0, 0, qi);
+ hapd->conf->domain_name != NULL, qi);
break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
@@ -667,29 +832,30 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
switch (subtype) {
case HS20_STYPE_CAPABILITY_LIST:
set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
- 1, 0, 0, qi);
+ 1, qi);
break;
case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
"Operator Friendly Name",
- hapd->conf->hs20_oper_friendly_name != NULL,
- 0, 0, qi);
+ hapd->conf->hs20_oper_friendly_name != NULL, qi);
break;
case HS20_STYPE_WAN_METRICS:
set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
- hapd->conf->hs20_wan_metrics != NULL,
- 0, 0, qi);
+ hapd->conf->hs20_wan_metrics != NULL, qi);
break;
case HS20_STYPE_CONNECTION_CAPABILITY:
set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
"Connection Capability",
hapd->conf->hs20_connection_capability != NULL,
- 0, 0, qi);
+ qi);
break;
case HS20_STYPE_OPERATING_CLASS:
set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
- hapd->conf->hs20_operating_class != NULL,
- 0, 0, qi);
+ hapd->conf->hs20_operating_class != NULL, qi);
+ break;
+ case HS20_STYPE_OSU_PROVIDERS_LIST:
+ set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+ hapd->conf->hs20_osu_providers_count, qi);
break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
@@ -716,6 +882,23 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
}
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+ const u8 *pos, const u8 *end,
+ struct anqp_query_info *qi)
+{
+ qi->request |= ANQP_REQ_ICON_REQUEST;
+ qi->icon_name = pos;
+ qi->icon_name_len = end - pos;
+ if (hapd->conf->hs20_icons_count) {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+ "(local)");
+ } else {
+ wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+ "available");
+ }
+}
+
+
static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
const u8 *pos, const u8 *end,
struct anqp_query_info *qi)
@@ -737,6 +920,21 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
return;
}
+#ifdef CONFIG_P2P
+ if (*pos == P2P_OUI_TYPE) {
+ /*
+ * This is for P2P SD and will be taken care of by the P2P
+ * implementation. This query needs to be ignored in the generic
+ * GAS server to avoid duplicated response.
+ */
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+ *pos);
+ qi->p2p_sd = 1;
+ return;
+ }
+#endif /* CONFIG_P2P */
+
if (*pos != HS20_ANQP_OUI_TYPE) {
wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
*pos);
@@ -760,6 +958,9 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
case HS20_STYPE_NAI_HOME_REALM_QUERY:
rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
break;
+ case HS20_STYPE_ICON_REQUEST:
+ rx_anqp_hs_icon_request(hapd, pos, end, qi);
+ break;
default:
wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
"%u", subtype);
@@ -772,17 +973,26 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
static void gas_serv_req_local_processing(struct hostapd_data *hapd,
const u8 *sa, u8 dialog_token,
- struct anqp_query_info *qi)
+ struct anqp_query_info *qi, int prot)
{
struct wpabuf *buf, *tx_buf;
- buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+ buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
qi->home_realm_query,
- qi->home_realm_query_len);
+ qi->home_realm_query_len,
+ qi->icon_name, qi->icon_name_len);
wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
buf);
if (!buf)
return;
+#ifdef CONFIG_P2P
+ if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+ wpa_printf(MSG_DEBUG,
+ "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+ wpabuf_free(buf);
+ return;
+ }
+#endif /* CONFIG_P2P */
if (wpabuf_len(buf) > hapd->gas_frag_limit ||
hapd->conf->gas_comeback_delay) {
@@ -802,13 +1012,17 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
"for " MACSTR " (dialog token %u)",
MAC2STR(sa), dialog_token);
wpabuf_free(buf);
- return;
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+ 0, NULL);
+ } else {
+ di->prot = prot;
+ di->sd_resp = buf;
+ di->sd_resp_pos = 0;
+ tx_buf = gas_anqp_build_initial_resp_buf(
+ dialog_token, WLAN_STATUS_SUCCESS,
+ comeback_delay, NULL);
}
- di->sd_resp = buf;
- di->sd_resp_pos = 0;
- tx_buf = gas_anqp_build_initial_resp_buf(
- dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
- NULL);
} else {
wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
tx_buf = gas_anqp_build_initial_resp_buf(
@@ -817,7 +1031,8 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
}
if (!tx_buf)
return;
-
+ if (prot)
+ convert_to_protected_dual(tx_buf);
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
wpabuf_head(tx_buf), wpabuf_len(tx_buf));
wpabuf_free(tx_buf);
@@ -826,7 +1041,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
const u8 *sa,
- const u8 *data, size_t len)
+ const u8 *data, size_t len, int prot)
{
const u8 *pos = data;
const u8 *end = data + len;
@@ -876,6 +1091,8 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
return;
wpabuf_put_data(buf, adv_proto, 2 + slen);
wpabuf_put_le16(buf, 0); /* Query Response Length */
+ if (prot)
+ convert_to_protected_dual(buf);
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
wpabuf_head(buf), wpabuf_len(buf));
wpabuf_free(buf);
@@ -927,100 +1144,13 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
pos += elen;
}
- gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
-}
-
-
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
- struct gas_dialog_info *dialog)
-{
- struct wpabuf *buf, *tx_buf;
- u8 dialog_token = dialog->dialog_token;
- size_t frag_len;
-
- if (dialog->sd_resp == NULL) {
- buf = gas_serv_build_gas_resp_payload(hapd,
- dialog->all_requested,
- dialog, NULL, 0);
- wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
- buf);
- if (!buf)
- goto tx_gas_response_done;
- dialog->sd_resp = buf;
- dialog->sd_resp_pos = 0;
- }
- frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
- if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
- hapd->conf->gas_comeback_delay) {
- u16 comeback_delay_tus = dialog->comeback_delay +
- GAS_SERV_COMEBACK_DELAY_FUDGE;
- u32 comeback_delay_secs, comeback_delay_usecs;
-
- if (hapd->conf->gas_comeback_delay) {
- /* Testing - allow overriding of the delay value */
- comeback_delay_tus = hapd->conf->gas_comeback_delay;
- }
-
- wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
- "%u) and comeback delay %u, "
- "requesting comebacks", (unsigned int) frag_len,
- (unsigned int) hapd->gas_frag_limit,
- dialog->comeback_delay);
- tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
- WLAN_STATUS_SUCCESS,
- comeback_delay_tus,
- NULL);
- if (tx_buf) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG,
- "GAS: Tx GAS Initial Resp (comeback = 10TU)");
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
- dst,
- wpabuf_head(tx_buf),
- wpabuf_len(tx_buf));
- }
- wpabuf_free(tx_buf);
-
- /* start a timer of 1.5 * comeback-delay */
- comeback_delay_tus = comeback_delay_tus +
- (comeback_delay_tus / 2);
- comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
- comeback_delay_usecs = (comeback_delay_tus * 1024) -
- (comeback_delay_secs * 1000000);
- eloop_register_timeout(comeback_delay_secs,
- comeback_delay_usecs,
- gas_serv_clear_cached_ies, dialog,
- NULL);
- goto tx_gas_response_done;
- }
-
- buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
- dialog->sd_resp_pos, frag_len);
- if (buf == NULL) {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
- "failed");
- goto tx_gas_response_done;
- }
- tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
- WLAN_STATUS_SUCCESS, 0, buf);
- wpabuf_free(buf);
- if (tx_buf == NULL)
- goto tx_gas_response_done;
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
- "Response (frag_id %d frag_len %d)",
- dialog->sd_frag_id, (int) frag_len);
- dialog->sd_frag_id++;
-
- hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
- wpabuf_head(tx_buf), wpabuf_len(tx_buf));
- wpabuf_free(tx_buf);
-tx_gas_response_done:
- gas_serv_clear_cached_ies(dialog, NULL);
+ gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
}
static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
const u8 *sa,
- const u8 *data, size_t len)
+ const u8 *data, size_t len, int prot)
{
struct gas_dialog_info *dialog;
struct wpabuf *buf, *tx_buf;
@@ -1051,33 +1181,6 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
goto send_resp;
}
- if (dialog->sd_resp == NULL) {
- wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
- dialog->requested, dialog->received);
- if ((dialog->requested & dialog->received) !=
- dialog->requested) {
- wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
- "from remote processing");
- gas_serv_dialog_clear(dialog);
- tx_buf = gas_anqp_build_comeback_resp_buf(
- dialog_token,
- WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
- NULL);
- if (tx_buf == NULL)
- return;
- goto send_resp;
- }
-
- buf = gas_serv_build_gas_resp_payload(hapd,
- dialog->all_requested,
- dialog, NULL, 0);
- wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
- buf);
- if (!buf)
- goto rx_gas_comeback_req_done;
- dialog->sd_resp = buf;
- dialog->sd_resp_pos = 0;
- }
frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
if (frag_len > hapd->gas_frag_limit) {
frag_len = hapd->gas_frag_limit;
@@ -1090,15 +1193,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
if (buf == NULL) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
"buffer");
- goto rx_gas_comeback_req_done;
+ gas_serv_dialog_clear(dialog);
+ return;
}
tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
WLAN_STATUS_SUCCESS,
dialog->sd_frag_id,
more, 0, buf);
wpabuf_free(buf);
- if (tx_buf == NULL)
- goto rx_gas_comeback_req_done;
+ if (tx_buf == NULL) {
+ gas_serv_dialog_clear(dialog);
+ return;
+ }
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
"(frag_id %d more=%d frag_len=%d)",
dialog->sd_frag_id, more, (int) frag_len);
@@ -1118,13 +1224,11 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
}
send_resp:
+ if (prot)
+ convert_to_protected_dual(tx_buf);
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
wpabuf_head(tx_buf), wpabuf_len(tx_buf));
wpabuf_free(tx_buf);
- return;
-
-rx_gas_comeback_req_done:
- gas_serv_clear_cached_ies(dialog, NULL);
}
@@ -1133,24 +1237,30 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
{
struct hostapd_data *hapd = ctx;
const struct ieee80211_mgmt *mgmt;
- size_t hdr_len;
const u8 *sa, *data;
+ int prot;
mgmt = (const struct ieee80211_mgmt *) buf;
- hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
- if (hdr_len > len)
+ if (len < IEEE80211_HDRLEN + 2)
return;
- if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
return;
+ /*
+ * Note: Public Action and Protected Dual of Public Action frames share
+ * the same payload structure, so it is fine to use definitions of
+ * Public Action frames to process both.
+ */
+ prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
sa = mgmt->sa;
- len -= hdr_len;
- data = &mgmt->u.action.u.public_action.action;
+ len -= IEEE80211_HDRLEN + 1;
+ data = buf + IEEE80211_HDRLEN + 1;
switch (data[0]) {
case WLAN_PA_GAS_INITIAL_REQ:
- gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+ gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
break;
case WLAN_PA_GAS_COMEBACK_REQ:
- gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+ gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
break;
}
}
@@ -1158,8 +1268,8 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
int gas_serv_init(struct hostapd_data *hapd)
{
- hapd->public_action_cb = gas_serv_rx_public_action;
- hapd->public_action_cb_ctx = hapd;
+ hapd->public_action_cb2 = gas_serv_rx_public_action;
+ hapd->public_action_cb2_ctx = hapd;
hapd->gas_frag_limit = 1400;
if (hapd->conf->gas_frag_limit > 0)
hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4213cf6da0207..4ec3201967c00 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -1,6 +1,6 @@
/*
* Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -37,29 +37,22 @@
(0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
#define ANQP_REQ_OPERATING_CLASS \
(0x10000 << HS20_STYPE_OPERATING_CLASS)
-
-/* To account for latencies between hostapd and external ANQP processor */
-#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
-#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+ (0x10000 << HS20_STYPE_ICON_REQUEST)
struct gas_dialog_info {
u8 valid;
- u8 index;
struct wpabuf *sd_resp; /* Fragmented response */
u8 dialog_token;
size_t sd_resp_pos; /* Offset in sd_resp */
u8 sd_frag_id;
- u16 comeback_delay;
-
- unsigned int requested;
- unsigned int received;
- unsigned int all_requested;
+ int prot; /* whether Protected Dual of Public Action frame is used */
};
struct hostapd_data;
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
- struct gas_dialog_info *dialog);
struct gas_dialog_info *
gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
u8 dialog_token);
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index cef9dafc52e05..3e4e16b4f396a 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,9 +11,12 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "radius/radius_client.h"
#include "radius/radius_das.h"
-#include "drivers/driver.h"
+#include "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
#include "hostapd.h"
#include "authsrv.h"
#include "sta_info.h"
@@ -32,14 +35,19 @@
#include "ap_config.h"
#include "p2p_hostapd.h"
#include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.h"
static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
-
-extern int wpa_debug_level;
-extern struct wpa_driver_ops *wpa_drivers[];
+static int setup_interface2(struct hostapd_iface *iface);
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -61,10 +69,21 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
static void hostapd_reload_bss(struct hostapd_data *hapd)
{
+ struct hostapd_ssid *ssid;
+
#ifndef CONFIG_NO_RADIUS
radius_client_reconfig(hapd->radius, hapd->conf->radius);
#endif /* CONFIG_NO_RADIUS */
+ ssid = &hapd->conf->ssid;
+ if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
+ ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
+ /*
+ * Force PSK to be derived again since SSID or passphrase may
+ * have changed.
+ */
+ hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
+ }
if (hostapd_setup_wpa_psk(hapd->conf)) {
wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
"after reloading configuration");
@@ -75,7 +94,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
else
hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
- if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
+ if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
hostapd_setup_wpa(hapd);
if (hapd->wpa_auth)
wpa_init_keys(hapd->wpa_auth);
@@ -108,19 +127,10 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
}
-int hostapd_reload_config(struct hostapd_iface *iface)
+static void hostapd_clear_old(struct hostapd_iface *iface)
{
- struct hostapd_data *hapd = iface->bss[0];
- struct hostapd_config *newconf, *oldconf;
size_t j;
- if (iface->interfaces == NULL ||
- iface->interfaces->config_read_cb == NULL)
- return -1;
- newconf = iface->interfaces->config_read_cb(iface->config_fname);
- if (newconf == NULL)
- return -1;
-
/*
* Deauthenticate all stations since the new configuration may not
* allow them to use the BSS anymore.
@@ -136,6 +146,31 @@ int hostapd_reload_config(struct hostapd_iface *iface)
radius_client_flush(iface->bss[j]->radius, 0);
#endif /* CONFIG_NO_RADIUS */
}
+}
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+ struct hostapd_data *hapd = iface->bss[0];
+ struct hostapd_config *newconf, *oldconf;
+ size_t j;
+
+ if (iface->config_fname == NULL) {
+ /* Only in-memory config in use - assume it has been updated */
+ hostapd_clear_old(iface);
+ for (j = 0; j < iface->num_bss; j++)
+ hostapd_reload_bss(iface->bss[j]);
+ return 0;
+ }
+
+ if (iface->interfaces == NULL ||
+ iface->interfaces->config_read_cb == NULL)
+ return -1;
+ newconf = iface->interfaces->config_read_cb(iface->config_fname);
+ if (newconf == NULL)
+ return -1;
+
+ hostapd_clear_old(iface);
oldconf = hapd->iconf;
iface->conf = newconf;
@@ -143,7 +178,18 @@ int hostapd_reload_config(struct hostapd_iface *iface)
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
hapd->iconf = newconf;
- hapd->conf = &newconf->bss[j];
+ hapd->iconf->channel = oldconf->channel;
+ hapd->iconf->secondary_channel = oldconf->secondary_channel;
+ hapd->iconf->ieee80211n = oldconf->ieee80211n;
+ hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
+ hapd->iconf->ht_capab = oldconf->ht_capab;
+ hapd->iconf->vht_capab = oldconf->vht_capab;
+ hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
+ hapd->iconf->vht_oper_centr_freq_seg0_idx =
+ oldconf->vht_oper_centr_freq_seg0_idx;
+ hapd->iconf->vht_oper_centr_freq_seg1_idx =
+ oldconf->vht_oper_centr_freq_seg1_idx;
+ hapd->conf = newconf->bss[j];
hostapd_reload_bss(hapd);
}
@@ -205,36 +251,30 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
errors++;
}
- if (ssid->dyn_vlan_keys) {
- size_t i;
- for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
- const char *ifname;
- struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i];
- if (key == NULL)
- continue;
- ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan,
- i);
- if (ifname == NULL)
- continue;
-
- idx = key->idx;
- if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
- broadcast_ether_addr, idx, 1,
- NULL, 0, key->key[idx],
- key->len[idx])) {
- wpa_printf(MSG_WARNING, "Could not set "
- "dynamic VLAN WEP encryption.");
- errors++;
- }
- }
- }
-
return errors;
}
static void hostapd_free_hapd_data(struct hostapd_data *hapd)
{
+ os_free(hapd->probereq_cb);
+ hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+ wpabuf_free(hapd->p2p_beacon_ie);
+ hapd->p2p_beacon_ie = NULL;
+ wpabuf_free(hapd->p2p_probe_resp_ie);
+ hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+ if (!hapd->started) {
+ wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+ __func__, hapd->conf->iface);
+ return;
+ }
+ hapd->started = 0;
+
+ wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
iapp_deinit(hapd->iapp);
hapd->iapp = NULL;
accounting_deinit(hapd);
@@ -252,32 +292,45 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
authsrv_deinit(hapd);
- if (hapd->interface_added &&
- hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
- wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
- hapd->conf->iface);
+ if (hapd->interface_added) {
+ hapd->interface_added = 0;
+ if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+ wpa_printf(MSG_WARNING,
+ "Failed to remove BSS interface %s",
+ hapd->conf->iface);
+ hapd->interface_added = 1;
+ } else {
+ /*
+ * Since this was a dynamically added interface, the
+ * driver wrapper may have removed its internal instance
+ * and hapd->drv_priv is not valid anymore.
+ */
+ hapd->drv_priv = NULL;
+ }
}
- os_free(hapd->probereq_cb);
- hapd->probereq_cb = NULL;
-
-#ifdef CONFIG_P2P
- wpabuf_free(hapd->p2p_beacon_ie);
- hapd->p2p_beacon_ie = NULL;
- wpabuf_free(hapd->p2p_probe_resp_ie);
- hapd->p2p_probe_resp_ie = NULL;
-#endif /* CONFIG_P2P */
-
wpabuf_free(hapd->time_adv);
#ifdef CONFIG_INTERWORKING
gas_serv_deinit(hapd);
#endif /* CONFIG_INTERWORKING */
+ bss_load_update_deinit(hapd);
+ ndisc_snoop_deinit(hapd);
+ dhcp_snoop_deinit(hapd);
+ x_snoop_deinit(hapd);
+
#ifdef CONFIG_SQLITE
- os_free(hapd->tmp_eap_user.identity);
- os_free(hapd->tmp_eap_user.password);
+ bin_clear_free(hapd->tmp_eap_user.identity,
+ hapd->tmp_eap_user.identity_len);
+ bin_clear_free(hapd->tmp_eap_user.password,
+ hapd->tmp_eap_user.password_len);
#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_MESH
+ wpabuf_free(hapd->mesh_pending_auth);
+ hapd->mesh_pending_auth = NULL;
+#endif /* CONFIG_MESH */
}
@@ -286,13 +339,13 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
* @hapd: Pointer to BSS data
*
* This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
+ * Most of the modules that are initialized in hostapd_setup_bss() are
+ * deinitialized here.
*/
static void hostapd_cleanup(struct hostapd_data *hapd)
{
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+ hapd->conf->iface);
if (hapd->iface->interfaces &&
hapd->iface->interfaces->ctrl_iface_deinit)
hapd->iface->interfaces->ctrl_iface_deinit(hapd);
@@ -300,20 +353,14 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
}
-/**
- * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
- * @iface: Pointer to interface data
- *
- * This function is called before per-BSS data structures are deinitialized
- * with hostapd_cleanup().
- */
-static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
-{
-}
-
-
static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
{
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = NULL;
os_free(iface->current_rates);
@@ -333,19 +380,23 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
*/
static void hostapd_cleanup_iface(struct hostapd_iface *iface)
{
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+
hostapd_cleanup_iface_partial(iface);
hostapd_config_free(iface->conf);
iface->conf = NULL;
os_free(iface->config_fname);
os_free(iface->bss);
+ wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
os_free(iface);
}
static void hostapd_clear_wep(struct hostapd_data *hapd)
{
- if (hapd->drv_priv) {
+ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
}
@@ -396,11 +447,15 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
return 0;
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries");
- if (hostapd_flush(hapd)) {
- wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to "
- "kernel driver");
- ret = -1;
+ if (!hapd->iface->driver_ap_teardown) {
+ wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+ "Flushing old station entries");
+
+ if (hostapd_flush(hapd)) {
+ wpa_msg(hapd->msg_ctx, MSG_WARNING,
+ "Could not connect to kernel driver");
+ ret = -1;
+ }
}
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
os_memset(addr, 0xff, ETH_ALEN);
@@ -411,6 +466,14 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
}
+static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+ hostapd_free_stas(hapd);
+ hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+ hostapd_clear_wep(hapd);
+}
+
+
/**
* hostapd_validate_bssid_configuration - Validate BSSID configuration
* @iface: Pointer to interface data
@@ -437,7 +500,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
/* Determine the bits necessary to any configured BSSIDs,
if they are higher than the number of BSSIDs. */
for (j = 0; j < iface->conf->num_bss; j++) {
- if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) {
+ if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
if (j)
auto_addr++;
continue;
@@ -445,7 +508,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
for (i = 0; i < ETH_ALEN; i++) {
mask[i] |=
- iface->conf->bss[j].bssid[i] ^
+ iface->conf->bss[j]->bssid[i] ^
hapd->own_addr[i];
}
}
@@ -510,7 +573,7 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
size_t i;
for (i = 0; i < conf->num_bss; i++) {
- if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+ if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
return 1;
}
}
@@ -524,57 +587,223 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
struct radius_das_attrs *attr)
{
- /* TODO */
+ if (attr->nas_identifier &&
+ (!hapd->conf->nas_identifier ||
+ os_strlen(hapd->conf->nas_identifier) !=
+ attr->nas_identifier_len ||
+ os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+ attr->nas_identifier_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+ return 1;
+ }
+
+ if (attr->nas_ip_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+ 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+ return 1;
+ }
+
+#ifdef CONFIG_IPV6
+ if (attr->nas_ipv6_addr &&
+ (hapd->conf->own_ip_addr.af != AF_INET6 ||
+ os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+ != 0)) {
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+ return 1;
+ }
+#endif /* CONFIG_IPV6 */
+
return 0;
}
static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
- struct radius_das_attrs *attr)
+ struct radius_das_attrs *attr,
+ int *multi)
{
- struct sta_info *sta = NULL;
+ struct sta_info *selected, *sta;
char buf[128];
+ int num_attr = 0;
+ int count;
- if (attr->sta_addr)
+ *multi = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next)
+ sta->radius_das_match = 1;
+
+ if (attr->sta_addr) {
+ num_attr++;
sta = ap_get_sta(hapd, attr->sta_addr);
+ if (!sta) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No Calling-Station-Id match");
+ return NULL;
+ }
- if (sta == NULL && attr->acct_session_id &&
- attr->acct_session_id_len == 17) {
+ selected = sta;
for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta != selected)
+ sta->radius_das_match = 0;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+ }
+
+ if (attr->acct_session_id) {
+ num_attr++;
+ if (attr->acct_session_id_len != 17) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
os_snprintf(buf, sizeof(buf), "%08X-%08X",
sta->acct_session_id_hi,
sta->acct_session_id_lo);
- if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
- break;
+ if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+ return NULL;
}
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
}
- if (sta == NULL && attr->cui) {
+ if (attr->acct_multi_session_id) {
+ num_attr++;
+ if (attr->acct_multi_session_id_len != 17) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+ return NULL;
+ }
+ count = 0;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->radius_das_match)
+ continue;
+ if (!sta->eapol_sm ||
+ !sta->eapol_sm->acct_multi_session_id_hi) {
+ sta->radius_das_match = 0;
+ continue;
+ }
+ os_snprintf(buf, sizeof(buf), "%08X+%08X",
+ sta->eapol_sm->acct_multi_session_id_hi,
+ sta->eapol_sm->acct_multi_session_id_lo);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
+ 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Acct-Multi-Session-Id match");
+ }
+
+ if (attr->cui) {
+ num_attr++;
+ count = 0;
+
for (sta = hapd->sta_list; sta; sta = sta->next) {
struct wpabuf *cui;
+
+ if (!sta->radius_das_match)
+ continue;
cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
- if (cui && wpabuf_len(cui) == attr->cui_len &&
+ if (!cui || wpabuf_len(cui) != attr->cui_len ||
os_memcmp(wpabuf_head(cui), attr->cui,
- attr->cui_len) == 0)
- break;
+ attr->cui_len) != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+ return NULL;
}
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Chargeable-User-Identity match");
}
- if (sta == NULL && attr->user_name) {
+ if (attr->user_name) {
+ num_attr++;
+ count = 0;
+
for (sta = hapd->sta_list; sta; sta = sta->next) {
u8 *identity;
size_t identity_len;
+
+ if (!sta->radius_das_match)
+ continue;
identity = ieee802_1x_get_identity(sta->eapol_sm,
&identity_len);
- if (identity &&
- identity_len == attr->user_name_len &&
+ if (!identity ||
+ identity_len != attr->user_name_len ||
os_memcmp(identity, attr->user_name, identity_len)
- == 0)
- break;
+ != 0)
+ sta->radius_das_match = 0;
+ else
+ count++;
+ }
+
+ if (count == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No matches remaining after User-Name check");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: User-Name match");
+ }
+
+ if (num_attr == 0) {
+ /*
+ * In theory, we could match all current associations, but it
+ * seems safer to just reject requests that do not include any
+ * session identification attributes.
+ */
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: No session identification attributes included");
+ return NULL;
+ }
+
+ selected = NULL;
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (sta->radius_das_match) {
+ if (selected) {
+ *multi = 1;
+ return NULL;
+ }
+ selected = sta;
}
}
- return sta;
+ return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+ struct radius_das_attrs *attr)
+{
+ if (!hapd->wpa_auth)
+ return -1;
+ return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
}
@@ -583,13 +812,30 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ int multi;
if (hostapd_das_nas_mismatch(hapd, attr))
return RADIUS_DAS_NAS_MISMATCH;
- sta = hostapd_das_find_sta(hapd, attr);
- if (sta == NULL)
+ sta = hostapd_das_find_sta(hapd, attr, &multi);
+ if (sta == NULL) {
+ if (multi) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: Multiple sessions match - not supported");
+ return RADIUS_DAS_MULTI_SESSION_MATCH;
+ }
+ if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "RADIUS DAS: PMKSA cache entry matched");
+ return RADIUS_DAS_SUCCESS;
+ }
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
return RADIUS_DAS_SESSION_NOT_FOUND;
+ }
+
+ wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+ " - disconnecting", MAC2STR(sta->addr));
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -604,7 +850,8 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
/**
* hostapd_setup_bss - Per-BSS setup (initialization)
* @hapd: Pointer to BSS data
- * @first: Whether this BSS is the first BSS of an interface
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ * but interface may exist
*
* This function is used to initialize all per-BSS data structures and
* resources. This gets called in a loop for each BSS when an interface is
@@ -618,35 +865,54 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
int ssid_len, set_ssid;
char force_ifname[IFNAMSIZ];
u8 if_addr[ETH_ALEN];
+ int flush_old_stations = 1;
- if (!first) {
- if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) {
+ wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+ __func__, hapd, conf->iface, first);
+
+#ifdef EAP_SERVER_TNC
+ if (conf->tnc && tncs_global_init() < 0) {
+ wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+ return -1;
+ }
+#endif /* EAP_SERVER_TNC */
+
+ if (hapd->started) {
+ wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
+ __func__, conf->iface);
+ return -1;
+ }
+ hapd->started = 1;
+
+ if (!first || first == -1) {
+ if (hostapd_mac_comp_empty(conf->bssid) == 0) {
/* Allocate the next available BSSID. */
do {
inc_byte_array(hapd->own_addr, ETH_ALEN);
} while (mac_in_conf(hapd->iconf, hapd->own_addr));
} else {
/* Allocate the configured BSSID. */
- os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN);
+ os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
if (hostapd_mac_comp(hapd->own_addr,
hapd->iface->bss[0]->own_addr) ==
0) {
wpa_printf(MSG_ERROR, "BSS '%s' may not have "
"BSSID set to the MAC address of "
- "the radio", hapd->conf->iface);
+ "the radio", conf->iface);
return -1;
}
}
hapd->interface_added = 1;
if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
- hapd->conf->iface, hapd->own_addr, hapd,
+ conf->iface, hapd->own_addr, hapd,
&hapd->drv_priv, force_ifname, if_addr,
- hapd->conf->bridge[0] ? hapd->conf->bridge :
- NULL)) {
+ conf->bridge[0] ? conf->bridge : NULL,
+ first == -1)) {
wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
MACSTR ")", MAC2STR(hapd->own_addr));
+ hapd->interface_added = 0;
return -1;
}
}
@@ -654,11 +920,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
if (conf->wmm_enabled < 0)
conf->wmm_enabled = hapd->iconf->ieee80211n;
- hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_MESH
+ if (hapd->iface->mconf == NULL)
+ flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+ if (flush_old_stations)
+ hostapd_flush_old_stations(hapd,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
- if (hostapd_setup_encryption(hapd->conf->iface, hapd))
+ if (hostapd_setup_encryption(conf->iface, hapd))
return -1;
/*
@@ -692,9 +965,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
if (!hostapd_drv_none(hapd)) {
wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
" and ssid \"%s\"",
- hapd->conf->iface, MAC2STR(hapd->own_addr),
- wpa_ssid_txt(hapd->conf->ssid.ssid,
- hapd->conf->ssid.ssid_len));
+ conf->iface, MAC2STR(hapd->own_addr),
+ wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
}
if (hostapd_setup_wpa_psk(conf)) {
@@ -710,7 +982,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (wpa_debug_level == MSG_MSGDUMP)
+ if (wpa_debug_level <= MSG_MSGDUMP)
conf->radius->msg_dumps = 1;
#ifndef CONFIG_NO_RADIUS
hapd->radius = radius_client_init(hapd, conf->radius);
@@ -719,17 +991,17 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (hapd->conf->radius_das_port) {
+ if (conf->radius_das_port) {
struct radius_das_conf das_conf;
os_memset(&das_conf, 0, sizeof(das_conf));
- das_conf.port = hapd->conf->radius_das_port;
- das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
+ das_conf.port = conf->radius_das_port;
+ das_conf.shared_secret = conf->radius_das_shared_secret;
das_conf.shared_secret_len =
- hapd->conf->radius_das_shared_secret_len;
- das_conf.client_addr = &hapd->conf->radius_das_client_addr;
- das_conf.time_window = hapd->conf->radius_das_time_window;
+ conf->radius_das_shared_secret_len;
+ das_conf.client_addr = &conf->radius_das_client_addr;
+ das_conf.time_window = conf->radius_das_time_window;
das_conf.require_event_timestamp =
- hapd->conf->radius_das_require_event_timestamp;
+ conf->radius_das_require_event_timestamp;
das_conf.ctx = hapd;
das_conf.disconnect = hostapd_das_disconnect;
hapd->radius_das = radius_das_init(&das_conf);
@@ -756,7 +1028,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+ if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
return -1;
if (accounting_init(hapd)) {
@@ -764,8 +1036,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
return -1;
}
- if (hapd->conf->ieee802_11f &&
- (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) {
+ if (conf->ieee802_11f &&
+ (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) {
wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
"failed.");
return -1;
@@ -776,21 +1048,47 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
wpa_printf(MSG_ERROR, "GAS server initialization failed");
return -1;
}
+
+ if (conf->qos_map_set_len &&
+ hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+ conf->qos_map_set_len)) {
+ wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+ return -1;
+ }
#endif /* CONFIG_INTERWORKING */
- if (hapd->iface->interfaces &&
- hapd->iface->interfaces->ctrl_iface_init &&
- hapd->iface->interfaces->ctrl_iface_init(hapd)) {
- wpa_printf(MSG_ERROR, "Failed to setup control interface");
+ if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+ wpa_printf(MSG_ERROR, "BSS Load initialization failed");
return -1;
}
+ if (conf->proxy_arp) {
+ if (x_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Generic snooping infrastructure initialization failed");
+ return -1;
+ }
+
+ if (dhcp_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "DHCP snooping initialization failed");
+ return -1;
+ }
+
+ if (ndisc_snoop_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Neighbor Discovery snooping initialization failed");
+ return -1;
+ }
+ }
+
if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
wpa_printf(MSG_ERROR, "VLAN initialization failed.");
return -1;
}
- ieee802_11_set_beacon(hapd);
+ if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+ return -1;
if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
return -1;
@@ -808,6 +1106,11 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
int i;
struct hostapd_tx_queue_params *p;
+#ifdef CONFIG_MESH
+ if (iface->mconf == NULL)
+ return;
+#endif /* CONFIG_MESH */
+
for (i = 0; i < NUM_TX_QUEUES; i++) {
p = &iface->conf->tx_queue[i];
@@ -821,11 +1124,152 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
}
+static int hostapd_set_acl_list(struct hostapd_data *hapd,
+ struct mac_acl_entry *mac_acl,
+ int n_entries, u8 accept_acl)
+{
+ struct hostapd_acl_params *acl_params;
+ int i, err;
+
+ acl_params = os_zalloc(sizeof(*acl_params) +
+ (n_entries * sizeof(acl_params->mac_acl[0])));
+ if (!acl_params)
+ return -ENOMEM;
+
+ for (i = 0; i < n_entries; i++)
+ os_memcpy(acl_params->mac_acl[i].addr, mac_acl[i].addr,
+ ETH_ALEN);
+
+ acl_params->acl_policy = accept_acl;
+ acl_params->num_mac_acl = n_entries;
+
+ err = hostapd_drv_set_acl(hapd, acl_params);
+
+ os_free(acl_params);
+
+ return err;
+}
+
+
+static void hostapd_set_acl(struct hostapd_data *hapd)
+{
+ struct hostapd_config *conf = hapd->iconf;
+ int err;
+ u8 accept_acl;
+
+ if (hapd->iface->drv_max_acl_mac_addrs == 0)
+ return;
+
+ if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+ accept_acl = 1;
+ err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
+ conf->bss[0]->num_accept_mac,
+ accept_acl);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "Failed to set accept acl");
+ return;
+ }
+ } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+ accept_acl = 0;
+ err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
+ conf->bss[0]->num_deny_mac,
+ accept_acl);
+ if (err) {
+ wpa_printf(MSG_DEBUG, "Failed to set deny acl");
+ return;
+ }
+ }
+}
+
+
+static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+{
+ if (!hapd->iface->interfaces ||
+ !hapd->iface->interfaces->ctrl_iface_init)
+ return 0;
+
+ if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to setup control interface for %s",
+ hapd->conf->iface);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int start_ctrl_iface(struct hostapd_iface *iface)
+{
+ size_t i;
+
+ if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
+ return 0;
+
+ for (i = 0; i < iface->num_bss; i++) {
+ struct hostapd_data *hapd = iface->bss[i];
+ if (iface->interfaces->ctrl_iface_init(hapd)) {
+ wpa_printf(MSG_ERROR,
+ "Failed to setup control interface for %s",
+ hapd->conf->iface);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_iface *iface = eloop_ctx;
+
+ if (!iface->wait_channel_update) {
+ wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
+ return;
+ }
+
+ /*
+ * It is possible that the existing channel list is acceptable, so try
+ * to proceed.
+ */
+ wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
+ setup_interface2(iface);
+}
+
+
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
+{
+ if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
+ return;
+
+ wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ setup_interface2(iface);
+}
+
+
static int setup_interface(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
size_t i;
- char country[4];
+
+ /*
+ * It is possible that setup_interface() is called after the interface
+ * was disabled etc., in which case driver_ap_teardown is possibly set
+ * to 1. Clear it here so any other key/station deletion, which is not
+ * part of a teardown flow, would also call the relevant driver
+ * callbacks.
+ */
+ iface->driver_ap_teardown = 0;
+
+ if (!iface->phy[0]) {
+ const char *phy = hostapd_drv_get_radio_name(hapd);
+ if (phy) {
+ wpa_printf(MSG_DEBUG, "phy: %s", phy);
+ os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+ }
+ }
/*
* Make sure that all BSSes get configured with a pointer to the same
@@ -839,15 +1283,49 @@ static int setup_interface(struct hostapd_iface *iface)
if (hostapd_validate_bssid_configuration(iface))
return -1;
+ /*
+ * Initialize control interfaces early to allow external monitoring of
+ * channel setup operations that may take considerable amount of time
+ * especially for DFS cases.
+ */
+ if (start_ctrl_iface(iface))
+ return -1;
+
if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+ char country[4], previous_country[4];
+
+ hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
+ if (hostapd_get_country(hapd, previous_country) < 0)
+ previous_country[0] = '\0';
+
os_memcpy(country, hapd->iconf->country, 3);
country[3] = '\0';
if (hostapd_set_country(hapd, country) < 0) {
wpa_printf(MSG_ERROR, "Failed to set country code");
return -1;
}
+
+ wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
+ previous_country, country);
+
+ if (os_strncmp(previous_country, country, 2) != 0) {
+ wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
+ iface->wait_channel_update = 1;
+ eloop_register_timeout(5, 0,
+ channel_list_update_timeout,
+ iface, NULL);
+ return 0;
+ }
}
+ return setup_interface2(iface);
+}
+
+
+static int setup_interface2(struct hostapd_iface *iface)
+{
+ iface->wait_channel_update = 0;
+
if (hostapd_get_hw_features(iface)) {
/* Not all drivers support this yet, so continue without hw
* feature data. */
@@ -856,48 +1334,117 @@ static int setup_interface(struct hostapd_iface *iface)
if (ret < 0) {
wpa_printf(MSG_ERROR, "Could not select hw_mode and "
"channel. (%d)", ret);
- return -1;
+ goto fail;
+ }
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+ return 0;
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
- return -1;
+ goto fail;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will "
"be completed in a callback");
return 0;
}
+
+ if (iface->conf->ieee80211h)
+ wpa_printf(MSG_DEBUG, "DFS support is enabled");
}
return hostapd_setup_interface_complete(iface, 0);
+
+fail:
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (iface->interfaces && iface->interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
}
+/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
{
struct hostapd_data *hapd = iface->bss[0];
size_t j;
u8 *prev_addr;
+ int delay_apply_cfg = 0;
+ int res_dfs_offload = 0;
- if (err) {
- wpa_printf(MSG_ERROR, "Interface initialization failed");
- eloop_terminate();
- return -1;
- }
+ if (err)
+ goto fail;
wpa_printf(MSG_DEBUG, "Completing interface initialization");
- if (hapd->iconf->channel) {
- iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+ if (iface->conf->channel) {
+#ifdef NEED_AP_MLME
+ int res;
+#endif /* NEED_AP_MLME */
+
+ iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d "
"Frequency: %d MHz",
- hostapd_hw_mode_txt(hapd->iconf->hw_mode),
- hapd->iconf->channel, iface->freq);
+ hostapd_hw_mode_txt(iface->conf->hw_mode),
+ iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+ /* Handle DFS only if it is not offloaded to the driver */
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+ /* Check DFS */
+ res = hostapd_handle_dfs(iface);
+ if (res <= 0) {
+ if (res < 0)
+ goto fail;
+ return res;
+ }
+ } else {
+ /* If DFS is offloaded to the driver */
+ res_dfs_offload = hostapd_handle_dfs_offload(iface);
+ if (res_dfs_offload <= 0) {
+ if (res_dfs_offload < 0)
+ goto fail;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Proceed with AP/channel setup");
+ /*
+ * If this is a DFS channel, move to completing
+ * AP setup.
+ */
+ if (res_dfs_offload == 1)
+ goto dfs_offload;
+ /* Otherwise fall through. */
+ }
+ }
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+ if (iface->mconf != NULL) {
+ wpa_printf(MSG_DEBUG,
+ "%s: Mesh configuration will be applied while joining the mesh network",
+ iface->bss[0]->conf->iface);
+ delay_apply_cfg = 1;
+ }
+#endif /* CONFIG_MESH */
- if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+ if (!delay_apply_cfg &&
+ hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
hapd->iconf->channel,
hapd->iconf->ieee80211n,
- hapd->iconf->secondary_channel)) {
+ hapd->iconf->ieee80211ac,
+ hapd->iconf->secondary_channel,
+ hapd->iconf->vht_oper_chwidth,
+ hapd->iconf->vht_oper_centr_freq_seg0_idx,
+ hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
wpa_printf(MSG_ERROR, "Could not set channel for "
"kernel driver");
- return -1;
+ goto fail;
}
}
@@ -908,7 +1455,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Failed to prepare rates table.");
- return -1;
+ goto fail;
}
}
@@ -916,14 +1463,14 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
"kernel driver");
- return -1;
+ goto fail;
}
if (hapd->iconf->fragm_threshold > -1 &&
hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
"for kernel driver");
- return -1;
+ goto fail;
}
prev_addr = hapd->own_addr;
@@ -932,20 +1479,29 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
hapd = iface->bss[j];
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
- if (hostapd_setup_bss(hapd, j == 0))
- return -1;
+ if (hostapd_setup_bss(hapd, j == 0)) {
+ do {
+ hapd = iface->bss[j];
+ hostapd_bss_deinit_no_free(hapd);
+ hostapd_free_hapd_data(hapd);
+ } while (j-- > 0);
+ goto fail;
+ }
if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
prev_addr = hapd->own_addr;
}
+ hapd = iface->bss[0];
hostapd_tx_queue_params(iface);
ap_list_init(iface);
+ hostapd_set_acl(hapd);
+
if (hostapd_driver_commit(hapd) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
"configuration", __func__);
- return -1;
+ goto fail;
}
/*
@@ -956,16 +1512,41 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
*/
for (j = 0; j < iface->num_bss; j++) {
if (hostapd_init_wps_complete(iface->bss[j]))
- return -1;
+ goto fail;
+ }
+
+ if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+ !res_dfs_offload) {
+ /*
+ * If freq is DFS, and DFS is offloaded to the driver, then wait
+ * for CAC to complete.
+ */
+ wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+ return res_dfs_offload;
}
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+ hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
if (hapd->setup_complete_cb)
hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
iface->bss[0]->conf->iface);
+ if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+ iface->interfaces->terminate_on_error--;
return 0;
+
+fail:
+ wpa_printf(MSG_ERROR, "Interface initialization failed");
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ if (iface->interfaces && iface->interfaces->terminate_on_error)
+ eloop_terminate();
+ return -1;
}
@@ -978,6 +1559,12 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
* and sets driver parameters based on the configuration.
* Flushes old stations, sets the channel, encryption,
* beacons, and WDS links based on the configuration.
+ *
+ * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
+ * or DFS operations, this function returns 0 before such operations have been
+ * completed. The pending operations are registered into eloop and will be
+ * completed from eloop callbacks. Those callbacks end up calling
+ * hostapd_setup_interface_complete() once setup has been completed.
*/
int hostapd_setup_interface(struct hostapd_iface *iface)
{
@@ -1027,68 +1614,327 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
}
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+ wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+ hapd->conf->iface);
+ hostapd_bss_deinit_no_free(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+ hostapd_cleanup(hapd);
+}
+
+
void hostapd_interface_deinit(struct hostapd_iface *iface)
{
- size_t j;
+ int j;
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
if (iface == NULL)
return;
- hostapd_cleanup_iface_pre(iface);
- for (j = 0; j < iface->num_bss; j++) {
- struct hostapd_data *hapd = iface->bss[j];
- hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- hostapd_clear_wep(hapd);
- hostapd_cleanup(hapd);
- }
+ hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+ hostapd_stop_setup_timers(iface);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+ iface->wait_channel_update = 0;
+
+ for (j = iface->num_bss - 1; j >= 0; j--)
+ hostapd_bss_deinit(iface->bss[j]);
}
void hostapd_interface_free(struct hostapd_iface *iface)
{
size_t j;
- for (j = 0; j < iface->num_bss; j++)
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+ for (j = 0; j < iface->num_bss; j++) {
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+ __func__, iface->bss[j]);
os_free(iface->bss[j]);
+ }
hostapd_cleanup_iface(iface);
}
-#ifdef HOSTAPD
+/**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+ * Returns: Pointer to the allocated interface data or %NULL on failure
+ *
+ * This function is used to allocate main data structures for per-interface
+ * data. The allocated data buffer will be freed by calling
+ * hostapd_cleanup_iface().
+ */
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file)
+{
+ struct hostapd_iface *hapd_iface = NULL;
+ struct hostapd_config *conf = NULL;
+ struct hostapd_data *hapd;
+ size_t i;
+
+ hapd_iface = os_zalloc(sizeof(*hapd_iface));
+ if (hapd_iface == NULL)
+ goto fail;
+
+ hapd_iface->config_fname = os_strdup(config_file);
+ if (hapd_iface->config_fname == NULL)
+ goto fail;
+
+ conf = interfaces->config_read_cb(hapd_iface->config_fname);
+ if (conf == NULL)
+ goto fail;
+ hapd_iface->conf = conf;
+
+ hapd_iface->num_bss = conf->num_bss;
+ hapd_iface->bss = os_calloc(conf->num_bss,
+ sizeof(struct hostapd_data *));
+ if (hapd_iface->bss == NULL)
+ goto fail;
+
+ for (i = 0; i < conf->num_bss; i++) {
+ hapd = hapd_iface->bss[i] =
+ hostapd_alloc_bss_data(hapd_iface, conf,
+ conf->bss[i]);
+ if (hapd == NULL)
+ goto fail;
+ hapd->msg_ctx = hapd;
+ }
+
+ return hapd_iface;
+
+fail:
+ wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
+ config_file);
+ if (conf)
+ hostapd_config_free(conf);
+ if (hapd_iface) {
+ os_free(hapd_iface->config_fname);
+ os_free(hapd_iface->bss);
+ wpa_printf(MSG_DEBUG, "%s: free iface %p",
+ __func__, hapd_iface);
+ os_free(hapd_iface);
+ }
+ return NULL;
+}
+
+
+static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname)
+{
+ size_t i, j;
+
+ for (i = 0; i < interfaces->count; i++) {
+ struct hostapd_iface *iface = interfaces->iface[i];
+ for (j = 0; j < iface->num_bss; j++) {
+ struct hostapd_data *hapd = iface->bss[j];
+ if (os_strcmp(ifname, hapd->conf->iface) == 0)
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * hostapd_interface_init_bss - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a BSS. This BSS is
+ * added to an existing interface sharing the same radio (if any) or a new
+ * interface is created if this is the first interface on a radio. This
+ * allocate memory for the BSS. No actual driver operations are started.
+ *
+ * This is similar to hostapd_interface_init(), but for a case where the
+ * configuration is used to add a single BSS instead of all BSSes for a radio.
+ */
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug)
+{
+ struct hostapd_iface *new_iface = NULL, *iface = NULL;
+ struct hostapd_data *hapd;
+ int k;
+ size_t i, bss_idx;
+
+ if (!phy || !*phy)
+ return NULL;
+
+ for (i = 0; i < interfaces->count; i++) {
+ if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+ iface = interfaces->iface[i];
+ break;
+ }
+ }
+
+ wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
+ config_fname, phy, iface ? "" : " --> new PHY");
+ if (iface) {
+ struct hostapd_config *conf;
+ struct hostapd_bss_config **tmp_conf;
+ struct hostapd_data **tmp_bss;
+ struct hostapd_bss_config *bss;
+ const char *ifname;
+
+ /* Add new BSS to existing iface */
+ conf = interfaces->config_read_cb(config_fname);
+ if (conf == NULL)
+ return NULL;
+ if (conf->num_bss > 1) {
+ wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ ifname = conf->bss[0]->iface;
+ if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+ wpa_printf(MSG_ERROR,
+ "Interface name %s already in use", ifname);
+ hostapd_config_free(conf);
+ return NULL;
+ }
+
+ tmp_conf = os_realloc_array(
+ iface->conf->bss, iface->conf->num_bss + 1,
+ sizeof(struct hostapd_bss_config *));
+ tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+ sizeof(struct hostapd_data *));
+ if (tmp_bss)
+ iface->bss = tmp_bss;
+ if (tmp_conf) {
+ iface->conf->bss = tmp_conf;
+ iface->conf->last_bss = tmp_conf[0];
+ }
+ if (tmp_bss == NULL || tmp_conf == NULL) {
+ hostapd_config_free(conf);
+ return NULL;
+ }
+ bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
+ iface->conf->num_bss++;
+
+ hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+ if (hapd == NULL) {
+ iface->conf->num_bss--;
+ hostapd_config_free(conf);
+ return NULL;
+ }
+ iface->conf->last_bss = bss;
+ iface->bss[iface->num_bss] = hapd;
+ hapd->msg_ctx = hapd;
+
+ bss_idx = iface->num_bss++;
+ conf->num_bss--;
+ conf->bss[0] = NULL;
+ hostapd_config_free(conf);
+ } else {
+ /* Add a new iface with the first BSS */
+ new_iface = iface = hostapd_init(interfaces, config_fname);
+ if (!iface)
+ return NULL;
+ os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+ iface->interfaces = interfaces;
+ bss_idx = 0;
+ }
+
+ for (k = 0; k < debug; k++) {
+ if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
+ iface->bss[bss_idx]->conf->logger_stdout_level--;
+ }
+
+ if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
+ !hostapd_drv_none(iface->bss[bss_idx])) {
+ wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+ config_fname);
+ if (new_iface)
+ hostapd_interface_deinit_free(new_iface);
+ return NULL;
+ }
+
+ return iface;
+}
+
void hostapd_interface_deinit_free(struct hostapd_iface *iface)
{
const struct wpa_driver_ops *driver;
void *drv_priv;
+
+ wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
if (iface == NULL)
return;
+ wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
+ __func__, (unsigned int) iface->num_bss,
+ (unsigned int) iface->conf->num_bss);
driver = iface->bss[0]->driver;
drv_priv = iface->bss[0]->drv_priv;
hostapd_interface_deinit(iface);
- if (driver && driver->hapd_deinit && drv_priv)
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+ if (driver && driver->hapd_deinit && drv_priv) {
driver->hapd_deinit(drv_priv);
+ iface->bss[0]->drv_priv = NULL;
+ }
hostapd_interface_free(iface);
}
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+ void *drv_priv,
+ struct hostapd_iface *hapd_iface)
+{
+ size_t j;
+
+ wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+ __func__, driver, drv_priv);
+ if (driver && driver->hapd_deinit && drv_priv) {
+ driver->hapd_deinit(drv_priv);
+ for (j = 0; j < hapd_iface->num_bss; j++) {
+ wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+ __func__, (int) j,
+ hapd_iface->bss[j]->drv_priv);
+ if (hapd_iface->bss[j]->drv_priv == drv_priv)
+ hapd_iface->bss[j]->drv_priv = NULL;
+ }
+ }
+}
+
+
int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
{
+ size_t j;
+
if (hapd_iface->bss[0]->drv_priv != NULL) {
wpa_printf(MSG_ERROR, "Interface %s already enabled",
- hapd_iface->conf->bss[0].iface);
+ hapd_iface->conf->bss[0]->iface);
return -1;
}
wpa_printf(MSG_DEBUG, "Enable interface %s",
- hapd_iface->conf->bss[0].iface);
+ hapd_iface->conf->bss[0]->iface);
+
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+ wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
+ return -1;
+ }
if (hapd_iface->interfaces == NULL ||
hapd_iface->interfaces->driver_init == NULL ||
- hapd_iface->interfaces->driver_init(hapd_iface) ||
- hostapd_setup_interface(hapd_iface)) {
- hostapd_interface_deinit_free(hapd_iface);
+ hapd_iface->interfaces->driver_init(hapd_iface))
+ return -1;
+
+ if (hostapd_setup_interface(hapd_iface)) {
+ hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
return -1;
}
+
return 0;
}
@@ -1098,19 +1944,17 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
size_t j;
wpa_printf(MSG_DEBUG, "Reload interface %s",
- hapd_iface->conf->bss[0].iface);
- for (j = 0; j < hapd_iface->num_bss; j++) {
- hostapd_flush_old_stations(hapd_iface->bss[j],
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-
-#ifndef CONFIG_NO_RADIUS
- /* TODO: update dynamic data based on changed configuration
- * items (e.g., open/close sockets, etc.) */
- radius_client_flush(hapd_iface->bss[j]->radius, 0);
-#endif /* CONFIG_NO_RADIUS */
-
- hostapd_reload_bss(hapd_iface->bss[j]);
+ hapd_iface->conf->bss[0]->iface);
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+ if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+ wpa_printf(MSG_ERROR, "Updated configuration is invalid");
+ return -1;
}
+ hostapd_clear_old(hapd_iface);
+ for (j = 0; j < hapd_iface->num_bss; j++)
+ hostapd_reload_bss(hapd_iface->bss[j]);
+
return 0;
}
@@ -1118,39 +1962,43 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
{
size_t j;
- struct hostapd_bss_config *bss;
const struct wpa_driver_ops *driver;
void *drv_priv;
if (hapd_iface == NULL)
return -1;
- bss = hapd_iface->bss[0]->conf;
+
+ if (hapd_iface->bss[0]->drv_priv == NULL) {
+ wpa_printf(MSG_INFO, "Interface %s already disabled",
+ hapd_iface->conf->bss[0]->iface);
+ return -1;
+ }
+
+ wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
driver = hapd_iface->bss[0]->driver;
drv_priv = hapd_iface->bss[0]->drv_priv;
- /* whatever hostapd_interface_deinit does */
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+ /* same as hostapd_interface_deinit without deinitializing ctrl-iface */
for (j = 0; j < hapd_iface->num_bss; j++) {
struct hostapd_data *hapd = hapd_iface->bss[j];
- hostapd_free_stas(hapd);
- hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
- hostapd_clear_wep(hapd);
+ hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
}
- if (driver && driver->hapd_deinit && drv_priv) {
- driver->hapd_deinit(drv_priv);
- hapd_iface->bss[0]->drv_priv = NULL;
- }
+ hostapd_deinit_driver(driver, drv_priv, hapd_iface);
/* From hostapd_cleanup_iface: These were initialized in
* hostapd_setup_interface and hostapd_setup_interface_complete
*/
hostapd_cleanup_iface_partial(hapd_iface);
- bss->wpa = 0;
- bss->wpa_key_mgmt = -1;
- bss->wpa_pairwise = -1;
- wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+ wpa_printf(MSG_DEBUG, "Interface %s disabled",
+ hapd_iface->bss[0]->conf->iface);
+ hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
return 0;
}
@@ -1201,7 +2049,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
return NULL;
}
- bss = conf->last_bss = conf->bss;
+ bss = conf->last_bss = conf->bss[0];
os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
bss->ctrl_interface = os_strdup(ctrl_iface);
@@ -1218,51 +2066,129 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
}
-static struct hostapd_iface * hostapd_data_alloc(
- struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+ struct hostapd_config *conf)
{
size_t i;
- struct hostapd_iface *hapd_iface =
- interfaces->iface[interfaces->count - 1];
struct hostapd_data *hapd;
- hapd_iface->conf = conf;
- hapd_iface->num_bss = conf->num_bss;
-
- hapd_iface->bss = os_zalloc(conf->num_bss *
+ hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL)
- return NULL;
+ return -1;
for (i = 0; i < conf->num_bss; i++) {
hapd = hapd_iface->bss[i] =
- hostapd_alloc_bss_data(hapd_iface, conf,
- &conf->bss[i]);
- if (hapd == NULL)
- return NULL;
+ hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
+ if (hapd == NULL) {
+ while (i > 0) {
+ i--;
+ os_free(hapd_iface->bss[i]);
+ hapd_iface->bss[i] = NULL;
+ }
+ os_free(hapd_iface->bss);
+ hapd_iface->bss = NULL;
+ return -1;
+ }
hapd->msg_ctx = hapd;
}
- hapd_iface->interfaces = interfaces;
+ hapd_iface->conf = conf;
+ hapd_iface->num_bss = conf->num_bss;
- return hapd_iface;
+ return 0;
}
int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_config *conf = NULL;
- struct hostapd_iface *hapd_iface = NULL;
+ struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+ struct hostapd_data *hapd;
char *ptr;
- size_t i;
+ size_t i, j;
+ const char *conf_file = NULL, *phy_name = NULL;
+
+ if (os_strncmp(buf, "bss_config=", 11) == 0) {
+ char *pos;
+ phy_name = buf + 11;
+ pos = os_strchr(phy_name, ':');
+ if (!pos)
+ return -1;
+ *pos++ = '\0';
+ conf_file = pos;
+ if (!os_strlen(conf_file))
+ return -1;
+
+ hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
+ conf_file, 0);
+ if (!hapd_iface)
+ return -1;
+ for (j = 0; j < interfaces->count; j++) {
+ if (interfaces->iface[j] == hapd_iface)
+ break;
+ }
+ if (j == interfaces->count) {
+ struct hostapd_iface **tmp;
+ tmp = os_realloc_array(interfaces->iface,
+ interfaces->count + 1,
+ sizeof(struct hostapd_iface *));
+ if (!tmp) {
+ hostapd_interface_deinit_free(hapd_iface);
+ return -1;
+ }
+ interfaces->iface = tmp;
+ interfaces->iface[interfaces->count++] = hapd_iface;
+ new_iface = hapd_iface;
+ }
+
+ if (new_iface) {
+ if (interfaces->driver_init(hapd_iface))
+ goto fail;
+
+ if (hostapd_setup_interface(hapd_iface)) {
+ hostapd_deinit_driver(
+ hapd_iface->bss[0]->driver,
+ hapd_iface->bss[0]->drv_priv,
+ hapd_iface);
+ goto fail;
+ }
+ } else {
+ /* Assign new BSS with bss[0]'s driver info */
+ hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
+ hapd->driver = hapd_iface->bss[0]->driver;
+ hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
+ os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
+ ETH_ALEN);
+
+ if (start_ctrl_iface_bss(hapd) < 0 ||
+ (hapd_iface->state == HAPD_IFACE_ENABLED &&
+ hostapd_setup_bss(hapd, -1))) {
+ hostapd_cleanup(hapd);
+ hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+ hapd_iface->conf->num_bss--;
+ hapd_iface->num_bss--;
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
+ __func__, hapd, hapd->conf->iface);
+ hostapd_config_free_bss(hapd->conf);
+ hapd->conf = NULL;
+ os_free(hapd);
+ return -1;
+ }
+ }
+ return 0;
+ }
ptr = os_strchr(buf, ' ');
if (ptr == NULL)
return -1;
*ptr++ = '\0';
+ if (os_strncmp(ptr, "config=", 7) == 0)
+ conf_file = ptr + 7;
+
for (i = 0; i < interfaces->count; i++) {
- if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface,
+ if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
buf)) {
wpa_printf(MSG_INFO, "Cannot add interface - it "
"already exists");
@@ -1276,29 +2202,33 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
"for interface", __func__);
goto fail;
}
+ new_iface = hapd_iface;
- conf = hostapd_config_alloc(interfaces, buf, ptr);
- if (conf == NULL) {
+ if (conf_file && interfaces->config_read_cb) {
+ conf = interfaces->config_read_cb(conf_file);
+ if (conf && conf->bss)
+ os_strlcpy(conf->bss[0]->iface, buf,
+ sizeof(conf->bss[0]->iface));
+ } else
+ conf = hostapd_config_alloc(interfaces, buf, ptr);
+ if (conf == NULL || conf->bss == NULL) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for configuration", __func__);
goto fail;
}
- hapd_iface = hostapd_data_alloc(interfaces, conf);
- if (hapd_iface == NULL) {
+ if (hostapd_data_alloc(hapd_iface, conf) < 0) {
wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
"for hostapd", __func__);
goto fail;
}
+ conf = NULL;
- if (hapd_iface->interfaces &&
- hapd_iface->interfaces->ctrl_iface_init &&
- hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
- wpa_printf(MSG_ERROR, "%s: Failed to setup control "
- "interface", __func__);
+ if (start_ctrl_iface(hapd_iface) < 0)
goto fail;
- }
- wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+ wpa_printf(MSG_INFO, "Add interface '%s'",
+ hapd_iface->conf->bss[0]->iface);
return 0;
@@ -1306,24 +2236,84 @@ fail:
if (conf)
hostapd_config_free(conf);
if (hapd_iface) {
- os_free(hapd_iface->bss[interfaces->count]);
- os_free(hapd_iface);
+ if (hapd_iface->bss) {
+ for (i = 0; i < hapd_iface->num_bss; i++) {
+ hapd = hapd_iface->bss[i];
+ if (!hapd)
+ continue;
+ if (hapd_iface->interfaces &&
+ hapd_iface->interfaces->ctrl_iface_deinit)
+ hapd_iface->interfaces->
+ ctrl_iface_deinit(hapd);
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+ __func__, hapd_iface->bss[i],
+ hapd->conf->iface);
+ hostapd_cleanup(hapd);
+ os_free(hapd);
+ hapd_iface->bss[i] = NULL;
+ }
+ os_free(hapd_iface->bss);
+ hapd_iface->bss = NULL;
+ }
+ if (new_iface) {
+ interfaces->count--;
+ interfaces->iface[interfaces->count] = NULL;
+ }
+ hostapd_cleanup_iface(hapd_iface);
}
return -1;
}
+static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
+{
+ size_t i;
+
+ wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
+
+ /* Remove hostapd_data only if it has already been initialized */
+ if (idx < iface->num_bss) {
+ struct hostapd_data *hapd = iface->bss[idx];
+
+ hostapd_bss_deinit(hapd);
+ wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+ __func__, hapd, hapd->conf->iface);
+ hostapd_config_free_bss(hapd->conf);
+ hapd->conf = NULL;
+ os_free(hapd);
+
+ iface->num_bss--;
+
+ for (i = idx; i < iface->num_bss; i++)
+ iface->bss[i] = iface->bss[i + 1];
+ } else {
+ hostapd_config_free_bss(iface->conf->bss[idx]);
+ iface->conf->bss[idx] = NULL;
+ }
+
+ iface->conf->num_bss--;
+ for (i = idx; i < iface->conf->num_bss; i++)
+ iface->conf->bss[i] = iface->conf->bss[i + 1];
+
+ return 0;
+}
+
+
int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
{
struct hostapd_iface *hapd_iface;
- size_t i, k = 0;
+ size_t i, j, k = 0;
for (i = 0; i < interfaces->count; i++) {
hapd_iface = interfaces->iface[i];
if (hapd_iface == NULL)
return -1;
- if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) {
+ if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+ hapd_iface->driver_ap_teardown =
+ !!(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
hostapd_interface_deinit_free(hapd_iface);
k = i;
while (k < (interfaces->count - 1)) {
@@ -1334,12 +2324,19 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
interfaces->count--;
return 0;
}
+
+ for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+ if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+ hapd_iface->driver_ap_teardown =
+ !(hapd_iface->drv_flags &
+ WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+ return hostapd_remove_bss(hapd_iface, j);
+ }
+ }
}
return -1;
}
-#endif /* HOSTAPD */
-
/**
* hostapd_new_assoc_sta - Notify that a new station associated with the AP
@@ -1379,8 +2376,9 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
/* Start accounting here, if IEEE 802.1X and WPA are not used.
* IEEE 802.1X/WPA code will start accounting after the station has
* been authorized. */
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
- os_get_time(&sta->connected_time);
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+ ap_sta_set_authorized(hapd, sta, 1);
+ os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
@@ -1393,11 +2391,335 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
} else
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
- wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - ap_max_inactivity)",
- __func__, MAC2STR(sta->addr),
- hapd->conf->ap_max_inactivity);
- eloop_cancel_timeout(ap_handle_timer, hapd, sta);
- eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
- ap_handle_timer, hapd, sta);
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ __func__, MAC2STR(sta->addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
+ }
+}
+
+
+const char * hostapd_state_text(enum hostapd_iface_state s)
+{
+ switch (s) {
+ case HAPD_IFACE_UNINITIALIZED:
+ return "UNINITIALIZED";
+ case HAPD_IFACE_DISABLED:
+ return "DISABLED";
+ case HAPD_IFACE_COUNTRY_UPDATE:
+ return "COUNTRY_UPDATE";
+ case HAPD_IFACE_ACS:
+ return "ACS";
+ case HAPD_IFACE_HT_SCAN:
+ return "HT_SCAN";
+ case HAPD_IFACE_DFS:
+ return "DFS";
+ case HAPD_IFACE_ENABLED:
+ return "ENABLED";
+ }
+
+ return "UNKNOWN";
+}
+
+
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+{
+ wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+ iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
+ hostapd_state_text(s));
+ iface->state = s;
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void free_beacon_data(struct beacon_data *beacon)
+{
+ os_free(beacon->head);
+ beacon->head = NULL;
+ os_free(beacon->tail);
+ beacon->tail = NULL;
+ os_free(beacon->probe_resp);
+ beacon->probe_resp = NULL;
+ os_free(beacon->beacon_ies);
+ beacon->beacon_ies = NULL;
+ os_free(beacon->proberesp_ies);
+ beacon->proberesp_ies = NULL;
+ os_free(beacon->assocresp_ies);
+ beacon->assocresp_ies = NULL;
}
+
+
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
+ struct beacon_data *beacon)
+{
+ struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+ struct wpa_driver_ap_params params;
+ int ret;
+
+ os_memset(beacon, 0, sizeof(*beacon));
+ ret = ieee802_11_build_ap_params(hapd, &params);
+ if (ret < 0)
+ return ret;
+
+ ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
+ &proberesp_extra,
+ &assocresp_extra);
+ if (ret)
+ goto free_ap_params;
+
+ ret = -1;
+ beacon->head = os_malloc(params.head_len);
+ if (!beacon->head)
+ goto free_ap_extra_ies;
+
+ os_memcpy(beacon->head, params.head, params.head_len);
+ beacon->head_len = params.head_len;
+
+ beacon->tail = os_malloc(params.tail_len);
+ if (!beacon->tail)
+ goto free_beacon;
+
+ os_memcpy(beacon->tail, params.tail, params.tail_len);
+ beacon->tail_len = params.tail_len;
+
+ if (params.proberesp != NULL) {
+ beacon->probe_resp = os_malloc(params.proberesp_len);
+ if (!beacon->probe_resp)
+ goto free_beacon;
+
+ os_memcpy(beacon->probe_resp, params.proberesp,
+ params.proberesp_len);
+ beacon->probe_resp_len = params.proberesp_len;
+ }
+
+ /* copy the extra ies */
+ if (beacon_extra) {
+ beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+ if (!beacon->beacon_ies)
+ goto free_beacon;
+
+ os_memcpy(beacon->beacon_ies,
+ beacon_extra->buf, wpabuf_len(beacon_extra));
+ beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+ }
+
+ if (proberesp_extra) {
+ beacon->proberesp_ies =
+ os_malloc(wpabuf_len(proberesp_extra));
+ if (!beacon->proberesp_ies)
+ goto free_beacon;
+
+ os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
+ wpabuf_len(proberesp_extra));
+ beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+ }
+
+ if (assocresp_extra) {
+ beacon->assocresp_ies =
+ os_malloc(wpabuf_len(assocresp_extra));
+ if (!beacon->assocresp_ies)
+ goto free_beacon;
+
+ os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
+ wpabuf_len(assocresp_extra));
+ beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+ }
+
+ ret = 0;
+free_beacon:
+ /* if the function fails, the caller should not free beacon data */
+ if (ret)
+ free_beacon_data(beacon);
+
+free_ap_extra_ies:
+ hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
+ assocresp_extra);
+free_ap_params:
+ ieee802_11_free_ap_params(&params);
+ return ret;
+}
+
+
+/*
+ * TODO: This flow currently supports only changing frequency within the
+ * same hw_mode. Any other changes to MAC parameters or provided settings (even
+ * width) are not supported.
+ */
+static int hostapd_change_config_freq(struct hostapd_data *hapd,
+ struct hostapd_config *conf,
+ struct hostapd_freq_params *params,
+ struct hostapd_freq_params *old_params)
+{
+ int channel;
+
+ if (!params->channel) {
+ /* check if the new channel is supported by hw */
+ params->channel = hostapd_hw_get_channel(hapd, params->freq);
+ }
+
+ channel = params->channel;
+ if (!channel)
+ return -1;
+
+ /* if a pointer to old_params is provided we save previous state */
+ if (old_params) {
+ old_params->channel = conf->channel;
+ old_params->ht_enabled = conf->ieee80211n;
+ old_params->sec_channel_offset = conf->secondary_channel;
+ }
+
+ conf->channel = channel;
+ conf->ieee80211n = params->ht_enabled;
+ conf->secondary_channel = params->sec_channel_offset;
+
+ /* TODO: maybe call here hostapd_config_check here? */
+
+ return 0;
+}
+
+
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct hostapd_freq_params old_freq;
+ int ret;
+
+ os_memset(&old_freq, 0, sizeof(old_freq));
+ if (!iface || !iface->freq || hapd->csa_in_progress)
+ return -1;
+
+ ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &settings->freq_params,
+ &old_freq);
+ if (ret)
+ return ret;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+
+ /* change back the configuration */
+ hostapd_change_config_freq(iface->bss[0], iface->conf,
+ &old_freq, NULL);
+
+ if (ret)
+ return ret;
+
+ /* set channel switch parameters for csa ie */
+ hapd->cs_freq_params = settings->freq_params;
+ hapd->cs_count = settings->cs_count;
+ hapd->cs_block_tx = settings->block_tx;
+
+ ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+ if (ret) {
+ free_beacon_data(&settings->beacon_after);
+ return ret;
+ }
+
+ settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+ settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+
+ return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+ os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+ hapd->cs_count = 0;
+ hapd->cs_block_tx = 0;
+ hapd->cs_c_off_beacon = 0;
+ hapd->cs_c_off_proberesp = 0;
+ hapd->csa_in_progress = 0;
+}
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings)
+{
+ int ret;
+
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+ wpa_printf(MSG_INFO, "CSA is not supported");
+ return -1;
+ }
+
+ ret = hostapd_fill_csa_settings(hapd, settings);
+ if (ret)
+ return ret;
+
+ ret = hostapd_drv_switch_channel(hapd, settings);
+ free_beacon_data(&settings->beacon_csa);
+ free_beacon_data(&settings->beacon_after);
+
+ if (ret) {
+ /* if we failed, clean cs parameters */
+ hostapd_cleanup_cs_params(hapd);
+ return ret;
+ }
+
+ hapd->csa_in_progress = 1;
+ return 0;
+}
+
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params)
+{
+ int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+ unsigned int i;
+
+ wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+ if (freq_params->center_freq1)
+ vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+ if (freq_params->center_freq2)
+ vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+ switch (freq_params->bandwidth) {
+ case 0:
+ case 20:
+ case 40:
+ vht_bw = VHT_CHANWIDTH_USE_HT;
+ break;
+ case 80:
+ if (freq_params->center_freq2)
+ vht_bw = VHT_CHANWIDTH_80P80MHZ;
+ else
+ vht_bw = VHT_CHANWIDTH_80MHZ;
+ break;
+ case 160:
+ vht_bw = VHT_CHANWIDTH_160MHZ;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+ freq_params->bandwidth);
+ break;
+ }
+
+ iface->freq = freq_params->freq;
+ iface->conf->channel = freq_params->channel;
+ iface->conf->secondary_channel = freq_params->sec_channel_offset;
+ iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
+ iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
+ iface->conf->vht_oper_chwidth = vht_bw;
+ iface->conf->ieee80211n = freq_params->ht_enabled;
+ iface->conf->ieee80211ac = freq_params->vht_enabled;
+
+ /*
+ * cs_params must not be cleared earlier because the freq_params
+ * argument may actually point to one of these.
+ */
+ for (i = 0; i < iface->num_bss; i++)
+ hostapd_cleanup_cs_params(iface->bss[i]);
+
+ hostapd_disable_iface(iface);
+ hostapd_enable_iface(iface);
+}
+
+#endif /* NEED_AP_MLME */
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index f1e7d9ff7ec5a..75cc24edb665b 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,19 +10,22 @@
#define HOSTAPD_H
#include "common/defs.h"
+#include "utils/list.h"
#include "ap_config.h"
+#include "drivers/driver.h"
-struct wpa_driver_ops;
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
struct hostapd_data;
struct sta_info;
-struct hostap_sta_driver_data;
struct ieee80211_ht_capabilities;
struct full_dynamic_vlan;
enum wps_event;
union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
struct hostapd_iface;
@@ -40,9 +43,19 @@ struct hapd_interfaces {
int global_ctrl_sock;
char *global_iface_path;
char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
+ gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
struct hostapd_iface **iface;
+
+ size_t terminate_on_error;
};
+enum hostapd_chan_status {
+ HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+ HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+ HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+};
struct hostapd_probereq_cb {
int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
@@ -63,6 +76,25 @@ struct hostapd_frame_info {
int ssi_signal; /* dBm */
};
+enum wps_status {
+ WPS_STATUS_SUCCESS = 1,
+ WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+ WPS_PBC_STATUS_DISABLE,
+ WPS_PBC_STATUS_ACTIVE,
+ WPS_PBC_STATUS_TIMEOUT,
+ WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+ enum wps_status status;
+ enum wps_error_indication failure_reason;
+ enum pbc_status pbc_status;
+ u8 peer_addr[ETH_ALEN];
+};
+
/**
* struct hostapd_data - hostapd per-BSS data structure
@@ -72,6 +104,9 @@ struct hostapd_data {
struct hostapd_config *iconf;
struct hostapd_bss_config *conf;
int interface_added; /* virtual interface added for this BSS */
+ unsigned int started:1;
+ unsigned int disabled:1;
+ unsigned int reenable_beacon:1;
u8 own_addr[ETH_ALEN];
@@ -111,7 +146,7 @@ struct hostapd_data {
struct eapol_authenticator *eapol_auth;
struct rsn_preauth_interface *preauth_iface;
- time_t michael_mic_failure;
+ struct os_reltime michael_mic_failure;
int michael_mic_failures;
int tkip_countermeasures;
@@ -121,6 +156,7 @@ struct hostapd_data {
void *ssl_ctx;
void *eap_sim_db_priv;
struct radius_server_data *radius_srv;
+ struct dl_list erp_keys; /* struct eap_server_erp_key */
int parameter_set_count;
@@ -143,6 +179,8 @@ struct hostapd_data {
unsigned int ap_pin_failures_consecutive;
struct upnp_wps_device_sm *wps_upnp;
unsigned int ap_pin_lockout_time;
+
+ struct wps_stat wps_stats;
#endif /* CONFIG_WPS */
struct hostapd_probereq_cb *probereq_cb;
@@ -151,6 +189,9 @@ struct hostapd_data {
void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
int freq);
void *public_action_cb_ctx;
+ void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
+ int freq);
+ void *public_action_cb2_ctx;
int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
int freq);
@@ -171,6 +212,22 @@ struct hostapd_data {
void (*setup_complete_cb)(void *ctx);
void *setup_complete_cb_ctx;
+ void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
+ size_t psk_len);
+ void *new_psk_cb_ctx;
+
+ /* channel switch parameters */
+ struct hostapd_freq_params cs_freq_params;
+ u8 cs_count;
+ int cs_block_tx;
+ unsigned int cs_c_off_beacon;
+ unsigned int cs_c_off_proberesp;
+ int csa_in_progress;
+
+ /* BSS Load */
+ unsigned int bss_load_update_timeout;
+
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
@@ -188,10 +245,34 @@ struct hostapd_data {
#ifdef CONFIG_INTERWORKING
size_t gas_frag_limit;
#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+ struct l2_packet_data *sock_dhcp;
+ struct l2_packet_data *sock_ndisc;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+ int num_plinks;
+ int max_plinks;
+ void (*mesh_sta_free_cb)(struct sta_info *sta);
+ struct wpabuf *mesh_pending_auth;
+ struct os_reltime mesh_pending_auth_time;
+#endif /* CONFIG_MESH */
#ifdef CONFIG_SQLITE
struct hostapd_eap_user tmp_eap_user;
#endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_SAE
+ /** Key used for generating SAE anti-clogging tokens */
+ u8 sae_token_key[8];
+ struct os_reltime last_sae_token_key_update;
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ unsigned int ext_mgmt_frame_handling:1;
+ unsigned int ext_eapol_frame_io:1;
+
+ struct l2_packet_data *l2_test;
+#endif /* CONFIG_TESTING_OPTIONS */
};
@@ -203,16 +284,42 @@ struct hostapd_iface {
void *owner;
char *config_fname;
struct hostapd_config *conf;
+ char phy[16]; /* Name of the PHY (radio) */
+
+ enum hostapd_iface_state {
+ HAPD_IFACE_UNINITIALIZED,
+ HAPD_IFACE_DISABLED,
+ HAPD_IFACE_COUNTRY_UPDATE,
+ HAPD_IFACE_ACS,
+ HAPD_IFACE_HT_SCAN,
+ HAPD_IFACE_DFS,
+ HAPD_IFACE_ENABLED
+ } state;
+
+#ifdef CONFIG_MESH
+ struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
size_t num_bss;
struct hostapd_data **bss;
+ unsigned int wait_channel_update:1;
+ unsigned int cac_started:1;
+
+ /*
+ * When set, indicates that the driver will handle the AP
+ * teardown: delete global keys, station keys, and stations.
+ */
+ unsigned int driver_ap_teardown:1;
+
int num_ap; /* number of entries in ap_list */
struct ap_info *ap_list; /* AP info list head */
struct ap_info *ap_hash[STA_HASH_SIZE];
- struct ap_info *ap_iter_list;
- unsigned int drv_flags;
+ u64 drv_flags;
+
+ /* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
+ unsigned int smps_modes;
/*
* A bitmap of supported protocols for probe response offload. See
@@ -220,6 +327,12 @@ struct hostapd_iface {
*/
unsigned int probe_resp_offloads;
+ /* extended capabilities supported by the driver */
+ const u8 *extended_capa, *extended_capa_mask;
+ unsigned int extended_capa_len;
+
+ unsigned int drv_max_acl_mac_addrs;
+
struct hostapd_hw_modes *hw_features;
int num_hw_features;
struct hostapd_hw_modes *current_mode;
@@ -253,11 +366,40 @@ struct hostapd_iface {
/* Number of HT associated stations 20 MHz */
int num_sta_ht_20mhz;
+ /* Number of HT40 intolerant stations */
+ int num_sta_ht40_intolerant;
+
/* Overlapping BSS information */
int olbc_ht;
u16 ht_op_mode;
+
+ /* surveying helpers */
+
+ /* number of channels surveyed */
+ unsigned int chans_surveyed;
+
+ /* lowest observed noise floor in dBm */
+ s8 lowest_nf;
+
+ /* channel utilization calculation */
+ u64 last_channel_time;
+ u64 last_channel_time_busy;
+ u8 channel_utilization;
+
+ unsigned int dfs_cac_ms;
+ struct os_reltime dfs_cac_start;
+
+ /* Latched with the actual secondary channel information and will be
+ * used while juggling between HT20 and HT40 modes. */
+ int secondary_ch;
+
+#ifdef CONFIG_ACS
+ unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
void (*scan_cb)(struct hostapd_iface *iface);
+ int num_ht40_scan_tries;
};
/* hostapd.c */
@@ -273,6 +415,11 @@ int hostapd_setup_interface(struct hostapd_iface *iface);
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
void hostapd_interface_deinit(struct hostapd_iface *iface);
void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+ const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+ const char *config_fname, int debug);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -281,6 +428,15 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+ struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+ const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -296,11 +452,13 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
+void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
+ const u8 *addr, int reason_code);
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal);
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
- int offset);
+ int offset, int width, int cf1, int cf2);
const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 45d518bc1e552..d7909fad4a144 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -1,7 +1,7 @@
/*
* Hotspot 2.0 AP ANQP processing
* Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,19 +13,165 @@
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ap_config.h"
+#include "ap_drv_ops.h"
#include "hs20.h"
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
{
+ u8 conf;
if (!hapd->conf->hs20)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
- *eid++ = 5;
+ *eid++ = 7;
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_INDICATION_OUI_TYPE;
- /* Hotspot Configuration: DGAF Enabled */
- *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+ conf = HS20_VERSION; /* Release Number */
+ 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;
+
return eid;
}
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ if (!hapd->conf->osen)
+ return eid;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (hapd->conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url)
+{
+ struct wpabuf *buf;
+ size_t len = 0;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ if (url) {
+ len = 1 + os_strlen(url);
+ if (5 + len > 255) {
+ wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+ "WNM-Notification: '%s'", url);
+ return -1;
+ }
+ }
+
+ buf = wpabuf_alloc(4 + 7 + len);
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Subscription Remediation subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5 + len);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+ if (url) {
+ wpabuf_put_u8(buf, len - 1);
+ wpabuf_put_data(buf, url, len - 1);
+ wpabuf_put_u8(buf, osu_method);
+ } else {
+ /* Server URL and Server Method fields not included */
+ wpabuf_put_u8(buf, 0);
+ }
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload)
+{
+ struct wpabuf *buf;
+ int ret;
+
+ /* TODO: should refuse to send notification if the STA is not associated
+ * or if the STA did not indicate support for WNM-Notification */
+
+ buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+ if (buf == NULL)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+ wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+ wpabuf_put_u8(buf, 1); /* Dialog token */
+ wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+ /* Deauthentication Imminent Notice subelement */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+ wpabuf_put_buf(buf, payload);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+
+ wpabuf_free(buf);
+
+ return ret;
+}
diff --git a/src/ap/hs20.h b/src/ap/hs20.h
index 98698ce2fd667..152439f4dcb45 100644
--- a/src/ap/hs20.h
+++ b/src/ap/hs20.h
@@ -1,6 +1,6 @@
/*
* Hotspot 2.0 AP ANQP processing
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -12,5 +12,11 @@
struct hostapd_data;
u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+ u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+ const u8 *addr,
+ const struct wpabuf *payload);
#endif /* HS20_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 923b698230701..05431d32c205d 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -20,10 +14,14 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
#include "hw_features.h"
@@ -44,10 +42,40 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
}
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+ static char info[256];
+ char *state;
+
+ switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+ case HOSTAPD_CHAN_DFS_UNKNOWN:
+ state = "unknown";
+ break;
+ case HOSTAPD_CHAN_DFS_USABLE:
+ state = "usable";
+ break;
+ case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+ state = "unavailable";
+ break;
+ case HOSTAPD_CHAN_DFS_AVAILABLE:
+ state = "available";
+ break;
+ default:
+ return "";
+ }
+ os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+ info[sizeof(info) - 1] = '\0';
+
+ return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
int hostapd_get_hw_features(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
- int ret = 0, i, j;
+ int i, j;
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
@@ -70,34 +98,47 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *feature = &modes[i];
+ int dfs_enabled = hapd->iconf->ieee80211h &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
/* set flag for channels we can use in current regulatory
* domain */
for (j = 0; j < feature->num_channels; j++) {
+ int dfs = 0;
+
/*
* Disable all channels that are marked not to allow
- * IBSS operation or active scanning. In addition,
- * disable all channels that require radar detection,
- * since that (in addition to full DFS) is not yet
- * supported.
+ * to initiate radiation (a.k.a. passive scan and no
+ * IBSS).
+ * Use radar channels only if the driver supports DFS.
*/
- if (feature->channels[j].flag &
- (HOSTAPD_CHAN_NO_IBSS |
- HOSTAPD_CHAN_PASSIVE_SCAN |
- HOSTAPD_CHAN_RADAR))
+ if ((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+ dfs = 1;
+ } else if (((feature->channels[j].flag &
+ HOSTAPD_CHAN_RADAR) &&
+ !(iface->drv_flags &
+ WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+ (feature->channels[j].flag &
+ HOSTAPD_CHAN_NO_IR)) {
feature->channels[j].flag |=
HOSTAPD_CHAN_DISABLED;
+ }
+
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
+
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
- "chan=%d freq=%d MHz max_tx_power=%d dBm",
+ "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
feature->mode,
feature->channels[j].chan,
feature->channels[j].freq,
- feature->channels[j].max_tx_power);
+ feature->channels[j].max_tx_power,
+ dfs ? dfs_info(&feature->channels[j]) : "");
}
}
- return ret;
+ return 0;
}
@@ -183,66 +224,16 @@ int hostapd_prepare_rates(struct hostapd_iface *iface,
#ifdef CONFIG_IEEE80211N
static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
{
- int sec_chan, ok, j, first;
- int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
- 184, 192 };
- size_t k;
+ int pri_chan, sec_chan;
if (!iface->conf->secondary_channel)
return 1; /* HT40 not used */
- sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
- wpa_printf(MSG_DEBUG, "HT40: control channel: %d "
- "secondary channel: %d",
- iface->conf->channel, sec_chan);
-
- /* Verify that HT40 secondary channel is an allowed 20 MHz
- * channel */
- ok = 0;
- for (j = 0; j < iface->current_mode->num_channels; j++) {
- struct hostapd_channel_data *chan =
- &iface->current_mode->channels[j];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- chan->chan == sec_chan) {
- ok = 1;
- break;
- }
- }
- if (!ok) {
- wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
- sec_chan);
- return 0;
- }
-
- /*
- * Verify that HT40 primary,secondary channel pair is allowed per
- * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
- * 2.4 GHz rules allow all cases where the secondary channel fits into
- * the list of allowed channels (already checked above).
- */
- if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
- return 1;
-
- if (iface->conf->secondary_channel > 0)
- first = iface->conf->channel;
- else
- first = sec_chan;
-
- ok = 0;
- for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
- if (first == allowed[k]) {
- ok = 1;
- break;
- }
- }
- if (!ok) {
- wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
- iface->conf->channel,
- iface->conf->secondary_channel);
- return 0;
- }
+ pri_chan = iface->conf->channel;
+ sec_chan = pri_chan + iface->conf->secondary_channel * 4;
- return 1;
+ return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
+ sec_chan);
}
@@ -258,158 +249,34 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
}
-static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
- int *pri_chan, int *sec_chan)
-{
- struct ieee80211_ht_operation *oper;
- struct ieee802_11_elems elems;
-
- *pri_chan = *sec_chan = 0;
-
- ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
- if (elems.ht_operation &&
- elems.ht_operation_len >= sizeof(*oper)) {
- oper = (struct ieee80211_ht_operation *) elems.ht_operation;
- *pri_chan = oper->control_chan;
- if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
- int sec = oper->ht_param &
- HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
- if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
- *sec_chan = *pri_chan + 4;
- else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
- *sec_chan = *pri_chan - 4;
- }
- }
-}
-
-
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
- int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
- int bss_pri_chan, bss_sec_chan;
- size_t i;
- int match;
+ int pri_chan, sec_chan;
+ int res;
pri_chan = iface->conf->channel;
- sec_chan = iface->conf->secondary_channel * 4;
- pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
- if (iface->conf->secondary_channel > 0)
- sec_freq = pri_freq + 20;
- else
- sec_freq = pri_freq - 20;
+ sec_chan = pri_chan + iface->conf->secondary_channel * 4;
- /*
- * Switch PRI/SEC channels if Beacons were detected on selected SEC
- * channel, but not on selected PRI channel.
- */
- pri_bss = sec_bss = 0;
- for (i = 0; i < scan_res->num; i++) {
- struct wpa_scan_res *bss = scan_res->res[i];
- if (bss->freq == pri_freq)
- pri_bss++;
- else if (bss->freq == sec_freq)
- sec_bss++;
- }
- if (sec_bss && !pri_bss) {
- wpa_printf(MSG_INFO, "Switch own primary and secondary "
- "channel to get secondary channel with no Beacons "
- "from other BSSes");
- ieee80211n_switch_pri_sec(iface);
- }
+ res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
- /*
- * Match PRI/SEC channel with any existing HT40 BSS on the same
- * channels that we are about to use (if already mixed order in
- * existing BSSes, use own preference).
- */
- match = 0;
- for (i = 0; i < scan_res->num; i++) {
- struct wpa_scan_res *bss = scan_res->res[i];
- ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
- if (pri_chan == bss_pri_chan &&
- sec_chan == bss_sec_chan) {
- match = 1;
- break;
- }
- }
- if (!match) {
- for (i = 0; i < scan_res->num; i++) {
- struct wpa_scan_res *bss = scan_res->res[i];
- ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
- &bss_sec_chan);
- if (pri_chan == bss_sec_chan &&
- sec_chan == bss_pri_chan) {
- wpa_printf(MSG_INFO, "Switch own primary and "
- "secondary channel due to BSS "
- "overlap with " MACSTR,
- MAC2STR(bss->bssid));
- ieee80211n_switch_pri_sec(iface);
- break;
- }
- }
- }
+ if (res == 2)
+ ieee80211n_switch_pri_sec(iface);
- return 1;
+ return !!res;
}
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
- int pri_freq, sec_freq;
- int affected_start, affected_end;
- size_t i;
+ int pri_chan, sec_chan;
- pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
- if (iface->conf->secondary_channel > 0)
- sec_freq = pri_freq + 20;
- else
- sec_freq = pri_freq - 20;
- affected_start = (pri_freq + sec_freq) / 2 - 25;
- affected_end = (pri_freq + sec_freq) / 2 + 25;
- wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
- affected_start, affected_end);
- for (i = 0; i < scan_res->num; i++) {
- struct wpa_scan_res *bss = scan_res->res[i];
- int pri = bss->freq;
- int sec = pri;
- int sec_chan, pri_chan;
-
- ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
- if (sec_chan) {
- if (sec_chan < pri_chan)
- sec = pri - 20;
- else
- sec = pri + 20;
- }
-
- if ((pri < affected_start || pri > affected_end) &&
- (sec < affected_start || sec > affected_end))
- continue; /* not within affected channel range */
-
- wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
- " freq=%d pri=%d sec=%d",
- MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
- if (sec_chan) {
- if (pri_freq != pri || sec_freq != sec) {
- wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
- "mismatch with BSS " MACSTR
- " <%d,%d> (chan=%d%c) vs. <%d,%d>",
- MAC2STR(bss->bssid),
- pri, sec, pri_chan,
- sec > pri ? '+' : '-',
- pri_freq, sec_freq);
- return 0;
- }
- }
-
- /* TODO: 40 MHz intolerant */
- }
+ pri_chan = iface->conf->channel;
+ sec_chan = pri_chan + iface->conf->secondary_channel * 4;
- return 1;
+ return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+ sec_chan);
}
@@ -436,6 +303,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
wpa_scan_results_free(scan_res);
+ iface->secondary_ch = iface->conf->secondary_channel;
if (!oper40) {
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
"channel pri=%d sec=%d based on overlapping BSSes",
@@ -443,10 +311,21 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
iface->conf->channel +
iface->conf->secondary_channel * 4);
iface->conf->secondary_channel = 0;
- iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+ /*
+ * TODO: Could consider scheduling another scan to check
+ * if channel width can be changed if no coex reports
+ * are received from associating stations.
+ */
+ }
}
res = ieee80211n_allowed_ht40_channel_pair(iface);
+ if (!res) {
+ iface->conf->secondary_channel = 0;
+ wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+ }
+
hostapd_setup_interface_complete(iface, !res);
}
@@ -491,25 +370,128 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
}
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+ struct wpa_driver_scan_params *params)
+{
+ /* Scan only the affected frequency range */
+ int pri_freq;
+ int affected_start, affected_end;
+ int i, pos;
+ struct hostapd_hw_modes *mode;
+
+ if (iface->current_mode == NULL)
+ return;
+
+ pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+ if (iface->conf->secondary_channel > 0) {
+ affected_start = pri_freq - 10;
+ affected_end = pri_freq + 30;
+ } else {
+ affected_start = pri_freq - 30;
+ affected_end = pri_freq + 10;
+ }
+ wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+
+ mode = iface->current_mode;
+ params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+ if (params->freqs == NULL)
+ return;
+ pos = 0;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (chan->freq < affected_start ||
+ chan->freq > affected_end)
+ continue;
+ params->freqs[pos++] = chan->freq;
+ }
+}
+
+
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+ struct hostapd_iface *iface = eloop_data;
+ struct wpa_driver_scan_params params;
+ int ret;
+
+ os_memset(&params, 0, sizeof(params));
+ if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+ ieee80211n_scan_channels_2g4(iface, &params);
+ else
+ ieee80211n_scan_channels_5g(iface, &params);
+
+ ret = hostapd_driver_scan(iface->bss[0], &params);
+ iface->num_ht40_scan_tries++;
+ os_free(params.freqs);
+
+ if (ret == -EBUSY &&
+ iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+ ret, strerror(-ret), iface->num_ht40_scan_tries);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return;
+ }
+
+ if (ret == 0) {
+ iface->scan_cb = ieee80211n_check_scan;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "Failed to request a scan in device, bringing up in HT20 mode");
+ iface->conf->secondary_channel = 0;
+ iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+ hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
+ int ret;
if (!iface->conf->secondary_channel)
return 0; /* HT40 not used */
+ hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
"40 MHz channel");
os_memset(&params, 0, sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, &params);
- if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
- wpa_printf(MSG_ERROR, "Failed to request a scan of "
- "neighboring BSSes");
- os_free(params.freqs);
+ else
+ ieee80211n_scan_channels_5g(iface, &params);
+
+ ret = hostapd_driver_scan(iface->bss[0], &params);
+ os_free(params.freqs);
+
+ if (ret == -EBUSY) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+ ret, strerror(-ret));
+ iface->num_ht40_scan_tries = 1;
+ eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+ eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+ return 1;
+ }
+
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+ ret, strerror(-ret));
return -1;
}
- os_free(params.freqs);
iface->scan_cb = ieee80211n_check_scan;
return 1;
@@ -535,11 +517,24 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
return 0;
}
- if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
- (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
- wpa_printf(MSG_ERROR, "Driver does not support configured "
- "HT capability [SMPS-*]");
- return 0;
+ switch (conf & HT_CAP_INFO_SMPS_MASK) {
+ case HT_CAP_INFO_SMPS_STATIC:
+ if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
+ wpa_printf(MSG_ERROR,
+ "Driver does not support configured HT capability [SMPS-STATIC]");
+ return 0;
+ }
+ break;
+ case HT_CAP_INFO_SMPS_DYNAMIC:
+ if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
+ wpa_printf(MSG_ERROR,
+ "Driver does not support configured HT capability [SMPS-DYNAMIC]");
+ return 0;
+ }
+ break;
+ case HT_CAP_INFO_SMPS_DISABLED:
+ default:
+ break;
}
if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
@@ -597,12 +592,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
return 0;
}
- if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
- wpa_printf(MSG_ERROR, "Driver does not support configured "
- "HT capability [PSMP]");
- return 0;
- }
-
if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
!(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
@@ -613,6 +602,112 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
return 1;
}
+
+#ifdef CONFIG_IEEE80211AC
+
+static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
+{
+ u32 req_cap = conf & cap;
+
+ /*
+ * Make sure we support all requested capabilities.
+ * NOTE: We assume that 'cap' represents a capability mask,
+ * not a discrete value.
+ */
+ if ((hw & req_cap) != req_cap) {
+ wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
+ name);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
+ unsigned int shift,
+ const char *name)
+{
+ u32 hw_max = hw & mask;
+ u32 conf_val = conf & mask;
+
+ if (conf_val > hw_max) {
+ wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+ name, conf_val >> shift, hw_max >> shift);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+ struct hostapd_hw_modes *mode = iface->current_mode;
+ u32 hw = mode->vht_capab;
+ u32 conf = iface->conf->vht_capab;
+
+ wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
+ hw, conf);
+
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+ iface->conf->bss[0]->vendor_vht &&
+ mode->vht_capab == 0 && iface->hw_features) {
+ int i;
+
+ for (i = 0; i < iface->num_hw_features; i++) {
+ if (iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &iface->hw_features[i];
+ hw = mode->vht_capab;
+ wpa_printf(MSG_DEBUG,
+ "update hw vht capab based on 5 GHz band: 0x%x",
+ hw);
+ break;
+ }
+ }
+ }
+
+#define VHT_CAP_CHECK(cap) \
+ do { \
+ if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
+ return 0; \
+ } while (0)
+
+#define VHT_CAP_CHECK_MAX(cap) \
+ do { \
+ if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
+ #cap)) \
+ return 0; \
+ } while (0)
+
+ VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
+ VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
+ VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+ VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+ VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+ VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+ VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+ VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+ VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+ VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+ VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+ VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+ VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+ VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+ VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+ return 1;
+}
+#endif /* CONFIG_IEEE80211AC */
+
#endif /* CONFIG_IEEE80211N */
@@ -624,6 +719,10 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
return 0;
if (!ieee80211n_supported_ht_capab(iface))
return -1;
+#ifdef CONFIG_IEEE80211AC
+ if (!ieee80211ac_supported_vht_capab(iface))
+ return -1;
+#endif /* CONFIG_IEEE80211AC */
ret = ieee80211n_check_40mhz(iface);
if (ret)
return ret;
@@ -635,6 +734,129 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
}
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+ int channel, int primary)
+{
+ int i;
+ struct hostapd_channel_data *chan;
+
+ for (i = 0; i < iface->current_mode->num_channels; i++) {
+ chan = &iface->current_mode->channels[i];
+ if (chan->chan != channel)
+ continue;
+
+ if (!(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" : "");
+ }
+
+ return 0;
+}
+
+
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+ if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+ return 0;
+
+ if (!iface->conf->secondary_channel)
+ return 1;
+
+ return hostapd_is_usable_chan(iface, iface->conf->channel +
+ iface->conf->secondary_channel * 4, 0);
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+ if (iface->conf->channel) {
+ if (hostapd_is_usable_chans(iface))
+ return HOSTAPD_CHAN_VALID;
+ else
+ return HOSTAPD_CHAN_INVALID;
+ }
+
+ /*
+ * The user set channel=0 or channel=acs_survey
+ * which is used to trigger ACS.
+ */
+
+ switch (acs_init(iface)) {
+ case HOSTAPD_CHAN_ACS:
+ return HOSTAPD_CHAN_ACS;
+ case HOSTAPD_CHAN_VALID:
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ return HOSTAPD_CHAN_INVALID;
+ }
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+ hostapd_logger(iface->bss[0], NULL,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Configured channel (%d) not found from the "
+ "channel list of current mode (%d) %s",
+ iface->conf->channel,
+ iface->current_mode->mode,
+ hostapd_hw_mode_txt(iface->current_mode->mode));
+ hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_WARNING,
+ "Hardware does not support configured channel");
+}
+
+
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+ int ret = -1;
+
+ if (err)
+ goto out;
+
+ switch (hostapd_check_chans(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+ ACS_EVENT_COMPLETED "freq=%d channel=%d",
+ hostapd_hw_get_freq(iface->bss[0],
+ iface->conf->channel),
+ iface->conf->channel);
+ break;
+ case HOSTAPD_CHAN_ACS:
+ wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+ hostapd_notify_bad_chans(iface);
+ goto out;
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+ wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+ hostapd_notify_bad_chans(iface);
+ goto out;
+ }
+
+ ret = hostapd_check_ht_capab(iface);
+ if (ret < 0)
+ goto out;
+ if (ret == 1) {
+ wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+ return 0;
+ }
+
+ ret = 0;
+out:
+ return hostapd_setup_interface_complete(iface, ret);
+}
+
+
/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
@@ -645,11 +867,20 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
*/
int hostapd_select_hw_mode(struct hostapd_iface *iface)
{
- int i, j, ok;
+ int i;
if (iface->num_hw_features < 1)
return -1;
+ if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+ iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+ iface->conf->channel == 14) {
+ wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+ iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+ iface->conf->ieee80211n = 0;
+ iface->conf->ieee80211ac = 0;
+ }
+
iface->current_mode = NULL;
for (i = 0; i < iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -670,82 +901,16 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
return -2;
}
- ok = 0;
- for (j = 0; j < iface->current_mode->num_channels; j++) {
- struct hostapd_channel_data *chan =
- &iface->current_mode->channels[j];
- if (chan->chan == iface->conf->channel) {
- if (chan->flag & HOSTAPD_CHAN_DISABLED) {
- wpa_printf(MSG_ERROR,
- "channel [%i] (%i) is disabled for "
- "use in AP mode, flags: 0x%x%s%s%s",
- j, chan->chan, chan->flag,
- chan->flag & HOSTAPD_CHAN_NO_IBSS ?
- " NO-IBSS" : "",
- chan->flag &
- HOSTAPD_CHAN_PASSIVE_SCAN ?
- " PASSIVE-SCAN" : "",
- chan->flag & HOSTAPD_CHAN_RADAR ?
- " RADAR" : "");
- } else {
- ok = 1;
- break;
- }
- }
- }
- if (ok && iface->conf->secondary_channel) {
- int sec_ok = 0;
- int sec_chan = iface->conf->channel +
- iface->conf->secondary_channel * 4;
- for (j = 0; j < iface->current_mode->num_channels; j++) {
- struct hostapd_channel_data *chan =
- &iface->current_mode->channels[j];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- (chan->chan == sec_chan)) {
- sec_ok = 1;
- break;
- }
- }
- if (!sec_ok) {
- hostapd_logger(iface->bss[0], NULL,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Configured HT40 secondary channel "
- "(%d) not found from the channel list "
- "of current mode (%d) %s",
- sec_chan, iface->current_mode->mode,
- hostapd_hw_mode_txt(
- iface->current_mode->mode));
- ok = 0;
- }
- }
- if (iface->conf->channel == 0) {
- /* TODO: could request a scan of neighboring BSSes and select
- * the channel automatically */
- wpa_printf(MSG_ERROR, "Channel not configured "
- "(hw_mode/channel in hostapd.conf)");
+ switch (hostapd_check_chans(iface)) {
+ case HOSTAPD_CHAN_VALID:
+ return 0;
+ case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+ return 1;
+ case HOSTAPD_CHAN_INVALID:
+ default:
+ hostapd_notify_bad_chans(iface);
return -3;
}
- if (ok == 0 && iface->conf->channel != 0) {
- hostapd_logger(iface->bss[0], NULL,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Configured channel (%d) not found from the "
- "channel list of current mode (%d) %s",
- iface->conf->channel,
- iface->current_mode->mode,
- hostapd_hw_mode_txt(iface->current_mode->mode));
- iface->current_mode = NULL;
- }
-
- if (iface->current_mode == NULL) {
- hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_WARNING,
- "Hardware does not support configured channel");
- return -4;
- }
-
- return 0;
}
@@ -768,35 +933,11 @@ const char * hostapd_hw_mode_txt(int mode)
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
{
- int i;
-
- if (!hapd->iface->current_mode)
- return 0;
-
- for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
- struct hostapd_channel_data *ch =
- &hapd->iface->current_mode->channels[i];
- if (ch->chan == chan)
- return ch->freq;
- }
-
- return 0;
+ return hw_get_freq(hapd->iface->current_mode, chan);
}
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
{
- int i;
-
- if (!hapd->iface->current_mode)
- return 0;
-
- for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
- struct hostapd_channel_data *ch =
- &hapd->iface->current_mode->channels[i];
- if (ch->freq == freq)
- return ch->chan;
- }
-
- return 0;
+ return hw_get_chan(hapd->iface->current_mode, freq);
}
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index abadcd137db1e..0f67ab8e5f37f 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef HW_FEATURES_H
@@ -21,6 +15,7 @@
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features);
int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
int hostapd_select_hw_mode(struct hostapd_iface *iface);
const char * hostapd_hw_mode_txt(int mode);
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
@@ -28,6 +23,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
int hostapd_check_ht_capab(struct hostapd_iface *iface);
int hostapd_prepare_rates(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
#else /* NEED_AP_MLME */
static inline void
hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -66,6 +62,10 @@ static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
return 0;
}
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
#endif /* NEED_AP_MLME */
#endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index be55c695672ce..99aa04dc3dd91 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -204,7 +204,7 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
addr.sin_port = htons(IAPP_UDP_PORT);
if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0)
- perror("sendto[IAPP-ADD]");
+ wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
}
@@ -231,7 +231,7 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
* FIX: what is correct RW with 802.11? */
if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
- perror("send[L2 Update]");
+ wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
}
@@ -242,29 +242,22 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
*/
void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
{
- struct ieee80211_mgmt *assoc;
- u16 seq;
+ u16 seq = 0; /* TODO */
if (iapp == NULL)
return;
- assoc = sta->last_assoc_req;
- seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
-
/* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
iapp_send_layer2_update(iapp, sta->addr);
iapp_send_add(iapp, sta->addr, seq);
- if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
- WLAN_FC_STYPE_REASSOC_REQ) {
- /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
- * Context Block, Timeout)
- */
- /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
- * IP address */
- }
+ /* TODO: If this was reassociation:
+ * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+ * Context Block, Timeout)
+ * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+ * IP address */
}
@@ -276,8 +269,8 @@ static void iapp_process_add_notify(struct iapp_data *iapp,
struct sta_info *sta;
if (len != sizeof(*add)) {
- printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
- len, (unsigned long) sizeof(*add));
+ wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
+ len, (unsigned long) sizeof(*add));
return;
}
@@ -326,7 +319,8 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
(struct sockaddr *) &from, &fromlen);
if (len < 0) {
- perror("recvfrom");
+ wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
+ strerror(errno));
return;
}
@@ -350,23 +344,24 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
hdr->version, hdr->command,
be_to_host16(hdr->identifier), hlen);
if (hdr->version != IAPP_VERSION) {
- printf("Dropping IAPP frame with unknown version %d\n",
- hdr->version);
+ wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
+ hdr->version);
return;
}
if (hlen > len) {
- printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+ wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
+ hlen, len);
return;
}
if (hlen < len) {
- printf("Ignoring %d extra bytes from IAPP frame\n",
- len - hlen);
+ wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
+ len - hlen);
len = hlen;
}
switch (hdr->command) {
case IAPP_CMD_ADD_notify:
- iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+ iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
break;
case IAPP_CMD_MOVE_notify:
/* TODO: MOVE is using TCP; so move this to TCP handler once it
@@ -376,7 +371,7 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
/* TODO: process */
break;
default:
- printf("Unknown IAPP command %d\n", hdr->command);
+ wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
break;
}
}
@@ -403,7 +398,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (iapp->udp_sock < 0) {
- perror("socket[PF_INET,SOCK_DGRAM]");
+ wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -411,35 +407,38 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
- perror("ioctl(SIOCGIFINDEX)");
+ wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
ifindex = ifr.ifr_ifindex;
if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
- perror("ioctl(SIOCGIFADDR)");
+ wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
if (paddr->sin_family != AF_INET) {
- printf("Invalid address family %i (SIOCGIFADDR)\n",
- paddr->sin_family);
+ wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
+ paddr->sin_family);
iapp_deinit(iapp);
return NULL;
}
iapp->own.s_addr = paddr->sin_addr.s_addr;
if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
- perror("ioctl(SIOCGIFBRDADDR)");
+ wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
paddr = (struct sockaddr_in *) &ifr.ifr_addr;
if (paddr->sin_family != AF_INET) {
- printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
- paddr->sin_family);
+ wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
+ paddr->sin_family);
iapp_deinit(iapp);
return NULL;
}
@@ -450,7 +449,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
uaddr.sin_port = htons(IAPP_UDP_PORT);
if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
sizeof(uaddr)) < 0) {
- perror("bind[UDP]");
+ wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -461,14 +461,16 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
mreq.imr_ifindex = 0;
if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq)) < 0) {
- perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+ wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (iapp->packet_sock < 0) {
- perror("socket[PF_PACKET,SOCK_RAW]");
+ wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
@@ -478,19 +480,20 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
addr.sll_ifindex = ifindex;
if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
- perror("bind[PACKET]");
+ wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
+ strerror(errno));
iapp_deinit(iapp);
return NULL;
}
if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
iapp, NULL)) {
- printf("Could not register read socket for IAPP.\n");
+ wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
iapp_deinit(iapp);
return NULL;
}
- printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+ wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
/* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
* RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
@@ -515,7 +518,8 @@ void iapp_deinit(struct iapp_data *iapp)
mreq.imr_ifindex = 0;
if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) {
- perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+ wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
+ strerror(errno));
}
eloop_unregister_read_sock(iapp->udp_sock);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 51c8d286d8687..89911b1fdd42f 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,10 +13,12 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
-#include "drivers/driver.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#include "common/sae.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -27,6 +29,7 @@
#include "sta_info.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
#include "wmm.h"
#include "ap_list.h"
#include "accounting.h"
@@ -36,6 +39,7 @@
#include "ap_drv_ops.h"
#include "wnm_ap.h"
#include "ieee802_11.h"
+#include "dfs.h"
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -59,7 +63,6 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
}
*pos++ = num;
- count = 0;
for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
i++) {
count++;
@@ -102,7 +105,6 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = num;
- count = 0;
for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
i++) {
count++;
@@ -135,6 +137,15 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
{
int capab = WLAN_CAPABILITY_ESS;
int privacy;
+ int dfs;
+
+ /* Check if any of configured channels require DFS */
+ dfs = hostapd_is_dfs_required(hapd->iface);
+ if (dfs < 0) {
+ wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+ dfs);
+ dfs = 0;
+ }
if (hapd->iface->num_sta_no_short_preamble == 0 &&
hapd->iconf->preamble == SHORT_PREAMBLE)
@@ -150,6 +161,11 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
if (hapd->conf->wpa)
privacy = 1;
+#ifdef CONFIG_HS20
+ if (hapd->conf->osen)
+ privacy = 1;
+#endif /* CONFIG_HS20 */
+
if (sta) {
int policy, def_klen;
if (probe && sta->ssid_probe) {
@@ -172,22 +188,21 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
hapd->iface->num_sta_no_short_slot_time == 0)
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
- return capab;
-}
+ /*
+ * Currently, Spectrum Management capability bit is set when directly
+ * requested in configuration by spectrum_mgmt_required or when AP is
+ * running on DFS channel.
+ * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+ */
+ if (hapd->iface->current_mode &&
+ hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+ (hapd->iconf->spectrum_mgmt_required || dfs))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+ if (hapd->conf->radio_measurements)
+ capab |= IEEE80211_CAP_RRM;
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
-{
- int i;
- if (len > HOSTAPD_MAX_SSID_LEN)
- len = HOSTAPD_MAX_SSID_LEN;
- for (i = 0; i < len; i++) {
- if (ssid[i] >= 32 && ssid[i] < 127)
- buf[i] = ssid[i];
- else
- buf[i] = '.';
- }
- buf[len] = '\0';
+ return capab;
}
@@ -225,7 +240,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
/* Transaction 3 */
if (!iswep || !sta->challenge || !challenge ||
- os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+ os_memcmp_const(sta->challenge, challenge,
+ WLAN_AUTH_CHALLENGE_LEN)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"shared key authentication - invalid "
@@ -236,13 +252,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (shared key)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
- /* Station will be marked authenticated if it ACKs the
- * authentication reply. */
-#else
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-#endif
os_free(sta->challenge);
sta->challenge = NULL;
@@ -283,7 +294,7 @@ static void send_auth_reply(struct hostapd_data *hapd,
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len);
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
- perror("send_auth_reply: send");
+ wpa_printf(MSG_INFO, "send_auth_reply: send");
os_free(buf);
}
@@ -317,19 +328,34 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
#ifdef CONFIG_SAE
+#define dot11RSNASAERetransPeriod 40 /* msec */
+#define dot11RSNASAESync 5 /* attempts */
+
+
static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
- struct sta_info *sta)
+ struct sta_info *sta, int update)
{
struct wpabuf *buf;
- buf = wpabuf_alloc(2);
- if (buf == NULL)
+ if (hapd->conf->ssid.wpa_passphrase == NULL) {
+ wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
+ }
+
+ if (update &&
+ sae_prepare_commit(hapd->own_addr, sta->addr,
+ (u8 *) hapd->conf->ssid.wpa_passphrase,
+ os_strlen(hapd->conf->ssid.wpa_passphrase),
+ sta->sae) < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
+ return NULL;
+ }
- wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */
- /* TODO: Anti-Clogging Token (if requested) */
- /* TODO: Scalar */
- /* TODO: Element */
+ buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
+ if (buf == NULL)
+ return NULL;
+ sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+ sta->sae->tmp->anti_clogging_token : NULL);
return buf;
}
@@ -340,114 +366,517 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
{
struct wpabuf *buf;
- buf = wpabuf_alloc(2);
+ buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
if (buf == NULL)
return NULL;
- wpabuf_put_le16(buf, sta->sae_send_confirm);
- sta->sae_send_confirm++;
- /* TODO: Confirm */
+ sae_write_confirm(sta->sae, buf);
return buf;
}
-static u16 handle_sae_commit(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *data, size_t len)
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *bssid, int update)
{
- wpa_hexdump(MSG_DEBUG, "SAE commit fields", data, len);
+ struct wpabuf *data;
- /* Check Finite Cyclic Group */
- if (len < 2)
+ data = auth_build_sae_commit(hapd, sta, update);
+ if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- if (WPA_GET_LE16(data) != 19) {
- wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
- WPA_GET_LE16(data));
- return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
- }
+
+ send_auth_reply(hapd, sta->addr, bssid,
+ WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+ wpabuf_head(data), wpabuf_len(data));
+
+ wpabuf_free(data);
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ const u8 *bssid)
+{
+ struct wpabuf *data;
+
+ data = auth_build_sae_confirm(hapd, sta);
+ if (data == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ send_auth_reply(hapd, sta->addr, bssid,
+ WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+ wpabuf_head(data), wpabuf_len(data));
+
+ wpabuf_free(data);
return WLAN_STATUS_SUCCESS;
}
-static u16 handle_sae_confirm(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *data, size_t len)
+static int use_sae_anti_clogging(struct hostapd_data *hapd)
+{
+ struct sta_info *sta;
+ unsigned int open = 0;
+
+ if (hapd->conf->sae_anti_clogging_threshold == 0)
+ return 1;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!sta->sae)
+ continue;
+ if (sta->sae->state != SAE_COMMITTED &&
+ sta->sae->state != SAE_CONFIRMED)
+ continue;
+ open++;
+ if (open >= hapd->conf->sae_anti_clogging_threshold)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
+ const u8 *token, size_t token_len)
+{
+ u8 mac[SHA256_MAC_LEN];
+
+ if (token_len != SHA256_MAC_LEN)
+ return -1;
+ if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+ addr, ETH_ALEN, mac) < 0 ||
+ os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
+ int group, const u8 *addr)
+{
+ struct wpabuf *buf;
+ u8 *token;
+ struct os_reltime now;
+
+ os_get_reltime(&now);
+ if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
+ os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
+ if (random_get_bytes(hapd->sae_token_key,
+ sizeof(hapd->sae_token_key)) < 0)
+ return NULL;
+ wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
+ hapd->sae_token_key, sizeof(hapd->sae_token_key));
+ hapd->last_sae_token_key_update = now;
+ }
+
+ buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
+ if (buf == NULL)
+ return NULL;
+
+ wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
+ token = wpabuf_put(buf, SHA256_MAC_LEN);
+ hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+ addr, ETH_ALEN, token);
+
+ return buf;
+}
+
+
+static int sae_check_big_sync(struct sta_info *sta)
+{
+ if (sta->sae->sync > dot11RSNASAESync) {
+ sta->sae->state = SAE_NOTHING;
+ sta->sae->sync = 0;
+ return -1;
+ }
+ return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = eloop_data;
+ int ret;
+
+ if (sae_check_big_sync(sta))
+ return;
+ sta->sae->sync++;
+
+ switch (sta->sae->state) {
+ case SAE_COMMITTED:
+ ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+ eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ case SAE_CONFIRMED:
+ ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+ eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ if (ret != WLAN_STATUS_SUCCESS)
+ wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
{
- u16 rc;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ return;
+
+ eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+ eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+ auth_sae_retransmit_timer, hapd, sta);
+}
- wpa_hexdump(MSG_DEBUG, "SAE confirm fields", data, len);
- if (len < 2)
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *bssid, u8 auth_transaction)
+{
+ int ret;
+
+ if (auth_transaction != 1 && auth_transaction != 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
- rc = WPA_GET_LE16(data);
- wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc);
+ switch (sta->sae->state) {
+ case SAE_NOTHING:
+ if (auth_transaction == 1) {
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_COMMITTED;
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ /*
+ * In mesh case, both Commit and Confirm can be sent
+ * immediately. In infrastructure BSS, only a single
+ * Authentication frame (Commit) is expected from the AP
+ * here and the second one (Confirm) will be sent once
+ * the STA has sent its second Authentication frame
+ * (Confirm).
+ */
+ if (hapd->conf->mesh & MESH_ENABLED) {
+ /*
+ * Send both Commit and Confirm immediately
+ * based on SAE finite state machine
+ * Nothing -> Confirm transition.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_CONFIRMED;
+ } else {
+ /*
+ * For infrastructure BSS, send only the Commit
+ * message now to get alternating sequence of
+ * Authentication frames between the AP and STA.
+ * Confirm will be sent in
+ * Commited -> Confirmed/Accepted transition
+ * when receiving Confirm from STA.
+ */
+ }
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "SAE confirm before commit");
+ }
+ break;
+ case SAE_COMMITTED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+ sta->sae->state = SAE_CONFIRMED;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ } else if (hapd->conf->mesh & MESH_ENABLED) {
+ /*
+ * In mesh case, follow SAE finite state machine and
+ * send Commit now, if sync count allows.
+ */
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ /*
+ * For instructure BSS, send the postponed Confirm from
+ * Nothing -> Confirmed transition that was reduced to
+ * Nothing -> Committed above.
+ */
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sta->sae->state = SAE_CONFIRMED;
+
+ /*
+ * Since this was triggered on Confirm RX, run another
+ * step to get to Accepted without waiting for
+ * additional events.
+ */
+ return sae_sm_step(hapd, sta, bssid, auth_transaction);
+ }
+ break;
+ case SAE_CONFIRMED:
+ sae_clear_retransmit_timer(hapd, sta);
+ if (auth_transaction == 1) {
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ if (ret)
+ return ret;
+
+ if (sae_process_commit(sta->sae) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ if (ret)
+ return ret;
+
+ sae_set_retransmit_timer(hapd, sta);
+ } else {
+ sta->flags |= WLAN_STA_AUTH;
+ sta->auth_alg = WLAN_AUTH_SAE;
+ mlme_authenticate_indication(hapd, sta);
+ wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+ sta->sae->state = SAE_ACCEPTED;
+ wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+ sta->sae->pmk);
+ }
+ break;
+ case SAE_ACCEPTED:
+ if (auth_transaction == 1) {
+ wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+ ") doing reauthentication",
+ MAC2STR(sta->addr));
+ ap_free_sta(hapd, sta);
+ } else {
+ if (sae_check_big_sync(sta))
+ return WLAN_STATUS_SUCCESS;
+ sta->sae->sync++;
+
+ ret = auth_sae_send_confirm(hapd, sta, bssid);
+ sae_clear_temp_data(sta->sae);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+ sta->sae->state);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
return WLAN_STATUS_SUCCESS;
}
static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len,
- u8 auth_transaction)
+ u16 auth_transaction, u16 status_code)
{
u16 resp = WLAN_STATUS_SUCCESS;
- struct wpabuf *data;
+ struct wpabuf *data = NULL;
+
+ if (!sta->sae) {
+ if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+ return;
+ sta->sae = os_zalloc(sizeof(*sta->sae));
+ if (sta->sae == NULL)
+ return;
+ sta->sae->state = SAE_NOTHING;
+ sta->sae->sync = 0;
+ }
if (auth_transaction == 1) {
+ const u8 *token = NULL, *pos, *end;
+ size_t token_len = 0;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
- "start SAE authentication (RX commit)");
- resp = handle_sae_commit(hapd, sta, mgmt->u.auth.variable,
- ((u8 *) mgmt) + len -
- mgmt->u.auth.variable);
- if (resp == WLAN_STATUS_SUCCESS)
- sta->sae_state = SAE_COMMIT;
- } else if (auth_transaction == 2) {
- if (sta->sae_state != SAE_COMMIT) {
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
- "SAE confirm before commit");
- resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
+ "start SAE authentication (RX commit, status=%u)",
+ status_code);
+
+ if ((hapd->conf->mesh & MESH_ENABLED) &&
+ status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+ sta->sae->tmp) {
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + len;
+ if (pos + sizeof(le16) > end) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Too short anti-clogging token request");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
+ resp = sae_group_allowed(sta->sae,
+ hapd->conf->sae_groups,
+ WPA_GET_LE16(pos));
+ if (resp != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Invalid group in anti-clogging token request");
+ goto reply;
+ }
+ pos += sizeof(le16);
+
+ wpabuf_free(sta->sae->tmp->anti_clogging_token);
+ sta->sae->tmp->anti_clogging_token =
+ wpabuf_alloc_copy(pos, end - pos);
+ if (sta->sae->tmp->anti_clogging_token == NULL) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to alloc for anti-clogging token");
+ return;
+ }
+
+ /*
+ * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+ * is 76, a new Commit Message shall be constructed
+ * with the Anti-Clogging Token from the received
+ * Authentication frame, and the commit-scalar and
+ * COMMIT-ELEMENT previously sent.
+ */
+ if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+ wpa_printf(MSG_ERROR,
+ "SAE: Failed to send commit message");
+ return;
+ }
+ sta->sae->state = SAE_COMMITTED;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+ return;
}
+
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return;
+
+ resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
+ ((const u8 *) mgmt) + len -
+ mgmt->u.auth.variable, &token,
+ &token_len, hapd->conf->sae_groups);
+ if (token && check_sae_token(hapd, sta->addr, token, token_len)
+ < 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
+ "incorrect token from " MACSTR,
+ MAC2STR(sta->addr));
+ return;
+ }
+
+ if (resp != WLAN_STATUS_SUCCESS)
+ goto reply;
+
+ if (!token && use_sae_anti_clogging(hapd)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Request anti-clogging token from "
+ MACSTR, MAC2STR(sta->addr));
+ data = auth_build_token_req(hapd, sta->sae->group,
+ sta->addr);
+ resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+ if (hapd->conf->mesh & MESH_ENABLED)
+ sta->sae->state = SAE_NOTHING;
+ goto reply;
+ }
+
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+ } else if (auth_transaction == 2) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
- "SAE authentication (RX confirm)");
- resp = handle_sae_confirm(hapd, sta, mgmt->u.auth.variable,
- ((u8 *) mgmt) + len -
- mgmt->u.auth.variable);
- if (resp == WLAN_STATUS_SUCCESS) {
- sta->flags |= WLAN_STA_AUTH;
- wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
- sta->auth_alg = WLAN_AUTH_SAE;
- mlme_authenticate_indication(hapd, sta);
+ "SAE authentication (RX confirm, status=%u)",
+ status_code);
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return;
+ if (sta->sae->state >= SAE_CONFIRMED ||
+ !(hapd->conf->mesh & MESH_ENABLED)) {
+ if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+ ((u8 *) mgmt) + len -
+ mgmt->u.auth.variable) < 0) {
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto reply;
+ }
}
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
- "unexpected SAE authentication transaction %u",
- auth_transaction);
+ "unexpected SAE authentication transaction %u (status=%u)",
+ auth_transaction, status_code);
+ if (status_code != WLAN_STATUS_SUCCESS)
+ return;
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
- sta->auth_alg = WLAN_AUTH_SAE;
+reply:
+ if (resp != WLAN_STATUS_SUCCESS) {
+ send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+ auth_transaction, resp,
+ data ? wpabuf_head(data) : (u8 *) "",
+ data ? wpabuf_len(data) : 0);
+ }
+ wpabuf_free(data);
+}
- if (resp == WLAN_STATUS_SUCCESS) {
- if (auth_transaction == 1)
- data = auth_build_sae_commit(hapd, sta);
- else
- data = auth_build_sae_confirm(hapd, sta);
- if (data == NULL)
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- } else
- data = NULL;
- send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
- auth_transaction, resp,
- data ? wpabuf_head(data) : (u8 *) "",
- data ? wpabuf_len(data) : 0);
- wpabuf_free(data);
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ int ret;
+
+ if (!sta->sae || !sta->sae->tmp)
+ return -1;
+
+ if (sta->sae->state != SAE_NOTHING)
+ return -1;
+
+ ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+ if (ret)
+ return -1;
+
+ sta->sae->state = SAE_COMMITTED;
+ sta->sae->sync = 0;
+ sae_set_retransmit_timer(hapd, sta);
+
+ return 0;
}
+
#endif /* CONFIG_SAE */
@@ -467,17 +896,29 @@ static void handle_auth(struct hostapd_data *hapd,
size_t resp_ies_len = 0;
char *identity = NULL;
char *radius_cui = NULL;
+ u16 seq_ctrl;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- printf("handle_auth - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->iconf->ignore_auth_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_auth_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring auth frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
fc = le_to_host16(mgmt->frame_control);
+ seq_ctrl = le_to_host16(mgmt->seq_ctrl);
if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
2 + WLAN_AUTH_CHALLENGE_LEN &&
@@ -486,10 +927,12 @@ static void handle_auth(struct hostapd_data *hapd,
challenge = &mgmt->u.auth.variable[2];
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
- "auth_transaction=%d status_code=%d wep=%d%s",
+ "auth_transaction=%d status_code=%d wep=%d%s "
+ "seq_ctrl=0x%x%s",
MAC2STR(mgmt->sa), auth_alg, auth_transaction,
status_code, !!(fc & WLAN_FC_ISWEP),
- challenge ? " challenge" : "");
+ challenge ? " challenge" : "",
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
if (hapd->tkip_countermeasures) {
resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
@@ -508,23 +951,23 @@ static void handle_auth(struct hostapd_data *hapd,
#endif /* CONFIG_SAE */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
- printf("Unsupported authentication algorithm (%d)\n",
- auth_alg);
+ wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+ auth_alg);
resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
goto fail;
}
if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
- printf("Unknown authentication transaction number (%d)\n",
- auth_transaction);
+ wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
+ auth_transaction);
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
goto fail;
}
if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
- printf("Station " MACSTR " not allowed to authenticate.\n",
- MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -535,8 +978,8 @@ static void handle_auth(struct hostapd_data *hapd,
&psk, &identity, &radius_cui);
if (res == HOSTAPD_ACL_REJECT) {
- printf("Station " MACSTR " not allowed to authenticate.\n",
- MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+ MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
@@ -550,15 +993,49 @@ static void handle_auth(struct hostapd_data *hapd,
return;
}
- sta = ap_sta_add(hapd, mgmt->sa);
- if (!sta) {
- resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto fail;
+ sta = ap_get_sta(hapd, mgmt->sa);
+ if (sta) {
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated authentication frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return;
+ }
+ } else {
+#ifdef CONFIG_MESH
+ if (hapd->conf->mesh & MESH_ENABLED) {
+ /* if the mesh peer is not available, we don't do auth.
+ */
+ wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+ " not yet known - drop Authentiation frame",
+ MAC2STR(mgmt->sa));
+ /*
+ * Save a copy of the frame so that it can be processed
+ * if a new peer entry is added shortly after this.
+ */
+ wpabuf_free(hapd->mesh_pending_auth);
+ hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+ os_get_reltime(&hapd->mesh_pending_auth_time);
+ return;
+ }
+#endif /* CONFIG_MESH */
+
+ sta = ap_sta_add(hapd, mgmt->sa);
+ if (!sta) {
+ resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+ goto fail;
+ }
}
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = WLAN_FC_STYPE_AUTH;
if (vlan_id > 0) {
- if (hostapd_get_vlan_id_ifname(hapd->conf->vlan,
- vlan_id) == NULL) {
+ if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
"%d received from RADIUS server",
@@ -599,15 +1076,10 @@ static void handle_auth(struct hostapd_data *hapd,
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (open system)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
- /* Station will be marked authenticated if it ACKs the
- * authentication reply. */
-#else
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->auth_alg = WLAN_AUTH_OPEN;
mlme_authenticate_indication(hapd, sta);
-#endif
break;
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
@@ -627,7 +1099,7 @@ static void handle_auth(struct hostapd_data *hapd,
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
"state machine");
@@ -644,7 +1116,23 @@ static void handle_auth(struct hostapd_data *hapd,
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SAE
case WLAN_AUTH_SAE:
- handle_auth_sae(hapd, sta, mgmt, len, auth_transaction);
+#ifdef CONFIG_MESH
+ if (status_code == WLAN_STATUS_SUCCESS &&
+ hapd->conf->mesh & MESH_ENABLED) {
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm =
+ wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Failed to initialize WPA state machine");
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
+ }
+#endif /* CONFIG_MESH */
+ handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+ status_code);
return;
#endif /* CONFIG_SAE */
}
@@ -700,12 +1188,10 @@ static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
- char ssid_txt[33];
- ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station tried to associate with unknown SSID "
- "'%s'", ssid_txt);
+ "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
@@ -767,6 +1253,21 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
}
+static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+ /* check for QoS Map support */
+ if (ext_capab_ie_len >= 5) {
+ if (ext_capab_ie[4] & 0x01)
+ sta->qos_map_enabled = 1;
+ }
+#endif /* CONFIG_INTERWORKING */
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
@@ -774,6 +1275,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
u16 resp;
const u8 *wpa_ie;
size_t wpa_ie_len;
+ const u8 *p2p_dev_addr = NULL;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -788,6 +1290,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -810,15 +1315,40 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
elems.vht_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
!(sta->flags & WLAN_STA_VHT)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory VHT PHY - reject association");
- return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+ }
+
+ if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
+ resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
+ elems.vendor_vht_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_P2P
+ if (elems.p2p) {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (sta->p2p_ie)
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ } else {
+ wpabuf_free(sta->p2p_ie);
+ sta->p2p_ie = NULL;
+ }
+#endif /* CONFIG_P2P */
+
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
wpa_ie = elems.rsn_ie;
wpa_ie_len = elems.rsn_ie_len;
@@ -870,7 +1400,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
wpa_ie_len += 2;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
- sta->addr);
+ sta->addr,
+ p2p_dev_addr);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_WARNING, "Failed to initialize WPA "
"state machine");
@@ -943,7 +1474,21 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) &&
- sta->auth_alg != WLAN_AUTH_SAE) {
+ sta->auth_alg == WLAN_AUTH_OPEN) {
+ struct rsn_pmksa_cache_entry *sa;
+ sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+ if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No PMKSA cache entry found for "
+ MACSTR, MAC2STR(sta->addr));
+ return WLAN_STATUS_INVALID_PMKID;
+ }
+ wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+ " using PMKSA caching", MAC2STR(sta->addr));
+ } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+ sta->auth_alg != WLAN_AUTH_SAE &&
+ !(sta->auth_alg == WLAN_AUTH_FT &&
+ wpa_auth_uses_ft_sae(sta->wpa_sm))) {
wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
"SAE AKM after non-SAE auth_alg %u",
MAC2STR(sta->addr), sta->auth_alg);
@@ -962,20 +1507,33 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_HS20
+ } else if (hapd->conf->osen) {
+ if (elems.osen == NULL) {
+ hostapd_logger(
+ hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "No HS 2.0 OSEN element in association request");
+ return WLAN_STATUS_INVALID_IE;
+ }
+
+ wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr, NULL);
+ if (sta->wpa_sm == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+ "state machine");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+ elems.osen - 2, elems.osen_len + 2) < 0)
+ return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
#ifdef CONFIG_P2P
- if (elems.p2p) {
- wpabuf_free(sta->p2p_ie);
- sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
- P2P_IE_VENDOR_TYPE);
-
- } else {
- wpabuf_free(sta->p2p_ie);
- sta->p2p_ie = NULL;
- }
-
p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
#endif /* CONFIG_P2P */
@@ -1038,8 +1596,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
reply->u.assoc_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
reply->u.assoc_resp.status_code = host_to_le16(status_code);
- reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
- | BIT(14) | BIT(15));
+ reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
/* Supported rates */
p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
/* Extended supported rates */
@@ -1066,12 +1623,21 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
- p = hostapd_eid_vht_capabilities(hapd, p);
- p = hostapd_eid_vht_operation(hapd, p);
+ if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+ p = hostapd_eid_vht_capabilities(hapd, p);
+ p = hostapd_eid_vht_operation(hapd, p);
+ }
#endif /* CONFIG_IEEE80211AC */
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
+ if (sta->qos_map_enabled)
+ p = hostapd_eid_qos_map_set(hapd, p);
+
+#ifdef CONFIG_IEEE80211AC
+ if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+ p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
if (sta->flags & WLAN_STA_WMM)
p = hostapd_eid_wmm(hapd, p);
@@ -1130,7 +1696,7 @@ static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc)
{
- u16 capab_info, listen_interval;
+ u16 capab_info, listen_interval, seq_ctrl, fc;
u16 resp = WLAN_STATUS_SUCCESS;
const u8 *pos;
int left, i;
@@ -1138,20 +1704,44 @@ static void handle_assoc(struct hostapd_data *hapd,
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
- printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
- "\n", reassoc, (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
+ reassoc, (unsigned long) len);
return;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (reassoc) {
+ if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_reassoc_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring reassoc request from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+ } else {
+ if (hapd->iconf->ignore_assoc_probability > 0.0 &&
+ drand48() < hapd->iconf->ignore_assoc_probability) {
+ wpa_printf(MSG_INFO,
+ "TESTING: ignoring assoc request from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ fc = le_to_host16(mgmt->frame_control);
+ seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
if (reassoc) {
capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
listen_interval = le_to_host16(
mgmt->u.reassoc_req.listen_interval);
wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
" capab_info=0x%02x listen_interval=%d current_ap="
- MACSTR,
+ MACSTR " seq_ctrl=0x%x%s",
MAC2STR(mgmt->sa), capab_info, listen_interval,
- MAC2STR(mgmt->u.reassoc_req.current_ap));
+ MAC2STR(mgmt->u.reassoc_req.current_ap),
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
pos = mgmt->u.reassoc_req.variable;
} else {
@@ -1159,8 +1749,10 @@ static void handle_assoc(struct hostapd_data *hapd,
listen_interval = le_to_host16(
mgmt->u.assoc_req.listen_interval);
wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
- " capab_info=0x%02x listen_interval=%d",
- MAC2STR(mgmt->sa), capab_info, listen_interval);
+ " capab_info=0x%02x listen_interval=%d "
+ "seq_ctrl=0x%x%s",
+ MAC2STR(mgmt->sa), capab_info, listen_interval,
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
pos = mgmt->u.assoc_req.variable;
}
@@ -1186,6 +1778,21 @@ static void handle_assoc(struct hostapd_data *hapd,
return;
}
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated association frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return;
+ }
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+ WLAN_FC_STYPE_ASSOC_REQ;
+
if (hapd->tkip_countermeasures) {
resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
goto fail;
@@ -1279,17 +1886,6 @@ static void handle_assoc(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211W */
- if (reassoc) {
- os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap,
- ETH_ALEN);
- }
-
- if (sta->last_assoc_req)
- os_free(sta->last_assoc_req);
- sta->last_assoc_req = os_malloc(len);
- if (sta->last_assoc_req)
- os_memcpy(sta->last_assoc_req, mgmt, len);
-
/* Make sure that the previously registered inactivity timer will not
* remove the STA immediately. */
sta->timeout_next = STA_NULLFUNC;
@@ -1305,8 +1901,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
- printf("handle_disassoc - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1316,12 +1912,13 @@ static void handle_disassoc(struct hostapd_data *hapd,
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
- printf("Station " MACSTR " trying to disassociate, but it "
- "is not associated.\n", MAC2STR(mgmt->sa));
+ wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
+ MAC2STR(mgmt->sa));
return;
}
ap_sta_set_authorized(hapd, sta, 0);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1332,6 +1929,9 @@ static void handle_disassoc(struct hostapd_data *hapd,
* authenticated. */
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(sta);
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
hostapd_drv_sta_remove(hapd, sta->addr);
if (sta->timeout_next == STA_NULLFUNC ||
@@ -1371,6 +1971,7 @@ static void handle_deauth(struct hostapd_data *hapd,
}
ap_sta_set_authorized(hapd, sta, 0);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
WLAN_STA_ASSOC_REQ_OK);
wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
@@ -1391,8 +1992,8 @@ static void handle_beacon(struct hostapd_data *hapd,
struct ieee802_11_elems elems;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
- printf("handle_beacon - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1407,9 +2008,9 @@ static void handle_beacon(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211W
-static void hostapd_sa_query_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt,
- size_t len)
+static int hostapd_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
const u8 *end;
@@ -1418,12 +2019,13 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd,
if (((u8 *) mgmt) + len < end) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
"frame (len=%lu)", (unsigned long) len);
- return;
+ 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);
+ return 1;
}
@@ -1435,29 +2037,8 @@ static int robust_action_frame(u8 category)
#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
-static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
- const struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- struct rx_action action;
- if (len < IEEE80211_HDRLEN + 2)
- return;
- os_memset(&action, 0, sizeof(action));
- action.da = mgmt->da;
- action.sa = mgmt->sa;
- action.bssid = mgmt->bssid;
- action.category = mgmt->u.action.category;
- action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
- action.len = len - IEEE80211_HDRLEN - 1;
- action.freq = hapd->iface->freq;
- ieee802_11_rx_wnm_action_ap(hapd, &action);
-}
-#endif /* CONFIG_WNM */
-
-
-static void handle_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+static int handle_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
sta = ap_get_sta(hapd, mgmt->sa);
@@ -1467,7 +2048,7 @@ static void handle_action(struct hostapd_data *hapd,
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
(unsigned long) len);
- return;
+ return 0;
}
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
@@ -1475,56 +2056,92 @@ static void handle_action(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
"frame (category=%u) from unassociated STA " MACSTR,
MAC2STR(mgmt->sa), mgmt->u.action.category);
- return;
+ return 0;
}
#ifdef CONFIG_IEEE80211W
if (sta && (sta->flags & WLAN_STA_MFP) &&
- !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
- robust_action_frame(mgmt->u.action.category))) {
+ !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+ robust_action_frame(mgmt->u.action.category)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Dropped unprotected Robust Action frame from "
"an MFP STA");
- return;
+ return 0;
}
#endif /* CONFIG_IEEE80211W */
+ if (sta) {
+ u16 fc = le_to_host16(mgmt->frame_control);
+ u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+ if ((fc & WLAN_FC_RETRY) &&
+ sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+ sta->last_seq_ctrl == seq_ctrl &&
+ sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Drop repeated action frame seq_ctrl=0x%x",
+ seq_ctrl);
+ return 1;
+ }
+
+ sta->last_seq_ctrl = seq_ctrl;
+ sta->last_subtype = WLAN_FC_STYPE_ACTION;
+ }
+
switch (mgmt->u.action.category) {
#ifdef CONFIG_IEEE80211R
case WLAN_ACTION_FT:
- if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+ if (!sta ||
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
- return;
+ return 1;
#endif /* CONFIG_IEEE80211R */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
- return;
+ return 1;
#ifdef CONFIG_IEEE80211W
case WLAN_ACTION_SA_QUERY:
- hostapd_sa_query_action(hapd, mgmt, len);
- return;
+ return hostapd_sa_query_action(hapd, mgmt, len);
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM
case WLAN_ACTION_WNM:
- hostapd_wnm_action(hapd, sta, mgmt, len);
- return;
+ ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+ return 1;
#endif /* CONFIG_WNM */
case WLAN_ACTION_PUBLIC:
+ case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+ if (mgmt->u.action.u.public_action.action ==
+ WLAN_PA_20_40_BSS_COEX) {
+ wpa_printf(MSG_DEBUG,
+ "HT20/40 coex mgmt frame received from STA "
+ MACSTR, MAC2STR(mgmt->sa));
+ hostapd_2040_coex_action(hapd, mgmt, len);
+ }
+#endif /* CONFIG_IEEE80211N */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len,
hapd->iface->freq);
- return;
}
+ if (hapd->public_action_cb2) {
+ hapd->public_action_cb2(hapd->public_action_cb2_ctx,
+ (u8 *) mgmt, len,
+ hapd->iface->freq);
+ }
+ if (hapd->public_action_cb || hapd->public_action_cb2)
+ return 1;
break;
case WLAN_ACTION_VENDOR_SPECIFIC:
if (hapd->vendor_action_cb) {
if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
(u8 *) mgmt, len,
hapd->iface->freq) == 0)
- return;
+ return 1;
}
break;
}
@@ -1547,7 +2164,7 @@ static void handle_action(struct hostapd_data *hapd,
"frame back to sender");
resp = os_malloc(len);
if (resp == NULL)
- return;
+ return 0;
os_memcpy(resp, mgmt, len);
os_memcpy(resp->da, resp->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
@@ -1560,6 +2177,8 @@ static void handle_action(struct hostapd_data *hapd,
}
os_free(resp);
}
+
+ return 1;
}
@@ -1576,15 +2195,16 @@ static void handle_action(struct hostapd_data *hapd,
* addition, it can be called to re-inserted pending frames (e.g., when using
* external RADIUS server as an MAC ACL).
*/
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
- struct hostapd_frame_info *fi)
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi)
{
struct ieee80211_mgmt *mgmt;
int broadcast;
u16 fc, stype;
+ int ret = 0;
if (len < 24)
- return;
+ return 0;
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
@@ -1592,7 +2212,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
if (stype == WLAN_FC_STYPE_BEACON) {
handle_beacon(hapd, mgmt, len, fi);
- return;
+ return 1;
}
broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
@@ -1605,16 +2225,19 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
!((hapd->conf->p2p & P2P_GROUP_OWNER) &&
stype == WLAN_FC_STYPE_ACTION) &&
#endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+ !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
- printf("MGMT: BSSID=" MACSTR " not our address\n",
- MAC2STR(mgmt->bssid));
- return;
+ wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
+ MAC2STR(mgmt->bssid));
+ return 0;
}
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
- return;
+ return 1;
}
if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
@@ -1622,33 +2245,38 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
MAC2STR(mgmt->da));
- return;
+ return 0;
}
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
handle_auth(hapd, mgmt, len);
+ ret = 1;
break;
case WLAN_FC_STYPE_ASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
handle_assoc(hapd, mgmt, len, 0);
+ ret = 1;
break;
case WLAN_FC_STYPE_REASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
handle_assoc(hapd, mgmt, len, 1);
+ ret = 1;
break;
case WLAN_FC_STYPE_DISASSOC:
wpa_printf(MSG_DEBUG, "mgmt::disassoc");
handle_disassoc(hapd, mgmt, len);
+ ret = 1;
break;
case WLAN_FC_STYPE_DEAUTH:
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
handle_deauth(hapd, mgmt, len);
+ ret = 1;
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action");
- handle_action(hapd, mgmt, len);
+ ret = handle_action(hapd, mgmt, len);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1656,6 +2284,8 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
"unknown mgmt frame subtype %d", stype);
break;
}
+
+ return ret;
}
@@ -1674,8 +2304,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
}
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
- printf("handle_auth_cb - too short payload (len=%lu)\n",
- (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+ (unsigned long) len);
return;
}
@@ -1685,8 +2315,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- printf("handle_auth_cb: STA " MACSTR " not found\n",
- MAC2STR(mgmt->da));
+ wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
return;
}
@@ -1700,6 +2330,30 @@ static void handle_auth_cb(struct hostapd_data *hapd,
}
+static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *ifname_wds)
+{
+ int i;
+ struct hostapd_ssid *ssid = sta->ssid;
+
+ if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+ return;
+
+ for (i = 0; i < 4; i++) {
+ if (ssid->wep.key[i] &&
+ hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
+ i == ssid->wep.idx, NULL, 0,
+ ssid->wep.key[i], ssid->wep.len[i])) {
+ wpa_printf(MSG_WARNING,
+ "Could not set WEP keys for WDS interface; %s",
+ ifname_wds);
+ break;
+ }
+ }
+}
+
+
static void handle_assoc_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int reassoc, int ok)
@@ -1708,18 +2362,19 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
struct sta_info *sta;
int new_assoc = 1;
struct ieee80211_ht_capabilities ht_cap;
+ struct ieee80211_vht_capabilities vht_cap;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
sizeof(mgmt->u.assoc_resp))) {
- printf("handle_assoc_cb(reassoc=%d) - too short payload "
- "(len=%lu)\n", reassoc, (unsigned long) len);
+ wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+ reassoc, (unsigned long) len);
return;
}
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
- printf("handle_assoc_cb: STA " MACSTR " not found\n",
- MAC2STR(mgmt->da));
+ wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+ MAC2STR(mgmt->da));
return;
}
@@ -1737,7 +2392,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
status = le_to_host16(mgmt->u.assoc_resp.status_code);
if (status != WLAN_STATUS_SUCCESS)
- goto fail;
+ return;
/* Stop previous accounting session, if one is started, and allocate
* new session id for the new session. */
@@ -1751,7 +2406,8 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
if (sta->flags & WLAN_STA_ASSOC)
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
- if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+ if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
sta->auth_alg == WLAN_AUTH_FT) {
/*
* Open, static WEP, or FT protocol; no separate authorization
@@ -1780,12 +2436,17 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
if (sta->flags & WLAN_STA_HT)
hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
#endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+ if (sta->flags & WLAN_STA_VHT)
+ hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
+#endif /* CONFIG_IEEE80211AC */
if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
sta->supported_rates, sta->supported_rates_len,
sta->listen_interval,
sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
- sta->flags, sta->qosinfo)) {
+ sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+ sta->flags, sta->qosinfo, sta->vht_opmode)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"Could not add STA to kernel driver");
@@ -1793,11 +2454,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_DISASSOC_AP_BUSY);
- goto fail;
+ return;
}
- if (sta->flags & WLAN_STA_WDS)
- hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ if (sta->flags & WLAN_STA_WDS) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
+ ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+ sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+ }
if (sta->eapol_sm == NULL) {
/*
@@ -1806,11 +2474,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
* interface selection is not going to change anymore.
*/
if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
- goto fail;
+ return;
} else if (sta->vlan_id) {
/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
- goto fail;
+ return;
}
hostapd_set_sta_flags(hapd, sta);
@@ -1822,13 +2490,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
-
- fail:
- /* Copy of the association request is not needed anymore */
- if (sta->last_assoc_req) {
- os_free(sta->last_assoc_req);
- sta->last_assoc_req = NULL;
- }
}
@@ -1895,6 +2556,14 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_mgmt_frame_handling) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
+ stype, ok);
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth cb");
@@ -1923,7 +2592,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
wpa_printf(MSG_DEBUG, "mgmt::action cb");
break;
default:
- printf("unknown mgmt cb frame subtype %d\n", stype);
+ wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
break;
}
}
@@ -2038,11 +2707,18 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
return;
if (wds && !(sta->flags & WLAN_STA_WDS)) {
+ int ret;
+ char ifname_wds[IFNAMSIZ + 1];
+
wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
"STA " MACSTR " (aid %u)",
MAC2STR(sta->addr), sta->aid);
sta->flags |= WLAN_STA_WDS;
- hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ ret = hostapd_set_wds_sta(hapd, ifname_wds,
+ sta->addr, sta->aid, 1);
+ if (!ret)
+ hostapd_set_wds_encryption(hapd, sta,
+ ifname_wds);
}
return;
}
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 1e5800d09c76b..41c27d906e72a 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -14,12 +14,14 @@ struct hostapd_data;
struct sta_info;
struct hostapd_frame_info;
struct ieee80211_ht_capabilities;
+struct ieee80211_mgmt;
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
- struct hostapd_frame_info *fi);
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+ struct hostapd_frame_info *fi);
void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
u16 stype, int ok);
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
#ifdef NEED_AP_MLME
int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
@@ -40,24 +42,37 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
int probe);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id);
void hostapd_get_ht_capab(struct hostapd_data *hapd,
struct ieee80211_ht_capabilities *ht_cap,
struct ieee80211_ht_capabilities *neg_ht_cap);
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab, size_t ht_capab_len);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len);
+
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+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, size_t vht_capab_len);
+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,
const u8 *buf, size_t len, int ack);
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -78,4 +93,15 @@ int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index c311e55949e3d..56c3ce0313d43 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -29,7 +29,7 @@
struct hostapd_cached_radius_acl {
- os_time_t timestamp;
+ struct os_reltime timestamp;
macaddr addr;
int accepted; /* HOSTAPD_ACL_* */
struct hostapd_cached_radius_acl *next;
@@ -43,7 +43,7 @@ struct hostapd_cached_radius_acl {
struct hostapd_acl_query_data {
- os_time_t timestamp;
+ struct os_reltime timestamp;
u8 radius_id;
macaddr addr;
u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -104,15 +104,16 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
char **identity, char **radius_cui)
{
struct hostapd_cached_radius_acl *entry;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
for (entry = hapd->acl_cache; entry; entry = entry->next) {
if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
continue;
- if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+ if (os_reltime_expired(&now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT))
return -1; /* entry has expired */
if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
if (session_timeout)
@@ -265,7 +266,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
return HOSTAPD_ACL_REJECT;
#else /* CONFIG_NO_RADIUS */
struct hostapd_acl_query_data *query;
- struct os_time t;
/* Check whether ACL cache has an entry for this station */
int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
@@ -305,8 +305,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
wpa_printf(MSG_ERROR, "malloc for query data failed");
return HOSTAPD_ACL_REJECT;
}
- os_get_time(&t);
- query->timestamp = t.sec;
+ os_get_reltime(&query->timestamp);
os_memcpy(query->addr, addr, ETH_ALEN);
if (hostapd_radius_acl_query(hapd, addr, query)) {
wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
@@ -338,7 +337,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
#ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+ struct os_reltime *now)
{
struct hostapd_cached_radius_acl *prev, *entry, *tmp;
@@ -346,7 +346,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
entry = hapd->acl_cache;
while (entry) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ if (os_reltime_expired(now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT)) {
wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
" has expired.", MAC2STR(entry->addr));
if (prev)
@@ -367,7 +368,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
- os_time_t now)
+ struct os_reltime *now)
{
struct hostapd_acl_query_data *prev, *entry, *tmp;
@@ -375,7 +376,8 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
entry = hapd->acl_queries;
while (entry) {
- if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+ if (os_reltime_expired(now, &entry->timestamp,
+ RADIUS_ACL_TIMEOUT)) {
wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
" has expired.", MAC2STR(entry->addr));
if (prev)
@@ -403,11 +405,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
- hostapd_acl_expire_cache(hapd, now.sec);
- hostapd_acl_expire_queries(hapd, now.sec);
+ os_get_reltime(&now);
+ hostapd_acl_expire_cache(hapd, &now);
+ hostapd_acl_expire_queries(hapd, &now);
eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
}
@@ -480,7 +482,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- struct os_time t;
query = hapd->acl_queries;
prev = NULL;
@@ -515,8 +516,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
goto done;
}
- os_get_time(&t);
- cache->timestamp = t.sec;
+ os_get_reltime(&cache->timestamp);
os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
u8 *buf;
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 6c3696f5e3698..4b0653de95dbd 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -3,26 +3,22 @@
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008, Intel Corporation
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
#include "beacon.h"
#include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -50,6 +46,35 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
pos += sizeof(*cap);
+ if (hapd->iconf->obss_interval) {
+ struct ieee80211_obss_scan_parameters *scan_params;
+
+ *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
+ *pos++ = sizeof(*scan_params);
+
+ scan_params = (struct ieee80211_obss_scan_parameters *) pos;
+ os_memset(scan_params, 0, sizeof(*scan_params));
+ scan_params->width_trigger_scan_interval =
+ host_to_le16(hapd->iconf->obss_interval);
+
+ /* Fill in default values for remaining parameters
+ * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+ scan_params->scan_passive_dwell =
+ host_to_le16(20);
+ scan_params->scan_active_dwell =
+ host_to_le16(10);
+ scan_params->scan_passive_total_per_channel =
+ host_to_le16(200);
+ scan_params->scan_active_total_per_channel =
+ host_to_le16(20);
+ scan_params->channel_transition_delay_factor =
+ host_to_le16(5);
+ scan_params->scan_activity_threshold =
+ host_to_le16(25);
+
+ pos += sizeof(*scan_params);
+ }
+
return pos;
}
@@ -68,14 +93,14 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
oper = (struct ieee80211_ht_operation *) pos;
os_memset(oper, 0, sizeof(*oper));
- oper->control_chan = hapd->iconf->channel;
+ oper->primary_chan = hapd->iconf->channel;
oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
if (hapd->iconf->secondary_channel == 1)
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
- HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
if (hapd->iconf->secondary_channel == -1)
oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
- HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+ HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
pos += sizeof(*oper);
@@ -105,45 +130,40 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
__func__, iface->ht_op_mode);
- if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
&& iface->num_sta_ht_no_gf) {
- iface->ht_op_mode |=
- HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
op_mode_changes++;
} else if ((iface->ht_op_mode &
- HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+ HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
iface->num_sta_ht_no_gf == 0) {
- iface->ht_op_mode &=
- ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
op_mode_changes++;
}
- if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
(iface->num_sta_no_ht || iface->olbc_ht)) {
- iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
op_mode_changes++;
} else if ((iface->ht_op_mode &
- HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+ HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
(iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
- iface->ht_op_mode &=
- ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
op_mode_changes++;
}
- new_op_mode = 0;
if (iface->num_sta_no_ht)
- new_op_mode = OP_MODE_MIXED;
- else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
- && iface->num_sta_ht_20mhz)
- new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+ new_op_mode = HT_PROT_NON_HT_MIXED;
+ else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
+ new_op_mode = HT_PROT_20MHZ_PROTECTION;
else if (iface->olbc_ht)
- new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+ new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
else
- new_op_mode = OP_MODE_PURE;
+ new_op_mode = HT_PROT_NO_PROTECTION;
- cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
if (cur_op_mode != new_op_mode) {
- iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+ iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
iface->ht_op_mode |= new_op_mode;
op_mode_changes++;
}
@@ -155,6 +175,140 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
}
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+ int pri_freq, sec_freq;
+ int affected_start, affected_end;
+ int pri = 2407 + 5 * channel;
+
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return 1;
+
+ pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+ if (iface->conf->secondary_channel > 0)
+ sec_freq = pri_freq + 20;
+ else
+ sec_freq = pri_freq - 20;
+
+ affected_start = (pri_freq + sec_freq) / 2 - 25;
+ affected_end = (pri_freq + sec_freq) / 2 + 25;
+ if ((pri < affected_start || pri > affected_end))
+ return 1; /* not within affected channel range */
+
+ wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+ affected_start, affected_end);
+ wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+ return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ struct ieee80211_2040_bss_coex_ie *bc_ie;
+ struct ieee80211_2040_intol_chan_report *ic_report;
+ int is_ht_allowed = 1;
+ int i;
+ const u8 *start = (const u8 *) mgmt;
+ const u8 *data = start + IEEE80211_HDRLEN + 2;
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+ mgmt->u.action.u.public_action.action);
+
+ if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return;
+
+ if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+ return;
+
+ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+ if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+ bc_ie->length < 1) {
+ wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+ bc_ie->element_id, bc_ie->length);
+ return;
+ }
+ if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+ return;
+ data += 2 + bc_ie->length;
+
+ wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
+ bc_ie->coex_param);
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20 MHz BSS width request bit is set in BSS coexistence information field");
+ is_ht_allowed = 0;
+ }
+
+ if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "40 MHz intolerant bit is set in BSS coexistence information field");
+ is_ht_allowed = 0;
+ }
+
+ if (start + len - data >= 3 &&
+ data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+ u8 ielen = data[1];
+
+ if (ielen > start + len - data - 2)
+ return;
+ ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+ wpa_printf(MSG_DEBUG,
+ "20/40 BSS Intolerant Channel Report: Operating Class %u",
+ ic_report->op_class);
+
+ /* Go through the channel report to find any BSS there in the
+ * affected channel range */
+ for (i = 0; i < ielen - 1; i++) {
+ u8 chan = ic_report->variable[i];
+
+ if (is_40_allowed(iface, chan))
+ continue;
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "20_40_INTOLERANT channel %d reported",
+ chan);
+ is_ht_allowed = 0;
+ }
+ }
+ wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d",
+ is_ht_allowed, iface->num_sta_ht40_intolerant);
+
+ if (!is_ht_allowed &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ if (iface->conf->secondary_channel) {
+ hostapd_logger(hapd, mgmt->sa,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Switching to 20 MHz operation");
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+ if (!iface->num_sta_ht40_intolerant &&
+ iface->conf->obss_interval) {
+ unsigned int delay_time;
+ delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+ NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ hapd->iface, NULL);
+ wpa_printf(MSG_DEBUG,
+ "Reschedule HT 20/40 timeout to occur in %u seconds",
+ delay_time);
+ }
+ }
+}
+
+
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab, size_t ht_capab_len)
{
@@ -183,6 +337,52 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
}
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+ return;
+
+ wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+ " in Association Request", MAC2STR(sta->addr));
+
+ if (sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 1;
+ iface->num_sta_ht40_intolerant++;
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+ if (iface->conf->secondary_channel &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ iface->conf->secondary_channel = 0;
+ ieee802_11_set_beacons(iface);
+ }
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+ if (!sta->ht40_intolerant_set)
+ return;
+
+ sta->ht40_intolerant_set = 0;
+ iface->num_sta_ht40_intolerant--;
+
+ if (iface->num_sta_ht40_intolerant == 0 &&
+ (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+ (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+ unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+ iface->conf->obss_interval;
+ wpa_printf(MSG_DEBUG,
+ "HT: Start 20->40 MHz transition timer (%d seconds)",
+ delay_time);
+ eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+ eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+ iface, NULL);
+ }
+}
+
+
static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
{
u16 ht_capab;
@@ -210,6 +410,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
__func__, MAC2STR(sta->addr),
hapd->iface->num_sta_ht_20mhz);
}
+
+ if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+ ht40_intolerant_add(hapd->iface, sta);
}
@@ -271,3 +474,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
}
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+ struct hostapd_iface *iface = eloop_data;
+
+ wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+ iface->conf->secondary_channel = iface->secondary_ch;
+ ieee802_11_set_beacons(iface);
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 76f78a7dbd9c1..d462ac8bf9cde 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -24,13 +24,13 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
{
u8 *pos = eid;
u32 timeout, tu;
- struct os_time now, passed;
+ struct os_reltime now, passed;
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &passed);
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
if (hapd->conf->assoc_sa_query_max_timeout > tu)
timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
@@ -69,7 +69,7 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
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)
- perror("ieee802_11_send_sa_query_req: send");
+ wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
}
@@ -107,7 +107,7 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
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)
- perror("ieee80211_mgmt_sa_query_request: send");
+ wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
}
@@ -164,10 +164,62 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
#endif /* CONFIG_IEEE80211W */
+static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
+{
+ *pos = 0x00;
+
+ switch (idx) {
+ case 0: /* Bits 0-7 */
+ if (hapd->iconf->obss_interval)
+ *pos |= 0x01; /* Bit 0 - Coexistence management */
+ break;
+ case 1: /* Bits 8-15 */
+ if (hapd->conf->proxy_arp)
+ *pos |= 0x10; /* Bit 12 - Proxy ARP */
+ break;
+ case 2: /* Bits 16-23 */
+ if (hapd->conf->wnm_sleep_mode)
+ *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
+ if (hapd->conf->bss_transition)
+ *pos |= 0x08; /* Bit 19 - BSS Transition */
+ break;
+ case 3: /* Bits 24-31 */
+#ifdef CONFIG_WNM
+ *pos |= 0x02; /* Bit 25 - SSID List */
+#endif /* CONFIG_WNM */
+ if (hapd->conf->time_advertisement == 2)
+ *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
+ if (hapd->conf->interworking)
+ *pos |= 0x80; /* Bit 31 - Interworking */
+ break;
+ case 4: /* Bits 32-39 */
+ if (hapd->conf->qos_map_set_len)
+ *pos |= 0x01; /* Bit 32 - QoS Map */
+ if (hapd->conf->tdls & TDLS_PROHIBIT)
+ *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
+ if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
+ /* Bit 39 - TDLS Channel Switching Prohibited */
+ *pos |= 0x80;
+ }
+ break;
+ case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20)
+ *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
+ break;
+ case 6: /* Bits 48-55 */
+ if (hapd->conf->ssid.utf8_ssid)
+ *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
+ break;
+ }
+}
+
+
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
- u8 len = 0;
+ u8 len = 0, i;
if (hapd->conf->tdls & (TDLS_PROHIBIT | TDLS_PROHIBIT_CHAN_SWITCH))
len = 5;
@@ -175,59 +227,57 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
len = 4;
if (len < 3 && hapd->conf->wnm_sleep_mode)
len = 3;
+ if (len < 1 && hapd->iconf->obss_interval)
+ len = 1;
if (len < 7 && hapd->conf->ssid.utf8_ssid)
len = 7;
#ifdef CONFIG_WNM
if (len < 4)
len = 4;
#endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20 && len < 6)
+ len = 6;
+#endif /* CONFIG_HS20 */
+ if (len < hapd->iface->extended_capa_len)
+ len = hapd->iface->extended_capa_len;
if (len == 0)
return eid;
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
- *pos++ = 0x00;
- *pos++ = 0x00;
+ for (i = 0; i < len; i++, pos++) {
+ hostapd_ext_capab_byte(hapd, pos, i);
- *pos = 0x00;
- if (hapd->conf->wnm_sleep_mode)
- *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
- if (hapd->conf->bss_transition)
- *pos |= 0x08; /* Bit 19 - BSS Transition */
- pos++;
+ if (i < hapd->iface->extended_capa_len) {
+ *pos &= ~hapd->iface->extended_capa_mask[i];
+ *pos |= hapd->iface->extended_capa[i];
+ }
+ }
- if (len < 4)
- return pos;
- *pos = 0x00;
-#ifdef CONFIG_WNM
- *pos |= 0x02; /* Bit 25 - SSID List */
-#endif /* CONFIG_WNM */
- if (hapd->conf->time_advertisement == 2)
- *pos |= 0x08; /* Bit 27 - UTC TSF Offset */
- if (hapd->conf->interworking)
- *pos |= 0x80; /* Bit 31 - Interworking */
- pos++;
+ while (len > 0 && eid[1 + len] == 0) {
+ len--;
+ eid[1] = len;
+ }
+ if (len == 0)
+ return eid;
- if (len < 5)
- return pos;
- *pos = 0x00;
- if (hapd->conf->tdls & TDLS_PROHIBIT)
- *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
- if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH)
- *pos |= 0x80; /* Bit 39 - TDLS Channel Switching Prohibited */
- pos++;
+ return eid + 2 + len;
+}
- if (len < 6)
- return pos;
- *pos = 0x00;
- pos++;
- if (len < 7)
- return pos;
- *pos = 0x00;
- if (hapd->conf->ssid.utf8_ssid)
- *pos |= 0x01; /* Bit 48 - UTF-8 SSID */
- pos++;
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+ u8 len = hapd->conf->qos_map_set_len;
+
+ if (!len)
+ return eid;
+
+ *pos++ = WLAN_EID_QOS_MAP_SET;
+ *pos++ = len;
+ os_memcpy(pos, hapd->conf->qos_map_set, len);
+ pos += len;
return pos;
}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index b21c2b7fb0d22..171538ad74a7a 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -12,7 +12,6 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_config.h"
#include "sta_info.h"
@@ -23,23 +22,35 @@
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_vht_capabilities *cap;
+ struct hostapd_hw_modes *mode = hapd->iface->current_mode;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
- hapd->conf->disable_11ac)
+ if (!mode)
return eid;
+ if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+ mode->vht_capab == 0 && hapd->iface->hw_features) {
+ int i;
+
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ if (hapd->iface->hw_features[i].mode ==
+ HOSTAPD_MODE_IEEE80211A) {
+ mode = &hapd->iface->hw_features[i];
+ break;
+ }
+ }
+ }
+
*pos++ = WLAN_EID_VHT_CAP;
*pos++ = sizeof(*cap);
cap = (struct ieee80211_vht_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
cap->vht_capabilities_info = host_to_le32(
- hapd->iface->current_mode->vht_capab);
+ hapd->iface->conf->vht_capab);
/* Supported MCS set comes from hw */
- os_memcpy(cap->vht_supported_mcs_set,
- hapd->iface->current_mode->vht_mcs_set, 8);
+ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
pos += sizeof(*cap);
@@ -52,9 +63,6 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
struct ieee80211_vht_operation *oper;
u8 *pos = eid;
- if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
- return eid;
-
*pos++ = WLAN_EID_VHT_OPERATION;
*pos++ = sizeof(*oper);
@@ -82,13 +90,55 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
}
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+ const u8 *sta_vht_capab)
+{
+ const struct ieee80211_vht_capabilities *vht_cap;
+ struct ieee80211_vht_capabilities ap_vht_cap;
+ u16 sta_rx_mcs_set, ap_tx_mcs_set;
+ int i;
+
+ if (!mode)
+ return 1;
+
+ /*
+ * Disable VHT caps for STAs for which there is not even a single
+ * allowed MCS in any supported number of streams, i.e., STA is
+ * advertising 3 (not supported) as VHT MCS rates for all supported
+ * stream cases.
+ */
+ os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+ sizeof(ap_vht_cap.vht_supported_mcs_set));
+ vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+ /* AP Tx MCS map vs. STA Rx MCS map */
+ sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+ ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+ for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+ if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+ continue;
+
+ if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+ continue;
+
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching VHT MCS found between AP TX and STA RX");
+ return 0;
+}
+
+
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_capab, size_t vht_capab_len)
{
/* Disable VHT caps for STAs associated to no-VHT BSSes. */
if (!vht_capab ||
vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
- hapd->conf->disable_11ac) {
+ hapd->conf->disable_11ac ||
+ !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
sta->flags &= ~WLAN_STA_VHT;
os_free(sta->vht_capabilities);
sta->vht_capabilities = NULL;
@@ -108,3 +158,140 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_SUCCESS;
}
+
+
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *ie, size_t len)
+{
+ const u8 *vht_capab;
+ unsigned int vht_capab_len;
+
+ if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+ hapd->conf->disable_11ac)
+ goto no_capab;
+
+ /* The VHT Capabilities element embedded in vendor VHT */
+ vht_capab = ie + 5;
+ if (vht_capab[0] != WLAN_EID_VHT_CAP)
+ goto no_capab;
+ vht_capab_len = vht_capab[1];
+ if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+ (int) vht_capab_len > ie + len - vht_capab - 2)
+ goto no_capab;
+ vht_capab += 2;
+
+ if (sta->vht_capabilities == NULL) {
+ sta->vht_capabilities =
+ os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+ if (sta->vht_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+ os_memcpy(sta->vht_capabilities, vht_capab,
+ sizeof(struct ieee80211_vht_capabilities));
+ return WLAN_STATUS_SUCCESS;
+
+no_capab:
+ sta->flags &= ~WLAN_STA_VENDOR_VHT;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 *pos = eid;
+
+ if (!hapd->iface->current_mode)
+ return eid;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = (5 + /* The Vendor OUI, type and subtype */
+ 2 + sizeof(struct ieee80211_vht_capabilities) +
+ 2 + sizeof(struct ieee80211_vht_operation));
+
+ WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+ pos += 4;
+ *pos++ = VENDOR_VHT_SUBTYPE;
+ pos = hostapd_eid_vht_capabilities(hapd, pos);
+ pos = hostapd_eid_vht_operation(hapd, pos);
+
+ return pos;
+}
+
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper_notif)
+{
+ if (!vht_oper_notif) {
+ sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+ sta->vht_opmode = *vht_oper_notif;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+void hostapd_get_vht_capab(struct hostapd_data *hapd,
+ struct ieee80211_vht_capabilities *vht_cap,
+ struct ieee80211_vht_capabilities *neg_vht_cap)
+{
+ u32 cap, own_cap, sym_caps;
+
+ if (vht_cap == NULL)
+ return;
+ os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
+
+ cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
+ own_cap = hapd->iconf->vht_capab;
+
+ /* mask out symmetric VHT capabilities we don't support */
+ sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
+ cap &= ~sym_caps | (own_cap & sym_caps);
+
+ /* mask out beamformer/beamformee caps if not supported */
+ if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+ cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+ VHT_CAP_BEAMFORMEE_STS_MAX);
+
+ if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+ cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
+ VHT_CAP_SOUNDING_DIMENSION_MAX);
+
+ if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+ cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+ if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
+ cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+ /* mask channel widths we don't support */
+ switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+ case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+ break;
+ case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+ if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+ cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+ break;
+ default:
+ cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ break;
+ }
+
+ if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+ cap &= ~VHT_CAP_SHORT_GI_160;
+
+ /*
+ * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+ * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+ */
+ if (!(own_cap & VHT_CAP_RXSTBC_MASK))
+ cap &= ~VHT_CAP_TXSTBC;
+ if (!(own_cap & VHT_CAP_TXSTBC))
+ cap &= ~VHT_CAP_RXSTBC_MASK;
+
+ neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d9c21a8b8117f..79dc0f9574880 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -29,11 +29,14 @@
#include "pmksa_cache_auth.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
#include "ieee802_1x.h"
static void ieee802_1x_finished(struct hostapd_data *hapd,
- struct sta_info *sta, int success);
+ struct sta_info *sta, int success,
+ int remediation);
static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -63,6 +66,20 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
if (wpa_auth_pairwise_set(sta->wpa_sm))
encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io) {
+ size_t hex_len = 2 * len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex) {
+ wpa_snprintf_hex(hex, hex_len, buf, len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ "EAPOL-TX " MACSTR " %s",
+ MAC2STR(sta->addr), hex);
+ os_free(hex);
+ }
+ } else
+#endif /* CONFIG_TESTING_OPTIONS */
if (sta->flags & WLAN_STA_PREAUTH) {
rsn_preauth_send(hapd, sta, buf, len);
} else {
@@ -96,12 +113,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
}
if (res && errno != ENOENT) {
- printf("Could not set station " MACSTR " flags for kernel "
- "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+ wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+ " flags for kernel driver (errno=%d).",
+ MAC2STR(sta->addr), errno);
}
if (authorized) {
- os_get_time(&sta->connected_time);
+ os_get_reltime(&sta->connected_time);
accounting_sta_start(hapd, sta);
}
}
@@ -186,114 +204,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
}
-#ifndef CONFIG_NO_VLAN
-static struct hostapd_wep_keys *
-ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
-{
- struct hostapd_wep_keys *key;
-
- key = os_zalloc(sizeof(*key));
- if (key == NULL)
- return NULL;
-
- key->default_len = hapd->conf->default_wep_key_len;
-
- if (key->idx >= hapd->conf->broadcast_key_idx_max ||
- key->idx < hapd->conf->broadcast_key_idx_min)
- key->idx = hapd->conf->broadcast_key_idx_min;
- else
- key->idx++;
-
- if (!key->key[key->idx])
- key->key[key->idx] = os_malloc(key->default_len);
- if (key->key[key->idx] == NULL ||
- random_get_bytes(key->key[key->idx], key->default_len)) {
- printf("Could not generate random WEP key (dynamic VLAN).\n");
- os_free(key->key[key->idx]);
- key->key[key->idx] = NULL;
- os_free(key);
- return NULL;
- }
- key->len[key->idx] = key->default_len;
-
- wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n",
- ifname, key->idx);
- wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
- key->key[key->idx], key->len[key->idx]);
-
- if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
- broadcast_ether_addr, key->idx, 1,
- NULL, 0, key->key[key->idx],
- key->len[key->idx]))
- printf("Could not set dynamic VLAN WEP encryption key.\n");
-
- hostapd_set_drv_ieee8021x(hapd, ifname, 1);
-
- return key;
-}
-
-
-static struct hostapd_wep_keys *
-ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid,
- size_t vlan_id)
-{
- const char *ifname;
-
- if (vlan_id == 0)
- return &ssid->wep;
-
- if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys &&
- ssid->dyn_vlan_keys[vlan_id])
- return ssid->dyn_vlan_keys[vlan_id];
-
- wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group "
- "state machine for VLAN ID %lu",
- (unsigned long) vlan_id);
-
- ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
- if (ifname == NULL) {
- wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - "
- "cannot create group key state machine",
- (unsigned long) vlan_id);
- return NULL;
- }
-
- if (ssid->dyn_vlan_keys == NULL) {
- int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
- ssid->dyn_vlan_keys = os_zalloc(size);
- if (ssid->dyn_vlan_keys == NULL)
- return NULL;
- ssid->max_dyn_vlan_keys = vlan_id;
- }
-
- if (ssid->max_dyn_vlan_keys < vlan_id) {
- struct hostapd_wep_keys **na;
- int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
- na = os_realloc(ssid->dyn_vlan_keys, size);
- if (na == NULL)
- return NULL;
- ssid->dyn_vlan_keys = na;
- os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0,
- (vlan_id - ssid->max_dyn_vlan_keys) *
- sizeof(ssid->dyn_vlan_keys[0]));
- ssid->max_dyn_vlan_keys = vlan_id;
- }
-
- ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname);
-
- return ssid->dyn_vlan_keys[vlan_id];
-}
-#endif /* CONFIG_NO_VLAN */
-
-
void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
{
struct eapol_authenticator *eapol = hapd->eapol_auth;
struct eapol_state_machine *sm = sta->eapol_sm;
-#ifndef CONFIG_NO_VLAN
- struct hostapd_wep_keys *key = NULL;
- int vlan_id;
-#endif /* CONFIG_NO_VLAN */
if (sm == NULL || !sm->eap_if->eapKeyData)
return;
@@ -302,18 +216,12 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
MAC2STR(sta->addr));
#ifndef CONFIG_NO_VLAN
- vlan_id = sta->vlan_id;
- if (vlan_id < 0 || vlan_id > MAX_VLAN_ID)
- vlan_id = 0;
-
- if (vlan_id) {
- key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id);
- if (key && key->key[key->idx])
- ieee802_1x_tx_key_one(hapd, sta, key->idx, 1,
- key->key[key->idx],
- key->len[key->idx]);
- } else
+ if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
+ wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+ return;
+ }
#endif /* CONFIG_NO_VLAN */
+
if (eapol->default_wep_key) {
ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
eapol->default_wep_key,
@@ -388,9 +296,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
{
const u8 *identity;
size_t identity_len;
+ const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
if (len <= sizeof(struct eap_hdr) ||
- eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
+ (hdr->code == EAP_CODE_RESPONSE &&
+ eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+ (hdr->code == EAP_CODE_INITIATE &&
+ eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+ (hdr->code != EAP_CODE_RESPONSE &&
+ hdr->code != EAP_CODE_INITIATE))
return;
identity = eap_get_identity(sm->eap, &identity_len);
@@ -399,21 +313,80 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
/* Save station identity for future RADIUS packets */
os_free(sm->identity);
- sm->identity = os_malloc(identity_len + 1);
+ sm->identity = (u8 *) dup_binstr(identity, identity_len);
if (sm->identity == NULL) {
sm->identity_len = 0;
return;
}
- os_memcpy(sm->identity, identity, identity_len);
sm->identity_len = identity_len;
- sm->identity[identity_len] = '\0';
hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity);
sm->dot1xAuthEapolRespIdFramesRx++;
}
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+ struct hostapd_radius_attr *req_attr,
+ struct sta_info *sta,
+ struct radius_msg *msg)
+{
+ u32 suite;
+ int ver, val;
+
+ ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+ val = wpa_auth_get_pairwise(sta->wpa_sm);
+ suite = wpa_cipher_to_suite(ver, val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+ return -1;
+ }
+
+ suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
+ WPA_PROTO_RSN : WPA_PROTO_WPA,
+ hapd->conf->wpa_group);
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+ return -1;
+ }
+
+ val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+ suite = wpa_akm_to_suite(val);
+ if (val != -1 &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_AKM_SUITE) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+ suite)) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
+ hapd->conf->group_mgmt_cipher);
+ if (!hostapd_config_get_radius_attr(
+ req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+ !radius_msg_add_attr_int32(
+ msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+ wpa_printf(MSG_ERROR,
+ "Could not add WLAN-Group-Mgmt-Cipher");
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
static int add_common_radius_sta_attr(struct hostapd_data *hapd,
struct hostapd_radius_attr *req_attr,
struct sta_info *sta,
@@ -465,6 +438,25 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
}
}
+#ifdef CONFIG_IEEE80211R
+ if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+ sta->wpa_sm &&
+ (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+ sta->auth_alg == WLAN_AUTH_FT) &&
+ !hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+ !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+ WPA_GET_BE16(
+ hapd->conf->mobility_domain))) {
+ wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+ return -1;
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ if (hapd->conf->wpa && sta->wpa_sm &&
+ add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+ return -1;
+
return 0;
}
@@ -528,6 +520,22 @@ int add_common_radius_attr(struct hostapd_data *hapd,
return -1;
}
+#ifdef CONFIG_INTERWORKING
+ if (hapd->conf->interworking &&
+ !is_zero_ether_addr(hapd->conf->hessid)) {
+ os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+ MAC2STR(hapd->conf->hessid));
+ buf[sizeof(buf) - 1] = '\0';
+ if (!hostapd_config_get_radius_attr(req_attr,
+ RADIUS_ATTR_WLAN_HESSID) &&
+ !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+ (u8 *) buf, os_strlen(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+ return -1;
+ }
+ }
+#endif /* CONFIG_INTERWORKING */
+
if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
return -1;
@@ -564,7 +572,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
sm->radius_identifier);
if (msg == NULL) {
- printf("Could not create net RADIUS packet\n");
+ wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
return;
}
@@ -573,7 +581,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
if (sm->identity &&
!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
sm->identity, sm->identity_len)) {
- printf("Could not add User-Name\n");
+ wpa_printf(MSG_INFO, "Could not add User-Name");
goto fail;
}
@@ -587,12 +595,12 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
RADIUS_ATTR_FRAMED_MTU) &&
!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
- printf("Could not add Framed-MTU\n");
+ wpa_printf(MSG_INFO, "Could not add Framed-MTU");
goto fail;
}
if (eap && !radius_msg_add_eap(msg, eap, len)) {
- printf("Could not add EAP-Message\n");
+ wpa_printf(MSG_INFO, "Could not add EAP-Message");
goto fail;
}
@@ -604,8 +612,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
RADIUS_ATTR_STATE);
if (res < 0) {
- printf("Could not copy State attribute from previous "
- "Access-Challenge\n");
+ wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
goto fail;
}
if (res > 0) {
@@ -632,6 +639,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
}
}
+#ifdef CONFIG_HS20
+ if (hapd->conf->hs20) {
+ u8 ver = 1; /* Release 2 */
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+ &ver, 1)) {
+ wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP "
+ "version");
+ goto fail;
+ }
+
+ if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+ const u8 *pos;
+ u8 buf[3];
+ u16 id;
+ pos = wpabuf_head_u8(sta->hs20_ie);
+ buf[0] = (*pos) >> 4;
+ if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
+ wpabuf_len(sta->hs20_ie) >= 3)
+ id = WPA_GET_LE16(pos + 1);
+ else
+ id = 0;
+ WPA_PUT_BE16(buf + 1, id);
+ if (!radius_msg_add_wfa(
+ msg,
+ RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+ buf, sizeof(buf))) {
+ wpa_printf(MSG_ERROR, "Could not add HS 2.0 "
+ "STA version");
+ goto fail;
+ }
+ }
+ }
+#endif /* CONFIG_HS20 */
+
if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
goto fail;
@@ -655,7 +697,7 @@ static void handle_eap_response(struct hostapd_data *hapd,
data = (u8 *) (eap + 1);
if (len < sizeof(*eap) + 1) {
- printf("handle_eap_response: too short response data\n");
+ wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
return;
}
@@ -675,6 +717,39 @@ static void handle_eap_response(struct hostapd_data *hapd,
}
+static void handle_eap_initiate(struct hostapd_data *hapd,
+ struct sta_info *sta, struct eap_hdr *eap,
+ size_t len)
+{
+#ifdef CONFIG_ERP
+ u8 type, *data;
+ struct eapol_state_machine *sm = sta->eapol_sm;
+
+ if (sm == NULL)
+ return;
+
+ if (len < sizeof(*eap) + 1) {
+ wpa_printf(MSG_INFO,
+ "handle_eap_initiate: too short response data");
+ return;
+ }
+
+ data = (u8 *) (eap + 1);
+ type = data[0];
+
+ hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+ "id=%d len=%d) from STA: EAP Initiate type %u",
+ eap->code, eap->identifier, be_to_host16(eap->length),
+ type);
+
+ wpabuf_free(sm->eap_if->eapRespData);
+ sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+ sm->eapolEap = TRUE;
+#endif /* CONFIG_ERP */
+}
+
+
/* Process incoming EAP packet from Supplicant */
static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len)
@@ -683,7 +758,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
u16 eap_len;
if (len < sizeof(*eap)) {
- printf(" too short EAP packet\n");
+ wpa_printf(MSG_INFO, " too short EAP packet");
return;
}
@@ -718,6 +793,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
case EAP_CODE_FAILURE:
wpa_printf(MSG_DEBUG, " (failure)");
return;
+ case EAP_CODE_INITIATE:
+ wpa_printf(MSG_DEBUG, " (initiate)");
+ handle_eap_initiate(hapd, sta, eap, eap_len);
+ break;
+ case EAP_CODE_FINISH:
+ wpa_printf(MSG_DEBUG, " (finish)");
+ break;
default:
wpa_printf(MSG_DEBUG, " (unknown code)");
return;
@@ -761,7 +843,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
struct rsn_pmksa_cache_entry *pmksa;
int key_mgmt;
- if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+ if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
!hapd->conf->wps_state)
return;
@@ -776,7 +858,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
}
if (len < sizeof(*hdr)) {
- printf(" too short IEEE 802.1X packet\n");
+ wpa_printf(MSG_INFO, " too short IEEE 802.1X packet");
return;
}
@@ -786,7 +868,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
hdr->version, hdr->type, datalen);
if (len - sizeof(*hdr) < datalen) {
- printf(" frame too short for this IEEE 802.1X packet\n");
+ wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet");
if (sta->eapol_sm)
sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
return;
@@ -812,7 +894,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
}
- if (!hapd->conf->ieee802_1x &&
+ if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
!(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
"802.1X not enabled and WPS not used");
@@ -832,7 +914,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
return;
#ifdef CONFIG_WPS
- if (!hapd->conf->ieee802_1x) {
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
u32 wflags = sta->flags & (WLAN_STA_WPS |
WLAN_STA_WPS2 |
WLAN_STA_MAYBE_WPS);
@@ -939,8 +1021,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
int key_mgmt;
#ifdef CONFIG_WPS
- if (hapd->conf->wps_state && hapd->conf->wpa &&
- (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+ if (hapd->conf->wps_state &&
+ ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+ (sta->flags & WLAN_STA_WPS))) {
/*
* Need to enable IEEE 802.1X/EAPOL state machines for possible
* WPS handshake even if IEEE 802.1X/EAPOL is not used for
@@ -950,7 +1033,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
}
#endif /* CONFIG_WPS */
- if (!force_1x && !hapd->conf->ieee802_1x) {
+ if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
"802.1X not enabled or forced for WPS");
/*
@@ -988,7 +1071,8 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
#ifdef CONFIG_WPS
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
- if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
+ if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+ !(sta->flags & WLAN_STA_WPS2)) {
/*
* Delay EAPOL frame transmission until a possible WPS STA
* initiates the handshake with EAPOL-Start. Only allow the
@@ -1127,15 +1211,11 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
if (eap_type >= 0)
sm->eap_type_authsrv = eap_type;
os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
- eap_type >= 0 ? eap_server_get_name(0, eap_type) :
- "??",
- eap_type);
+ eap_server_get_name(0, eap_type), eap_type);
break;
case EAP_CODE_RESPONSE:
os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
- eap_type >= 0 ? eap_server_get_name(0, eap_type) :
- "??",
- eap_type);
+ eap_server_get_name(0, eap_type), eap_type);
break;
case EAP_CODE_SUCCESS:
os_strlcpy(buf, "EAP Success", sizeof(buf));
@@ -1191,6 +1271,11 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd,
sm->eap_if->aaaEapKeyDataLen = len;
sm->eap_if->aaaEapKeyAvailable = TRUE;
}
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "MS-MPPE: 1x_get_keys, could not get keys: %p send: %p recv: %p",
+ keys, keys ? keys->send : NULL,
+ keys ? keys->recv : NULL);
}
if (keys) {
@@ -1272,13 +1357,10 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd,
NULL) < 0)
return;
- identity = os_malloc(len + 1);
+ identity = (u8 *) dup_binstr(buf, len);
if (identity == NULL)
return;
- os_memcpy(identity, buf, len);
- identity[len] = '\0';
-
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with "
"User-Name from Access-Accept '%s'",
@@ -1317,6 +1399,147 @@ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
}
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ if (len > 2) {
+ sta->remediation_url = os_malloc(len);
+ if (!sta->remediation_url)
+ return;
+ sta->remediation_method = pos[0];
+ os_memcpy(sta->remediation_url, pos + 1, len - 1);
+ sta->remediation_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+ "for " MACSTR " - server method %u URL %s",
+ MAC2STR(sta->addr), sta->remediation_method,
+ sta->remediation_url);
+ } else {
+ sta->remediation_url = NULL;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+ "for " MACSTR, MAC2STR(sta->addr));
+ }
+ /* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len)
+{
+ if (len < 3)
+ return; /* Malformed information */
+ sta->hs20_deauth_requested = 1;
+ wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u "
+ "Re-auth Delay %u",
+ *pos, WPA_GET_LE16(pos + 1));
+ wpabuf_free(sta->hs20_deauth_req);
+ sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+ if (sta->hs20_deauth_req) {
+ wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+ wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
+ wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+ }
+ ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *pos,
+ size_t len, int session_timeout)
+{
+ unsigned int swt;
+ int warning_time, beacon_int;
+
+ if (len < 1)
+ return; /* Malformed information */
+ os_free(sta->hs20_session_info_url);
+ sta->hs20_session_info_url = os_malloc(len);
+ if (sta->hs20_session_info_url == NULL)
+ return;
+ swt = pos[0];
+ os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
+ sta->hs20_session_info_url[len - 1] = '\0';
+ wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u "
+ "(session_timeout=%d)",
+ sta->hs20_session_info_url, swt, session_timeout);
+ if (session_timeout < 0) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL");
+ return;
+ }
+ if (swt == 255)
+ swt = 1; /* Use one minute as the AP selected value */
+
+ if ((unsigned int) session_timeout < swt * 60)
+ warning_time = 0;
+ else
+ warning_time = session_timeout - swt * 60;
+
+ beacon_int = hapd->iconf->beacon_int;
+ if (beacon_int < 1)
+ beacon_int = 100; /* best guess */
+ sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+ if (sta->hs20_disassoc_timer > 65535)
+ sta->hs20_disassoc_timer = 65535;
+
+ ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ struct radius_msg *msg,
+ int session_timeout)
+{
+#ifdef CONFIG_HS20
+ u8 *buf, *pos, *end, type, sublen;
+ size_t len;
+
+ buf = NULL;
+ sta->remediation = 0;
+ sta->hs20_deauth_requested = 0;
+
+ for (;;) {
+ if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ break;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ switch (type) {
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+ ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+ ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+ break;
+ case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+ ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+ session_timeout);
+ break;
+ }
+ }
+#endif /* CONFIG_HS20 */
+}
+
+
struct sta_id_search {
u8 identifier;
struct eapol_state_machine *sm;
@@ -1391,15 +1614,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
"EAP-Message");
} else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
req, 1)) {
- printf("Incoming RADIUS packet did not have correct "
- "Message-Authenticator - dropped\n");
+ wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
return RADIUS_RX_INVALID_AUTHENTICATOR;
}
if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
hdr->code != RADIUS_CODE_ACCESS_REJECT &&
hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
- printf("Unknown RADIUS message code\n");
+ wpa_printf(MSG_INFO, "Unknown RADIUS message code");
return RADIUS_RX_UNKNOWN;
}
@@ -1443,8 +1665,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
sta->vlan_id = radius_msg_get_vlanid(msg);
}
if (sta->vlan_id > 0 &&
- hostapd_get_vlan_id_ifname(hapd->conf->vlan,
- sta->vlan_id)) {
+ hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -1463,6 +1684,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
break;
+ sta->session_timeout_set = !!session_timeout_set;
+ sta->session_timeout = session_timeout;
+
/* RFC 3580, Ch. 3.17 */
if (session_timeout_set && termination_action ==
RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
@@ -1477,7 +1701,11 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
ieee802_1x_store_radius_class(hapd, sta, msg);
ieee802_1x_update_sta_identity(hapd, sta, msg);
ieee802_1x_update_sta_cui(hapd, sta, msg);
- if (sm->eap_if->eapKeyAvailable &&
+ ieee802_1x_check_hs20(hapd, sta, msg,
+ session_timeout_set ?
+ (int) session_timeout : -1);
+ if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
session_timeout_set ?
(int) session_timeout : -1, sm) == 0) {
@@ -1564,7 +1792,7 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
if (eapol->default_wep_key == NULL ||
random_get_bytes(eapol->default_wep_key,
hapd->conf->default_wep_key_len)) {
- printf("Could not generate random WEP key.\n");
+ wpa_printf(MSG_INFO, "Could not generate random WEP key");
os_free(eapol->default_wep_key);
eapol->default_wep_key = NULL;
return -1;
@@ -1680,14 +1908,14 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
- int preauth)
+ int preauth, int remediation)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
if (preauth)
rsn_preauth_finished(hapd, sta, success);
else
- ieee802_1x_finished(hapd, sta, success);
+ ieee802_1x_finished(hapd, sta, success, remediation);
}
@@ -1720,7 +1948,9 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
user->password_hash = eap_user->password_hash;
}
user->force_version = eap_user->force_version;
+ user->macacl = eap_user->macacl;
user->ttls_auth = eap_user->ttls_auth;
+ user->remediation = eap_user->remediation;
return 0;
}
@@ -1804,12 +2034,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
}
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+ struct hostapd_data *hapd = ctx;
+ struct eap_server_erp_key *erp;
+
+ dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+ list) {
+ if (os_strcmp(erp->keyname_nai, keyname) == 0)
+ return erp;
+ }
+
+ return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+ struct hostapd_data *hapd = ctx;
+
+ dl_list_add(&hapd->erp_keys, &erp->list);
+ return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
int ieee802_1x_init(struct hostapd_data *hapd)
{
int i;
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
+ dl_list_init(&hapd->erp_keys);
+
os_memset(&conf, 0, sizeof(conf));
conf.ctx = hapd;
conf.eap_reauth_period = hapd->conf->eap_reauth_period;
@@ -1821,6 +2082,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
conf.eap_req_id_text = hapd->conf->eap_req_id_text;
conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+ conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+ conf.erp_domain = hapd->conf->erp_domain;
+ conf.erp = hapd->conf->eap_server_erp;
conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@@ -1834,6 +2098,13 @@ int ieee802_1x_init(struct hostapd_data *hapd)
conf.fragment_size = hapd->conf->fragment_size;
conf.pwd_group = hapd->conf->pwd_group;
conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
+ if (hapd->conf->server_id) {
+ conf.server_id = (const u8 *) hapd->conf->server_id;
+ conf.server_id_len = os_strlen(hapd->conf->server_id);
+ } else {
+ conf.server_id = (const u8 *) "hostapd";
+ conf.server_id_len = 7;
+ }
os_memset(&cb, 0, sizeof(cb));
cb.eapol_send = ieee802_1x_eapol_send;
@@ -1846,6 +2117,10 @@ int ieee802_1x_init(struct hostapd_data *hapd)
cb.abort_auth = _ieee802_1x_abort_auth;
cb.tx_key = _ieee802_1x_tx_key;
cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+ cb.erp_get_key = ieee802_1x_erp_get_key;
+ cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
hapd->eapol_auth = eapol_auth_init(&conf, &cb);
if (hapd->eapol_auth == NULL)
@@ -1877,6 +2152,18 @@ int ieee802_1x_init(struct hostapd_data *hapd)
}
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+ struct eap_server_erp_key *erp;
+
+ while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+ list)) != NULL) {
+ dl_list_del(&erp->list);
+ bin_clear_free(erp, sizeof(*erp));
+ }
+}
+
+
void ieee802_1x_deinit(struct hostapd_data *hapd)
{
eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
@@ -1887,6 +2174,8 @@ void ieee802_1x_deinit(struct hostapd_data *hapd)
eapol_auth_deinit(hapd->eapol_auth);
hapd->eapol_auth = NULL;
+
+ ieee802_1x_erp_flush(hapd);
}
@@ -2061,7 +2350,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
{
int len = 0, ret;
struct eapol_state_machine *sm = sta->eapol_sm;
- struct os_time t;
+ struct os_reltime diff;
+ const char *name1;
+ const char *name2;
if (sm == NULL)
return 0;
@@ -2075,7 +2366,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
sta->aid,
EAPOL_VERSION,
sm->initialize);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2103,7 +2394,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
sm->reAuthPeriod,
bool_txt(sm->reAuthEnabled),
bool_txt(sm->keyTxEnabled));
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2133,7 +2424,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
sm->dot1xAuthEapLengthErrorFramesRx,
sm->dot1xAuthLastEapolFrameVersion,
MAC2STR(sm->addr));
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2171,12 +2462,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
sm->backendOtherRequestsToSupplicant,
sm->backendAuthSuccesses,
sm->backendAuthFails);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
/* dot1xAuthSessionStatsTable */
- os_get_time(&t);
+ os_reltime_age(&sta->acct_session_start, &diff);
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -2191,9 +2482,30 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
(wpa_key_mgmt_wpa_ieee8021x(
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
- (unsigned int) (t.sec - sta->acct_session_start),
+ (unsigned int) diff.sec,
sm->identity);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+
+ if (sm->acct_multi_session_id_hi) {
+ ret = os_snprintf(buf + len, buflen - len,
+ "authMultiSessionId=%08X+%08X\n",
+ sm->acct_multi_session_id_hi,
+ sm->acct_multi_session_id_lo);
+ if (os_snprintf_error(buflen - len, ret))
+ return len;
+ len += ret;
+ }
+
+ name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+ name2 = eap_server_get_name(0, sm->eap_type_supp);
+ ret = os_snprintf(buf + len, buflen - len,
+ "last_eap_type_as=%d (%s)\n"
+ "last_eap_type_sta=%d (%s)\n",
+ sm->eap_type_authsrv, name1,
+ sm->eap_type_supp, name2);
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2202,16 +2514,55 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
static void ieee802_1x_finished(struct hostapd_data *hapd,
- struct sta_info *sta, int success)
+ struct sta_info *sta, int success,
+ int remediation)
{
const u8 *key;
size_t len;
/* TODO: get PMKLifetime from WPA parameters */
static const int dot11RSNAConfigPMKLifetime = 43200;
+ unsigned int session_timeout;
+
+#ifdef CONFIG_HS20
+ if (remediation && !sta->remediation) {
+ sta->remediation = 1;
+ os_free(sta->remediation_url);
+ sta->remediation_url =
+ os_strdup(hapd->conf->subscr_remediation_url);
+ sta->remediation_method = 1; /* SOAP-XML SPP */
+ }
+
+ if (success) {
+ if (sta->remediation) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+ "to " MACSTR " to indicate Subscription "
+ "Remediation",
+ MAC2STR(sta->addr));
+ hs20_send_wnm_notification(hapd, sta->addr,
+ sta->remediation_method,
+ sta->remediation_url);
+ os_free(sta->remediation_url);
+ sta->remediation_url = NULL;
+ }
+
+ if (sta->hs20_deauth_req) {
+ wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+ "to " MACSTR " to indicate imminent "
+ "deauthentication", MAC2STR(sta->addr));
+ hs20_send_wnm_notification_deauth_req(
+ hapd, sta->addr, sta->hs20_deauth_req);
+ }
+ }
+#endif /* CONFIG_HS20 */
key = ieee802_1x_get_key(sta->eapol_sm, &len);
- if (success && key && len >= PMK_LEN &&
- wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
+ if (sta->session_timeout_set)
+ session_timeout = sta->session_timeout;
+ else
+ session_timeout = dot11RSNAConfigPMKLifetime;
+ if (success && key && len >= PMK_LEN && !sta->remediation &&
+ !sta->hs20_deauth_requested &&
+ wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
sta->eapol_sm) == 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG,
@@ -2238,5 +2589,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
os_sleep(0, 10000);
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+ hostapd_wps_eap_completed(hapd);
}
}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index e1df94057de70..de6e0e75fac3b 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -29,6 +29,7 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
struct sta_info *sta, int authorized);
void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
void ieee802_1x_deinit(struct hostapd_data *hapd);
int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *buf, size_t len, int ack);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
new file mode 100644
index 0000000000000..b0d42dcd82c49
--- /dev/null
+++ b/src/ap/ndisc_snoop.c
@@ -0,0 +1,171 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+
+struct ip6addr {
+ struct in6_addr addr;
+ struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+ struct ip6_hdr ipv6h;
+ struct icmp6_hdr icmp6h;
+ struct in6_addr target_addr;
+ u8 opt_type;
+ u8 len;
+ u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT 134
+#define NEIGHBOR_SOLICITATION 135
+#define NEIGHBOR_ADVERTISEMENT 136
+#define SOURCE_LL_ADDR 1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+ struct ip6addr *ip6addr;
+
+ ip6addr = os_zalloc(sizeof(*ip6addr));
+ if (!ip6addr)
+ return -1;
+
+ os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+ dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+ return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ struct ip6addr *ip6addr, *prev;
+
+ dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+ list) {
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+ os_free(ip6addr);
+ }
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+ struct ip6addr *ip6addr;
+
+ dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+ if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+ ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+ ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+ ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+ size_t len)
+{
+ struct hostapd_data *hapd = ctx;
+ struct icmpv6_ndmsg *msg;
+ struct in6_addr *saddr;
+ struct sta_info *sta;
+ int res;
+ char addrtxt[INET6_ADDRSTRLEN + 1];
+
+ if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+ return;
+ msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+ switch (msg->icmp6h.icmp6_type) {
+ case NEIGHBOR_SOLICITATION:
+ if (len < ETH_HLEN + sizeof(*msg))
+ return;
+ if (msg->opt_type != SOURCE_LL_ADDR)
+ return;
+
+ saddr = &msg->ipv6h.ip6_src;
+ if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 &&
+ saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) {
+ if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+ return;
+ sta = ap_get_sta(hapd, msg->opt_lladdr);
+ if (!sta)
+ return;
+
+ if (sta_has_ip6addr(sta, saddr))
+ return;
+
+ if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
+ == NULL)
+ addrtxt[0] = '\0';
+ wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+ MACSTR, addrtxt, MAC2STR(sta->addr));
+ hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
+ res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
+ 128, sta->addr);
+ if (res) {
+ wpa_printf(MSG_ERROR,
+ "ndisc_snoop: Adding ip neigh failed: %d",
+ res);
+ return;
+ }
+
+ if (sta_ip6addr_add(sta, saddr))
+ return;
+ }
+ break;
+ case ROUTER_ADVERTISEMENT:
+ if (!hapd->conf->disable_dgaf)
+ return;
+ /* fall through */
+ case NEIGHBOR_ADVERTISEMENT:
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+ (u8 *) buf, len);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+ hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+ L2_PACKET_FILTER_NDISC);
+ if (hapd->sock_ndisc == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+ l2_packet_deinit(hapd->sock_ndisc);
+}
diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h
new file mode 100644
index 0000000000000..3cc9a557b06b3
--- /dev/null
+++ b/src/ap/ndisc_snoop.h
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
diff --git a/src/ap/p2p_hostapd.c b/src/ap/p2p_hostapd.c
index 795d313b8c163..9be640cb46220 100644
--- a/src/ap/p2p_hostapd.c
+++ b/src/ap/p2p_hostapd.c
@@ -96,9 +96,8 @@ u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
u8 bitmap;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
*eid++ = 4 + 3 + 1;
- WPA_PUT_BE24(eid, OUI_WFA);
- eid += 3;
- *eid++ = P2P_OUI_TYPE;
+ WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+ eid += 4;
*eid++ = P2P_ATTR_MANAGEABILITY;
WPA_PUT_LE16(eid, 1);
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
index ba5c606442f43..efc1d7e4c78f0 100644
--- a/src/ap/peerkey_auth.c
+++ b/src/ap/peerkey_auth.c
@@ -79,15 +79,15 @@ static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+ struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+ const u8 *key_data, size_t key_data_len)
{
struct wpa_eapol_ie_parse kde;
struct wpa_stsl_search search;
u8 *buf, *pos;
size_t buf_len;
- if (wpa_parse_kde_ies((const u8 *) (key + 1),
- WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+ if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
return;
}
@@ -221,8 +221,8 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
return;
/* Peer RSN IE */
- os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len);
- pos = buf + kde->rsn_ie_len;
+ os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
+ pos += kde->rsn_ie_len;
/* Peer MAC Address */
pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
@@ -253,14 +253,14 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+ struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+ const u8 *key_data, size_t key_data_len)
{
struct wpa_eapol_ie_parse kde;
struct wpa_stsl_search search;
u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
- if (wpa_parse_kde_ies((const u8 *) (key + 1),
- WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+ if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
return;
}
@@ -324,15 +324,15 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+ struct wpa_state_machine *sm,
+ const u8 *key_data, size_t key_data_len)
{
struct wpa_eapol_ie_parse kde;
struct wpa_stsl_search search;
struct rsn_error_kde error;
u16 mui, error_type;
- if (wpa_parse_kde_ies((const u8 *) (key + 1),
- WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+ if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
return;
}
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index d27fd302ba969..877affe4eadcc 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -1,6 +1,6 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
#include "sta_info.h"
#include "ap_config.h"
#include "pmksa_cache_auth.h"
@@ -37,53 +38,55 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
{
- if (entry == NULL)
- return;
os_free(entry->identity);
wpabuf_free(entry->cui);
#ifndef CONFIG_NO_RADIUS
radius_free_class(&entry->radius_class);
#endif /* CONFIG_NO_RADIUS */
- os_free(entry);
+ bin_clear_free(entry, sizeof(*entry));
}
-static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
- struct rsn_pmksa_cache_entry *entry)
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
+ unsigned int hash;
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx);
- pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+
+ /* unlink from hash list */
+ hash = PMKID_HASH(entry->pmkid);
+ pos = pmksa->pmkid[hash];
prev = NULL;
while (pos) {
if (pos == entry) {
- if (prev != NULL) {
- prev->hnext = pos->hnext;
- } else {
- pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
- pos->hnext;
- }
+ if (prev != NULL)
+ prev->hnext = entry->hnext;
+ else
+ pmksa->pmkid[hash] = entry->hnext;
break;
}
prev = pos;
pos = pos->hnext;
}
+ /* unlink from entry list */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos == entry) {
if (prev != NULL)
- prev->next = pos->next;
+ prev->next = entry->next;
else
- pmksa->pmksa = pos->next;
+ pmksa->pmksa = entry->next;
break;
}
prev = pos;
pos = pos->next;
}
+
_pmksa_cache_free_entry(entry);
}
@@ -91,9 +94,9 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
- struct os_time now;
+ struct os_reltime now;
- os_get_time(&now);
+ os_get_reltime(&now);
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(pmksa->pmksa->spa));
@@ -107,12 +110,12 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
{
int sec;
- struct os_time now;
+ struct os_reltime now;
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
if (pmksa->pmksa == NULL)
return;
- os_get_time(&now);
+ os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0)
sec = 0;
@@ -144,6 +147,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
entry->eap_type_authsrv = eapol->eap_type_authsrv;
entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+
+ entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+ entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo;
}
@@ -181,6 +187,9 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
eapol->eap_type_authsrv = entry->eap_type_authsrv;
((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+
+ eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
+ eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
}
@@ -188,6 +197,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
+ int hash;
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
@@ -205,8 +215,10 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
entry->next = prev->next;
prev->next = entry;
}
- entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
- pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
+
+ hash = PMKID_HASH(entry->pmkid);
+ entry->hnext = pmksa->pmkid[hash];
+ pmksa->pmkid[hash] = entry;
pmksa->pmksa_count++;
if (prev == NULL)
@@ -222,6 +234,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @session_timeout: Session timeout
@@ -237,23 +251,32 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len,
- const u8 *aa, const u8 *spa, int session_timeout,
- struct eapol_state_machine *eapol, int akmp)
+ const u8 *kck, size_t kck_len,
+ const u8 *aa, const u8 *spa, int session_timeout,
+ struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry, *pos;
- struct os_time now;
+ struct os_reltime now;
if (pmk_len > PMK_LEN)
return NULL;
+ if (wpa_key_mgmt_suite_b(akmp) && !kck)
+ return NULL;
+
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
os_memcpy(entry->pmk, pmk, pmk_len);
entry->pmk_len = pmk_len;
- rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
- wpa_key_mgmt_sha256(akmp));
- os_get_time(&now);
+ if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+ else if (wpa_key_mgmt_suite_b(akmp))
+ rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+ else
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+ wpa_key_mgmt_sha256(akmp));
+ os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
entry->expiration += session_timeout;
@@ -342,6 +365,8 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
_pmksa_cache_free_entry(prev);
}
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ pmksa->pmksa_count = 0;
+ pmksa->pmksa = NULL;
for (i = 0; i < PMKID_HASH_SIZE; i++)
pmksa->pmkid[i] = NULL;
os_free(pmksa);
@@ -361,18 +386,22 @@ pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
{
struct rsn_pmksa_cache_entry *entry;
- if (pmkid)
- entry = pmksa->pmkid[PMKID_HASH(pmkid)];
- else
- entry = pmksa->pmksa;
- while (entry) {
- if ((spa == NULL ||
- os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
- (pmkid == NULL ||
- os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
- return entry;
- entry = pmkid ? entry->hnext : entry->next;
+ if (pmkid) {
+ for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
+ entry = entry->hnext) {
+ if ((spa == NULL ||
+ os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+ return entry;
+ }
+ } else {
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ if (spa == NULL ||
+ os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+ return entry;
+ }
}
+
return NULL;
}
@@ -394,15 +423,13 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache_entry *entry;
u8 new_pmkid[PMKID_LEN];
- entry = pmksa->pmksa;
- while (entry) {
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
continue;
rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
wpa_key_mgmt_sha256(entry->akmp));
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
return entry;
- entry = entry->next;
}
return NULL;
}
@@ -428,3 +455,74 @@ pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
return pmksa;
}
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+ struct radius_das_attrs *attr)
+{
+ int match = 0;
+
+ if (attr->sta_addr) {
+ if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->acct_multi_session_id) {
+ char buf[20];
+
+ if (attr->acct_multi_session_id_len != 17)
+ return 0;
+ os_snprintf(buf, sizeof(buf), "%08X+%08X",
+ entry->acct_multi_session_id_hi,
+ entry->acct_multi_session_id_lo);
+ if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->cui) {
+ if (!entry->cui ||
+ attr->cui_len != wpabuf_len(entry->cui) ||
+ os_memcmp(attr->cui, wpabuf_head(entry->cui),
+ attr->cui_len) != 0)
+ return 0;
+ match++;
+ }
+
+ if (attr->user_name) {
+ if (!entry->identity ||
+ attr->user_name_len != entry->identity_len ||
+ os_memcmp(attr->user_name, entry->identity,
+ attr->user_name_len) != 0)
+ return 0;
+ match++;
+ }
+
+ return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr)
+{
+ int found = 0;
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (attr->acct_session_id)
+ return -1;
+
+ entry = pmksa->pmksa;
+ while (entry) {
+ if (das_attr_match(entry, attr)) {
+ found++;
+ prev = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, prev);
+ continue;
+ }
+ entry = entry->next;
+ }
+
+ return found ? 0 : -1;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index d473f3fdced3a..8b7be1291b53a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -30,6 +30,9 @@ struct rsn_pmksa_cache_entry {
u8 eap_type_authsrv;
int vlan_id;
int opportunistic;
+
+ u32 acct_multi_session_id_hi;
+ u32 acct_multi_session_id_lo;
};
struct rsn_pmksa_cache;
@@ -47,6 +50,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len,
+ const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
struct rsn_pmksa_cache_entry *
@@ -55,5 +59,9 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid);
void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol);
+void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+ struct radius_das_attrs *attr);
#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 97cd0136b86dc..7e75e1a61e1ae 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
/*
* hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -12,9 +12,9 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
+#include "common/sae.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
-#include "drivers/driver.h"
#include "p2p/p2p.h"
#include "hostapd.h"
#include "accounting.h"
@@ -30,11 +30,14 @@
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "gas_serv.h"
+#include "wnm_ap.h"
+#include "ndisc_snoop.h"
#include "sta_info.h"
static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta);
static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx);
static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_IEEE80211W
@@ -69,6 +72,30 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
}
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct sta_info *sta;
+
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ const u8 *p2p_dev_addr;
+
+ if (sta->p2p_ie == NULL)
+ continue;
+
+ p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+ if (p2p_dev_addr == NULL)
+ continue;
+
+ if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+ return sta;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
{
struct sta_info *tmp;
@@ -118,6 +145,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
}
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+ sta_ip6addr_del(hapd, sta);
+}
+
+
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
int set_beacon = 0;
@@ -128,9 +161,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
ap_sta_set_authorized(hapd, sta, 0);
if (sta->flags & WLAN_STA_WDS)
- hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
+ hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
- if (!(sta->flags & WLAN_STA_PREAUTH))
+ if (!hapd->iface->driver_ap_teardown &&
+ !(sta->flags & WLAN_STA_PREAUTH))
hostapd_drv_sta_remove(hapd, sta->addr);
ap_sta_hash_del(hapd, sta);
@@ -179,6 +217,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hapd->iface->num_sta_ht_20mhz--;
}
+#ifdef CONFIG_IEEE80211N
+ ht40_intolerant_remove(hapd->iface, sta);
+#endif /* CONFIG_IEEE80211N */
+
#ifdef CONFIG_P2P
if (sta->no_p2p_set) {
sta->no_p2p_set = 0;
@@ -193,6 +235,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
set_beacon++;
#endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
+#ifdef CONFIG_MESH
+ if (hapd->mesh_sta_free_cb)
+ hapd->mesh_sta_free_cb(sta);
+#endif /* CONFIG_MESH */
+
if (set_beacon)
ieee802_11_set_beacons(hapd->iface);
@@ -200,17 +247,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
__func__, MAC2STR(sta->addr));
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+ sae_clear_retransmit_timer(hapd, sta);
ieee802_1x_free_station(sta);
wpa_auth_sta_deinit(sta->wpa_sm);
rsn_preauth_free_station(hapd, sta);
#ifndef CONFIG_NO_RADIUS
- radius_client_flush_auth(hapd->radius, sta->addr);
+ if (hapd->radius)
+ radius_client_flush_auth(hapd->radius, sta->addr);
#endif /* CONFIG_NO_RADIUS */
- os_free(sta->last_assoc_req);
os_free(sta->challenge);
#ifdef CONFIG_IEEE80211W
@@ -236,9 +285,18 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
wpabuf_free(sta->hs20_ie);
os_free(sta->ht_capabilities);
+ os_free(sta->vht_capabilities);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
+ os_free(sta->remediation_url);
+ wpabuf_free(sta->hs20_deauth_req);
+ os_free(sta->hs20_session_info_url);
+
+#ifdef CONFIG_SAE
+ sae_clear_data(sta->sae);
+ os_free(sta->sae);
+#endif /* CONFIG_SAE */
os_free(sta);
}
@@ -277,6 +335,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
unsigned long next_time = 0;
+ int reason;
wpa_printf(MSG_DEBUG, "%s: " MACSTR " flags=0x%x timeout_next=%d",
__func__, MAC2STR(sta->addr), sta->flags,
@@ -311,8 +370,15 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
* but do not disconnect the station now.
*/
next_time = hapd->conf->ap_max_inactivity + fuzz;
- } else if (inactive_sec < hapd->conf->ap_max_inactivity &&
- sta->flags & WLAN_STA_ASSOC) {
+ } else if (inactive_sec == -ENOENT) {
+ wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ "Station " MACSTR " has lost its driver entry",
+ MAC2STR(sta->addr));
+
+ /* Avoid sending client probe on removed client */
+ sta->timeout_next = STA_DISASSOC;
+ goto skip_poll;
+ } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
/* station activity detected; reset timeout state */
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Station " MACSTR " has been active %is ago",
@@ -344,6 +410,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
next_time = hapd->conf->ap_max_inactivity;
}
+skip_poll:
if (next_time) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
"for " MACSTR " (%lu seconds)",
@@ -372,9 +439,11 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
hapd, sta->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
} else {
- hostapd_drv_sta_disassoc(
- hapd, sta->addr,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ reason = (sta->timeout_next == STA_DISASSOC) ?
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+ WLAN_REASON_PREV_AUTH_NOT_VALID;
+
+ hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
}
}
@@ -388,6 +457,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
hapd, sta);
break;
case STA_DISASSOC:
+ case STA_DISASSOC_FROM_CLI:
ap_sta_set_authorized(hapd, sta, 0);
sta->flags &= ~WLAN_STA_ASSOC;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
@@ -399,14 +469,16 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated due to "
"inactivity");
+ reason = (sta->timeout_next == STA_DISASSOC) ?
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY :
+ WLAN_REASON_PREV_AUTH_NOT_VALID;
sta->timeout_next = STA_DEAUTH;
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
"for " MACSTR " (%d seconds - AP_DEAUTH_DELAY)",
__func__, MAC2STR(sta->addr), AP_DEAUTH_DELAY);
eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
hapd, sta);
- mlme_disassociate_indication(
- hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
+ mlme_disassociate_indication(hapd, sta, reason);
break;
case STA_DEAUTH:
case STA_REMOVE:
@@ -429,7 +501,6 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
- u8 addr[ETH_ALEN];
if (!(sta->flags & WLAN_STA_AUTH)) {
if (sta->flags & WLAN_STA_GAS) {
@@ -440,6 +511,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
return;
}
+ hostapd_drv_sta_deauth(hapd, sta->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
mlme_deauthenticate_indication(hapd, sta,
WLAN_REASON_PREV_AUTH_NOT_VALID);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -447,9 +520,19 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
"session timeout");
sta->acct_terminate_cause =
RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
- os_memcpy(addr, sta->addr, ETH_ALEN);
ap_free_sta(hapd, sta);
- hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout)
+{
+ if (eloop_replenish_timeout(session_timeout, 0,
+ ap_handle_session_timer, hapd, sta) == 1) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "setting session timeout "
+ "to %d seconds", session_timeout);
+ }
}
@@ -471,6 +554,32 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
}
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM
+ struct hostapd_data *hapd = eloop_ctx;
+ struct sta_info *sta = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR,
+ MAC2STR(sta->addr));
+ if (sta->hs20_session_info_url == NULL)
+ return;
+
+ wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+ sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time)
+{
+ eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+ eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+ hapd, sta);
+}
+
+
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
@@ -495,13 +604,16 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
sta->acct_interim_interval = hapd->conf->acct_interim_interval;
accounting_sta_get_id(hapd, sta);
+ if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+ wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+ "for " MACSTR " (%d seconds - ap_max_inactivity)",
+ __func__, MAC2STR(addr),
+ hapd->conf->ap_max_inactivity);
+ eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+ ap_handle_timer, hapd, sta);
+ }
+
/* initialize STA info data */
- wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
- "for " MACSTR " (%d seconds - ap_max_inactivity)",
- __func__, MAC2STR(addr),
- hapd->conf->ap_max_inactivity);
- eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
- ap_handle_timer, hapd, sta);
os_memcpy(sta->addr, addr, ETH_ALEN);
sta->next = hapd->sta_list;
hapd->sta_list = sta;
@@ -509,6 +621,8 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
ap_sta_hash_add(hapd, sta);
sta->ssid = &hapd->conf->ssid;
ap_sta_remove_in_other_bss(hapd, sta);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ dl_list_init(&sta->ip6addr);
return sta;
}
@@ -518,6 +632,10 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
{
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ if (sta->ipaddr)
+ hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+ ap_sta_ip6addr_del(hapd, sta);
+
wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
MAC2STR(sta->addr));
if (hostapd_drv_sta_remove(hapd, sta->addr) &&
@@ -570,7 +688,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
{
wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->flags &= ~WLAN_STA_ASSOC;
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_DEAUTH;
wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
@@ -608,7 +727,8 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
{
wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
- sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
ap_sta_set_authorized(hapd, sta, 0);
sta->timeout_next = STA_REMOVE;
wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
@@ -663,13 +783,6 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
if (sta->vlan_id == old_vlanid)
return 0;
- /*
- * During 1x reauth, if the vlan id changes, then remove the old id and
- * proceed furthur to add the new one.
- */
- if (old_vlanid > 0)
- vlan_remove_dynamic(hapd, old_vlanid);
-
iface = hapd->conf->iface;
if (sta->ssid->vlan[0])
iface = sta->ssid->vlan;
@@ -677,15 +790,19 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
sta->vlan_id = 0;
else if (sta->vlan_id > 0) {
+ struct hostapd_vlan *wildcard_vlan = NULL;
vlan = hapd->conf->vlan;
while (vlan) {
- if (vlan->vlan_id == sta->vlan_id ||
- vlan->vlan_id == VLAN_ID_WILDCARD) {
- iface = vlan->ifname;
+ if (vlan->vlan_id == sta->vlan_id)
break;
- }
+ if (vlan->vlan_id == VLAN_ID_WILDCARD)
+ wildcard_vlan = vlan;
vlan = vlan->next;
}
+ if (!vlan)
+ vlan = wildcard_vlan;
+ if (vlan)
+ iface = vlan->ifname;
}
if (sta->vlan_id > 0 && vlan == NULL) {
@@ -693,7 +810,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
"binding station to (vlan_id=%d)",
sta->vlan_id);
- return -1;
+ ret = -1;
+ goto done;
} else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
if (vlan == NULL) {
@@ -702,7 +820,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_LEVEL_DEBUG, "could not add "
"dynamic VLAN interface for vlan_id=%d",
sta->vlan_id);
- return -1;
+ ret = -1;
+ goto done;
}
iface = vlan->ifname;
@@ -756,6 +875,12 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
"entry to vlan_id=%d", sta->vlan_id);
}
+
+done:
+ /* During 1x reauth, if the vlan id changes, then remove the old id. */
+ if (old_vlanid > 0)
+ vlan_remove_dynamic(hapd, old_vlanid);
+
return ret;
#else /* CONFIG_NO_VLAN */
return 0;
@@ -768,9 +893,9 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
{
u32 tu;
- struct os_time now, passed;
- os_get_time(&now);
- os_time_sub(&now, &sta->sa_query_start, &passed);
+ struct os_reltime now, passed;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &sta->sa_query_start, &passed);
tu = (passed.sec * 1000000 + passed.usec) / 1024;
if (hapd->conf->assoc_sa_query_max_timeout < tu) {
hostapd_logger(hapd, sta->addr,
@@ -807,13 +932,21 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
return;
if (sta->sa_query_count == 0) {
/* Starting a new SA Query procedure */
- os_get_time(&sta->sa_query_start);
+ os_get_reltime(&sta->sa_query_start);
}
trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
sta->sa_query_trans_id = nbuf;
sta->sa_query_count++;
- os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+ if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+ /*
+ * We don't really care which ID is used here, so simply
+ * hardcode this if the mostly theoretical os_get_random()
+ * failure happens.
+ */
+ trans_id[0] = 0x12;
+ trans_id[1] = 0x34;
+ }
timeout = hapd->conf->assoc_sa_query_retry_timeout;
sec = ((timeout / 1000) * 1024) / 1000;
@@ -849,13 +982,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
const u8 *dev_addr = NULL;
+ char buf[100];
#ifdef CONFIG_P2P
u8 addr[ETH_ALEN];
+ u8 ip_addr_buf[4];
#endif /* CONFIG_P2P */
if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
return;
+ if (authorized)
+ sta->flags |= WLAN_STA_AUTHORIZED;
+ else
+ sta->flags &= ~WLAN_STA_AUTHORIZED;
+
#ifdef CONFIG_P2P
if (hapd->p2p_group == NULL) {
if (sta->p2p_ie != NULL &&
@@ -863,52 +1003,46 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
dev_addr = addr;
} else
dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
+
+ if (dev_addr)
+ os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR,
+ MAC2STR(sta->addr), MAC2STR(dev_addr));
+ else
#endif /* CONFIG_P2P */
+ os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
+
+ if (hapd->sta_authorized_cb)
+ hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+ sta->addr, authorized, dev_addr);
if (authorized) {
- if (dev_addr)
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
- MACSTR " p2p_dev_addr=" MACSTR,
- MAC2STR(sta->addr), MAC2STR(dev_addr));
- else
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED
- MACSTR, MAC2STR(sta->addr));
- if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
- wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED MACSTR " p2p_dev_addr="
- MACSTR,
- MAC2STR(sta->addr), MAC2STR(dev_addr));
- else if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
- wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+ char ip_addr[100];
+ ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+ if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+ os_snprintf(ip_addr, sizeof(ip_addr),
+ " ip_addr=%u.%u.%u.%u",
+ ip_addr_buf[0], ip_addr_buf[1],
+ ip_addr_buf[2], ip_addr_buf[3]);
+ }
+#endif /* CONFIG_P2P */
- sta->flags |= WLAN_STA_AUTHORIZED;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+ buf, ip_addr);
+
+ 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);
} else {
- if (dev_addr)
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
- MACSTR " p2p_dev_addr=" MACSTR,
- MAC2STR(sta->addr), MAC2STR(dev_addr));
- else
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED
- MACSTR, MAC2STR(sta->addr));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
+
if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx && dev_addr)
- wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_DISCONNECTED MACSTR " p2p_dev_addr="
- MACSTR, MAC2STR(sta->addr), MAC2STR(dev_addr));
- else if (hapd->msg_ctx_parent &&
- hapd->msg_ctx_parent != hapd->msg_ctx)
- wpa_msg(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_DISCONNECTED MACSTR,
- MAC2STR(sta->addr));
- sta->flags &= ~WLAN_STA_AUTHORIZED;
+ hapd->msg_ctx_parent != hapd->msg_ctx)
+ wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
+ AP_STA_DISCONNECTED "%s", buf);
}
-
- if (hapd->sta_authorized_cb)
- hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
- sta->addr, authorized, dev_addr);
}
@@ -969,3 +1103,36 @@ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
ap_sta_disassoc_cb_timeout(hapd, sta);
}
+
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+{
+ int res;
+
+ buf[0] = '\0';
+ res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+ (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+ (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+ (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
+ ""),
+ (flags & WLAN_STA_SHORT_PREAMBLE ?
+ "[SHORT_PREAMBLE]" : ""),
+ (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
+ (flags & WLAN_STA_WMM ? "[WMM]" : ""),
+ (flags & WLAN_STA_MFP ? "[MFP]" : ""),
+ (flags & WLAN_STA_WPS ? "[WPS]" : ""),
+ (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+ (flags & WLAN_STA_WDS ? "[WDS]" : ""),
+ (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+ (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+ (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+ (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+ (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+ (flags & WLAN_STA_WNM_SLEEP_MODE ?
+ "[WNM_SLEEP_MODE]" : ""));
+ if (os_snprintf_error(buflen, res))
+ res = -1;
+
+ return res;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index d5e92faa7637a..57551ab17d5dd 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -9,12 +9,16 @@
#ifndef STA_INFO_H
#define STA_INFO_H
+#ifdef CONFIG_MESH
+/* needed for mesh_plink_state enum */
+#include "common/defs.h"
+#endif /* CONFIG_MESH */
+
+#include "list.h"
+
/* STA flags */
#define WLAN_STA_AUTH BIT(0)
#define WLAN_STA_ASSOC BIT(1)
-#define WLAN_STA_PS BIT(2)
-#define WLAN_STA_TIM BIT(3)
-#define WLAN_STA_PERM BIT(4)
#define WLAN_STA_AUTHORIZED BIT(5)
#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
#define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -29,6 +33,9 @@
#define WLAN_STA_WPS2 BIT(16)
#define WLAN_STA_GAS BIT(17)
#define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -42,6 +49,8 @@ struct sta_info {
struct sta_info *next; /* next entry in sta list */
struct sta_info *hnext; /* next entry in hash table list */
u8 addr[6];
+ be32 ipaddr;
+ struct dl_list ip6addr; /* list head for struct ip6addr */
u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
u32 flags; /* Bitfield of WLAN_STA_* */
u16 capability;
@@ -50,19 +59,39 @@ struct sta_info {
int supported_rates_len;
u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
+#ifdef CONFIG_MESH
+ enum mesh_plink_state plink_state;
+ u16 peer_lid;
+ u16 my_lid;
+ u16 mpm_close_reason;
+ int mpm_retries;
+ u8 my_nonce[32];
+ u8 peer_nonce[32];
+ u8 aek[32]; /* SHA256 digest length */
+ u8 mtk[16];
+ u8 mgtk[16];
+ u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
unsigned int nonerp_set:1;
unsigned int no_short_slot_time_set:1;
unsigned int no_short_preamble_set:1;
unsigned int no_ht_gf_set:1;
unsigned int no_ht_set:1;
+ unsigned int ht40_intolerant_set:1;
unsigned int ht_20mhz_set:1;
unsigned int no_p2p_set:1;
+ unsigned int qos_map_enabled:1;
+ unsigned int remediation:1;
+ unsigned int hs20_deauth_requested:1;
+ unsigned int session_timeout_set:1;
+ unsigned int radius_das_match:1;
u16 auth_alg;
- u8 previous_ap[6];
enum {
- STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE
+ STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
+ STA_DISASSOC_FROM_CLI
} timeout_next;
u16 deauth_reason;
@@ -71,12 +100,9 @@ struct sta_info {
/* IEEE 802.1X related data */
struct eapol_state_machine *eapol_sm;
- /* IEEE 802.11f (IAPP) related data */
- struct ieee80211_mgmt *last_assoc_req;
-
u32 acct_session_id_hi;
u32 acct_session_id_lo;
- time_t acct_session_start;
+ struct os_reltime acct_session_start;
int acct_session_started;
int acct_terminate_cause; /* Acct-Terminate-Cause */
int acct_interim_interval; /* Acct-Interim-Interval */
@@ -103,6 +129,7 @@ struct sta_info {
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
+ u8 vht_opmode;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
@@ -111,7 +138,7 @@ struct sta_info {
u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
* sa_query_count octets of pending SA Query
* transaction identifiers */
- struct os_time sa_query_start;
+ struct os_reltime sa_query_start;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_INTERWORKING
@@ -123,13 +150,25 @@ struct sta_info {
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+ u8 remediation_method;
+ char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+ struct wpabuf *hs20_deauth_req;
+ char *hs20_session_info_url;
+ int hs20_disassoc_timer;
- struct os_time connected_time;
+ struct os_reltime connected_time;
#ifdef CONFIG_SAE
- enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
- u16 sae_send_confirm;
+ struct sae_data *sae;
#endif /* CONFIG_SAE */
+
+ u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+ /* Last Authentication/(Re)Association Request/Action frame sequence
+ * control */
+ u16 last_seq_ctrl;
+ /* Last Authentication/(Re)Association Request/Action frame subtype */
+ u8 last_subtype;
};
@@ -156,14 +195,20 @@ int ap_for_each_sta(struct hostapd_data *hapd,
void *ctx),
void *ctx);
struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_free_stas(struct hostapd_data *hapd);
void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+ u32 session_timeout);
void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
u32 session_timeout);
void ap_sta_no_session_timeout(struct hostapd_data *hapd,
struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+ struct sta_info *sta, int warning_time);
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason);
@@ -191,4 +236,6 @@ static inline int ap_sta_is_authorized(struct sta_info *sta)
void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+
#endif /* STA_INFO_H */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
index 4a2ea0665d886..4725e2b3e8f1e 100644
--- a/src/ap/tkip_countermeasures.c
+++ b/src/ap/tkip_countermeasures.c
@@ -68,7 +68,7 @@ void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
{
- struct os_time now;
+ struct os_reltime now;
int ret = 0;
if (addr && local) {
@@ -89,8 +89,8 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
}
}
- os_get_time(&now);
- if (now.sec > hapd->michael_mic_failure + 60) {
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
hapd->michael_mic_failures = 1;
} else {
hapd->michael_mic_failures++;
@@ -99,7 +99,7 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
ret = 1;
}
}
- hapd->michael_mic_failure = now.sec;
+ hapd->michael_mic_failure = now;
return ret;
}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 7b1a9e6711a5c..dc6501997db03 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -493,8 +487,18 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
while (vlan) {
if (os_strcmp(ifname, vlan->ifname) == 0) {
- os_snprintf(br_name, sizeof(br_name), "brvlan%d",
- vlan->vlan_id);
+ if (hapd->conf->vlan_bridge[0]) {
+ os_snprintf(br_name, sizeof(br_name), "%s%d",
+ hapd->conf->vlan_bridge,
+ vlan->vlan_id);
+ } else if (tagged_interface) {
+ os_snprintf(br_name, sizeof(br_name),
+ "br%s.%d", tagged_interface,
+ vlan->vlan_id);
+ } else {
+ os_snprintf(br_name, sizeof(br_name),
+ "brvlan%d", vlan->vlan_id);
+ }
if (!br_addbr(br_name))
vlan->clean |= DVLAN_CLEAN_BR;
@@ -550,8 +554,18 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
while (vlan) {
if (os_strcmp(ifname, vlan->ifname) == 0) {
- os_snprintf(br_name, sizeof(br_name), "brvlan%d",
- vlan->vlan_id);
+ if (hapd->conf->vlan_bridge[0]) {
+ os_snprintf(br_name, sizeof(br_name), "%s%d",
+ hapd->conf->vlan_bridge,
+ vlan->vlan_id);
+ } else if (tagged_interface) {
+ os_snprintf(br_name, sizeof(br_name),
+ "br%s.%d", tagged_interface,
+ vlan->vlan_id);
+ } else {
+ os_snprintf(br_name, sizeof(br_name),
+ "brvlan%d", vlan->vlan_id);
+ }
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
br_delif(br_name, vlan->ifname);
@@ -603,6 +617,7 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
struct ifinfomsg *ifi;
int attrlen, nlmsg_len, rta_len;
struct rtattr *attr;
+ char ifname[IFNAMSIZ + 1];
if (len < sizeof(*ifi))
return;
@@ -617,29 +632,39 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+ os_memset(ifname, 0, sizeof(ifname));
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
- char ifname[IFNAMSIZ + 1];
-
if (attr->rta_type == IFLA_IFNAME) {
int n = attr->rta_len - rta_len;
if (n < 0)
break;
- os_memset(ifname, 0, sizeof(ifname));
-
- if ((size_t) n > sizeof(ifname))
- n = sizeof(ifname);
+ if ((size_t) n >= sizeof(ifname))
+ n = sizeof(ifname) - 1;
os_memcpy(ifname, ((char *) attr) + rta_len, n);
- if (del)
- vlan_dellink(ifname, hapd);
- else
- vlan_newlink(ifname, hapd);
}
attr = RTA_NEXT(attr, attrlen);
}
+
+ if (!ifname[0])
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+ del ? "DEL" : "NEW",
+ ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+ (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+ (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+ (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+ (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+ if (del)
+ vlan_dellink(ifname, hapd);
+ else
+ vlan_newlink(ifname, hapd);
}
@@ -663,7 +688,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
}
h = (struct nlmsghdr *) buf;
- while (left >= (int) sizeof(*h)) {
+ while (NLMSG_OK(h, left)) {
int len, plen;
len = h->nlmsg_len;
@@ -684,9 +709,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
break;
}
- len = NLMSG_ALIGN(len);
- left -= len;
- h = (struct nlmsghdr *) ((char *) h + len);
+ h = NLMSG_NEXT(h, left);
}
if (left > 0) {
@@ -837,6 +860,24 @@ int vlan_init(struct hostapd_data *hapd)
hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+ !hapd->conf->vlan) {
+ /* dynamic vlans enabled but no (or empty) vlan_file given */
+ struct hostapd_vlan *vlan;
+ vlan = os_zalloc(sizeof(*vlan));
+ if (vlan == NULL) {
+ wpa_printf(MSG_ERROR, "Out of memory while assigning "
+ "VLAN interfaces");
+ return -1;
+ }
+
+ vlan->vlan_id = VLAN_ID_WILDCARD;
+ os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
+ hapd->conf->iface);
+ vlan->next = hapd->conf->vlan;
+ hapd->conf->vlan = vlan;
+ }
+
if (vlan_dynamic_add(hapd, hapd->conf->vlan))
return -1;
@@ -850,6 +891,7 @@ void vlan_deinit(struct hostapd_data *hapd)
#ifdef CONFIG_FULL_DYNAMIC_VLAN
full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+ hapd->full_dynamic_vlan = NULL;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
}
@@ -858,7 +900,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
struct hostapd_vlan *vlan,
int vlan_id)
{
- struct hostapd_vlan *n;
+ struct hostapd_vlan *n = NULL;
char *ifname, *pos;
if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
@@ -871,28 +913,24 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
if (ifname == NULL)
return NULL;
pos = os_strchr(ifname, '#');
- if (pos == NULL) {
- os_free(ifname);
- return NULL;
- }
+ if (pos == NULL)
+ goto free_ifname;
*pos++ = '\0';
n = os_zalloc(sizeof(*n));
- if (n == NULL) {
- os_free(ifname);
- return NULL;
- }
+ if (n == NULL)
+ goto free_ifname;
n->vlan_id = vlan_id;
n->dynamic_vlan = 1;
os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
pos);
- os_free(ifname);
if (hostapd_vlan_if_add(hapd, n->ifname)) {
os_free(n);
- return NULL;
+ n = NULL;
+ goto free_ifname;
}
n->next = hapd->conf->vlan;
@@ -902,6 +940,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
ifconfig_up(n->ifname);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+free_ifname:
+ os_free(ifname);
return n;
}
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index 382d5dee4f806..781eaac441be2 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -3,14 +3,8 @@
* Copyright 2003, Instant802 Networks, Inc.
* Copyright 2005, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef VLAN_INIT_H
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index d21c82f6de2e4..6d4177c2a847c 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -4,14 +4,8 @@
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "utils/includes.h"
@@ -157,7 +151,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
len = ((u8 *) (t + 1)) - buf;
if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
- perror("wmm_send_action: send");
+ wpa_printf(MSG_INFO, "wmm_send_action: send failed");
}
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
index 96b04e8909634..b70b8636038f5 100644
--- a/src/ap/wmm.h
+++ b/src/ap/wmm.h
@@ -3,14 +3,8 @@
* Copyright 2002-2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#ifndef WME_H
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 54a6b857d7845..4c8bc10083c4e 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -1,6 +1,6 @@
/*
* hostapd - WNM
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,7 +9,9 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ap_config.h"
@@ -72,7 +74,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wnmsleep_ie.len = wnmsleep_ie_len - 2;
wnmsleep_ie.action_type = action_type;
wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
- wnmsleep_ie.intval = intval;
+ wnmsleep_ie.intval = host_to_le16(intval);
/* TFS IE(s) */
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
@@ -154,6 +156,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
*/
if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+ sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
addr, NULL, NULL);
wpa_set_wnmsleep(sta->wpa_sm, 1);
@@ -167,6 +170,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wnmsleep_ie.status ==
WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+ sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
wpa_set_wnmsleep(sta->wpa_sm, 0);
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
addr, NULL, NULL);
@@ -233,7 +237,7 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
wnmsleep_ie->action_type,
- wnmsleep_ie->intval);
+ le_to_host16(wnmsleep_ie->intval));
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
/* clear the tfs after sending the resp frame */
@@ -243,29 +247,350 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
}
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+ const u8 *addr,
+ u8 dialog_token,
+ const char *url)
+{
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len, len;
+ u8 *pos;
+ int res;
+
+ if (url)
+ url_len = os_strlen(url);
+ else
+ url_len = 0;
+
+ mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+ if (mgmt == NULL)
+ return -1;
+ 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->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+ mgmt->u.action.u.bss_tm_req.req_mode = 0;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+ if (url) {
+ *pos++ += url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+ "validity_interval=%u",
+ MAC2STR(addr), dialog_token,
+ mgmt->u.action.u.bss_tm_req.req_mode,
+ le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
+ mgmt->u.action.u.bss_tm_req.validity_interval);
+
+ len = pos - &mgmt->u.action.category;
+ res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+ mgmt->da, &mgmt->u.action.category, len);
+ os_free(mgmt);
+ return res;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm,
+ size_t len)
+{
+ u8 dialog_token, reason;
+ const u8 *pos, *end;
+
+ if (len < 2) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ pos = frm;
+ end = pos + len;
+ dialog_token = *pos++;
+ reason = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+ MACSTR " dialog_token=%u reason=%u",
+ MAC2STR(addr), dialog_token, reason);
+
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+
+ ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+ const u8 *addr, const u8 *frm,
+ size_t len)
+{
+ u8 dialog_token, status_code, bss_termination_delay;
+ const u8 *pos, *end;
+
+ if (len < 3) {
+ wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
+ pos = frm;
+ end = pos + len;
+ dialog_token = *pos++;
+ status_code = *pos++;
+ bss_termination_delay = *pos++;
+
+ wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+ MACSTR " dialog_token=%u status_code=%u "
+ "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+ status_code, bss_termination_delay);
+
+ if (status_code == WNM_BSS_TM_ACCEPT) {
+ if (end - pos < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+ MAC2STR(pos));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+ " status_code=%u bss_termination_delay=%u target_bssid="
+ MACSTR,
+ MAC2STR(addr), status_code, bss_termination_delay,
+ MAC2STR(pos));
+ pos += ETH_ALEN;
+ } else {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+ " status_code=%u bss_termination_delay=%u",
+ MAC2STR(addr), status_code, bss_termination_delay);
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+ pos, end - pos);
+}
+
+
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- struct rx_action *action)
+ const struct ieee80211_mgmt *mgmt, size_t len)
{
- if (action->len < 1 || action->data == NULL)
+ u8 action;
+ const u8 *payload;
+ size_t plen;
+
+ if (len < IEEE80211_HDRLEN + 2)
return -1;
- switch (action->data[0]) {
+ payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+ action = *payload++;
+ plen = len - IEEE80211_HDRLEN - 2;
+
+ switch (action) {
case WNM_BSS_TRANS_MGMT_QUERY:
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
- /* TODO */
- return -1;
+ ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
case WNM_BSS_TRANS_MGMT_RESP:
- wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
- "Response");
- /* TODO */
- return -1;
+ ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+ plen);
+ return 0;
case WNM_SLEEP_MODE_REQ:
- ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
- action->len - 1);
+ ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
- action->data[0], MAC2STR(action->sa));
+ action, MAC2STR(mgmt->sa));
return -1;
}
+
+
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+ MACSTR, disassoc_timer, MAC2STR(sta->addr));
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+ int disassoc_timer)
+{
+ int timeout, beacon_int;
+
+ /*
+ * Prevent STA from reconnecting using cached PMKSA to force
+ * full authentication with the authentication server (which may
+ * decide to reject the connection),
+ */
+ wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+ beacon_int = hapd->iconf->beacon_int;
+ if (beacon_int < 1)
+ beacon_int = 100; /* best guess */
+ /* Calculate timeout in ms based on beacon_int in TU */
+ timeout = disassoc_timer * beacon_int * 128 / 125;
+ wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+ " set to %d ms", MAC2STR(sta->addr), timeout);
+
+ sta->timeout_next = STA_DISASSOC_FROM_CLI;
+ eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+ eloop_register_timeout(timeout / 1000,
+ timeout % 1000 * 1000,
+ ap_handle_timer, hapd, sta);
+}
+
+
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, const char *url,
+ int disassoc_timer)
+{
+ u8 buf[1000], *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len;
+
+ os_memset(buf, 0, sizeof(buf));
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode =
+ WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
+ WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ /* Session Information URL */
+ url_len = os_strlen(url);
+ if (url_len > 255)
+ return -1;
+ *pos++ = url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+ "Management Request frame");
+ return -1;
+ }
+
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+ }
+
+ return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, const char *url,
+ const u8 *nei_rep, size_t nei_rep_len)
+{
+ u8 *buf, *pos;
+ struct ieee80211_mgmt *mgmt;
+ size_t url_len;
+
+ wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+ MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+ MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+ buf = os_zalloc(1000 + nei_rep_len);
+ if (buf == NULL)
+ return -1;
+ mgmt = (struct ieee80211_mgmt *) buf;
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_WNM;
+ mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+ mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+ mgmt->u.action.u.bss_tm_req.disassoc_timer =
+ host_to_le16(disassoc_timer);
+ mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+ pos = mgmt->u.action.u.bss_tm_req.variable;
+
+ if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+ bss_term_dur) {
+ os_memcpy(pos, bss_term_dur, 12);
+ pos += 12;
+ }
+
+ if (url) {
+ /* Session Information URL */
+ url_len = os_strlen(url);
+ if (url_len > 255) {
+ os_free(buf);
+ return -1;
+ }
+
+ *pos++ = url_len;
+ os_memcpy(pos, url, url_len);
+ pos += url_len;
+ }
+
+ if (nei_rep) {
+ os_memcpy(pos, nei_rep, nei_rep_len);
+ pos += nei_rep_len;
+ }
+
+ if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to send BSS Transition Management Request frame");
+ os_free(buf);
+ return -1;
+ }
+ os_free(buf);
+
+ if (disassoc_timer) {
+ /* send disassociation frame after time-out */
+ set_disassoc_timer(hapd, sta, disassoc_timer);
+ }
+
+ return 0;
+}
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index f05726ee74d17..7789307209c99 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -1,6 +1,6 @@
/*
* IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,9 +9,18 @@
#ifndef WNM_AP_H
#define WNM_AP_H
-struct rx_action;
+struct sta_info;
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
- struct rx_action *action);
+ const struct ieee80211_mgmt *mgmt, size_t len);
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, int disassoc_timer);
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+ struct sta_info *sta, const char *url,
+ int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+ u8 req_mode, int disassoc_timer, u8 valid_int,
+ const u8 *bss_term_dur, const char *url,
+ const u8 *nei_rep, size_t nei_rep_len);
#endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 2c70149a18189..9c5f6094acbb8 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/state_machine.h"
+#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
@@ -32,7 +33,8 @@
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+ size_t data_len);
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
@@ -41,6 +43,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+ const u8 *pmk, struct wpa_ptk *ptk);
static const u32 dot11RSNAConfigGroupUpdateCount = 4;
static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -82,11 +86,14 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
- const u8 *addr, const u8 *prev_psk)
+ const u8 *addr,
+ const u8 *p2p_dev_addr,
+ const u8 *prev_psk)
{
if (wpa_auth->cb.get_psk == NULL)
return NULL;
- return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+ return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+ prev_psk);
}
@@ -131,6 +138,17 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
}
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+ const u8 *addr)
+{
+ if (wpa_auth->cb.start_ampe == NULL)
+ return -1;
+ return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
@@ -207,6 +225,8 @@ static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
ret = 1;
#endif /* CONFIG_IEEE80211W */
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+ ret = 1;
return ret;
}
@@ -282,8 +302,9 @@ static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
- u8 buf[ETH_ALEN + 8 + sizeof(group)];
+ u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
u8 rkey[32];
+ unsigned long ptr;
if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
return -1;
@@ -295,7 +316,8 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
*/
os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
wpa_get_ntp_timestamp(buf + ETH_ALEN);
- os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group));
+ ptr = (unsigned long) group;
+ os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
if (random_get_bytes(rkey, sizeof(rkey)) < 0)
return -1;
@@ -393,6 +415,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
wpa_auth);
if (wpa_auth->pmksa == NULL) {
wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+ os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
return NULL;
@@ -402,6 +425,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (wpa_auth->ft_pmk_cache == NULL) {
wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+ os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
os_free(wpa_auth);
@@ -419,6 +443,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
wpa_rekey_gtk, wpa_auth, NULL);
}
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(conf->ip_addr_start)) {
+ int count = WPA_GET_BE32(conf->ip_addr_end) -
+ WPA_GET_BE32(conf->ip_addr_start) + 1;
+ if (count > 1000)
+ count = 1000;
+ if (count > 0)
+ wpa_auth->ip_pool = bitfield_alloc(count);
+ }
+#endif /* CONFIG_P2P */
+
return wpa_auth;
}
@@ -432,6 +467,8 @@ int wpa_init_keys(struct wpa_authenticator *wpa_auth)
wpa_group_sm_step(wpa_auth, group);
group->GInit = FALSE;
wpa_group_sm_step(wpa_auth, group);
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
return 0;
}
@@ -459,6 +496,11 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
wpa_auth->ft_pmk_cache = NULL;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
os_free(wpa_auth->wpa_ie);
group = wpa_auth->group;
@@ -506,14 +548,20 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr)
{
struct wpa_state_machine *sm;
+ if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return NULL;
+
sm = os_zalloc(sizeof(struct wpa_state_machine));
if (sm == NULL)
return NULL;
os_memcpy(sm->addr, addr, ETH_ALEN);
+ if (p2p_dev_addr)
+ os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
sm->wpa_auth = wpa_auth;
sm->group = wpa_auth->group;
@@ -533,6 +581,8 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FT authentication already completed - do not "
"start 4-way handshake");
+ /* Go to PTKINITDONE state to allow GTK rekeying */
+ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
return 0;
}
#endif /* CONFIG_IEEE80211R */
@@ -570,12 +620,26 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr)) {
+ u32 start;
+ wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+ "address %u.%u.%u.%u from " MACSTR,
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+ bitfield_clear(sm->wpa_auth->ip_pool,
+ WPA_GET_BE32(sm->ip_addr) - start);
+ }
+#endif /* CONFIG_P2P */
if (sm->GUpdateStationKeys) {
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
}
#ifdef CONFIG_IEEE80211R
os_free(sm->assoc_resp_ftie);
+ wpabuf_free(sm->ft_pending_req_ies);
#endif /* CONFIG_IEEE80211R */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
@@ -734,40 +798,96 @@ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
}
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+ size_t data_len)
+{
+ struct wpa_ptk PTK;
+ int ok = 0;
+ const u8 *pmk = NULL;
+
+ for (;;) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+ sm->p2p_dev_addr, pmk);
+ if (pmk == NULL)
+ break;
+ } else
+ pmk = sm->PMK;
+
+ wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+ == 0) {
+ ok = 1;
+ break;
+ }
+
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+ break;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Earlier SNonce did not result in matching MIC");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "WPA: Earlier SNonce resulted in matching MIC");
+ sm->alt_snonce_valid = 0;
+ os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+ os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+ sm->PTK_valid = TRUE;
+
+ return 0;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
+ struct wpa_eapol_key_192 *key192;
u16 key_info, key_data_length;
enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
SMK_M1, SMK_M3, SMK_ERROR } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
int ft;
- const u8 *eapol_key_ie;
- size_t eapol_key_ie_len;
+ const u8 *eapol_key_ie, *key_data;
+ size_t eapol_key_ie_len, keyhdrlen, mic_len;
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return;
- if (data_len < sizeof(*hdr) + sizeof(*key))
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+ keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+ if (data_len < sizeof(*hdr) + keyhdrlen)
return;
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
+ key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
key_info = WPA_GET_BE16(key->key_info);
- key_data_length = WPA_GET_BE16(key->key_data_length);
+ if (mic_len == 24) {
+ key_data = (const u8 *) (key192 + 1);
+ key_data_length = WPA_GET_BE16(key192->key_data_length);
+ } else {
+ key_data = (const u8 *) (key + 1);
+ key_data_length = WPA_GET_BE16(key->key_data_length);
+ }
wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
" key_info=0x%x type=%u key_data_length=%u",
MAC2STR(sm->addr), key_info, key->type, key_data_length);
- if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+ if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
"key_data overflow (%d > %lu)",
key_data_length,
(unsigned long) (data_len - sizeof(*hdr) -
- sizeof(*key)));
+ keyhdrlen));
return;
}
@@ -835,6 +955,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_aes_cmac(sm) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+ !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
@@ -853,6 +975,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
return;
}
}
+
+ if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+ "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+ return;
+ }
}
if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -888,8 +1017,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
"based on retransmitted EAPOL-Key "
"1/4");
sm->update_snonce = 1;
- wpa_replay_counter_mark_invalid(sm->prev_key_replay,
- key->replay_counter);
+ os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+ sm->alt_snonce_valid = TRUE;
+ os_memcpy(sm->alt_replay_counter,
+ sm->key_replay[0].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ goto continue_processing;
+ }
+
+ if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+ sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+ os_memcmp(key->replay_counter, sm->alt_replay_counter,
+ WPA_REPLAY_COUNTER_LEN) == 0) {
+ /*
+ * Supplicant may still be using the old SNonce since
+ * there was two EAPOL-Key 2/4 messages and they had
+ * different SNonce values.
+ */
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
goto continue_processing;
}
@@ -948,8 +1094,7 @@ continue_processing:
wpa_sta_disconnect(wpa_auth, sm->addr);
return;
}
- if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
- &kde) < 0) {
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 2/4 with "
"invalid Key Data contents");
@@ -958,6 +1103,9 @@ continue_processing:
if (kde.rsn_ie) {
eapol_key_ie = kde.rsn_ie;
eapol_key_ie_len = kde.rsn_ie_len;
+ } else if (kde.osen) {
+ eapol_key_ie = kde.osen;
+ eapol_key_ie_len = kde.osen_len;
} else {
eapol_key_ie = kde.wpa_ie;
eapol_key_ie_len = kde.wpa_ie_len;
@@ -987,6 +1135,26 @@ continue_processing:
return;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+ wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+ int idx;
+ wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+ "EAPOL-Key exchange");
+ idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+ if (idx >= 0) {
+ u32 start = WPA_GET_BE32(wpa_auth->conf.
+ ip_addr_start);
+ bitfield_set(wpa_auth->ip_pool, idx);
+ WPA_PUT_BE32(sm->ip_addr, start + idx);
+ wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+ "address %u.%u.%u.%u to " MACSTR,
+ sm->ip_addr[0], sm->ip_addr[1],
+ sm->ip_addr[2], sm->ip_addr[3],
+ MAC2STR(sm->addr));
+ }
+ }
+#endif /* CONFIG_P2P */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1051,7 +1219,10 @@ continue_processing:
sm->MICVerified = FALSE;
if (sm->PTK_valid && !sm->update_snonce) {
- if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+ data_len) &&
+ (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+ wpa_try_alt_snonce(sm, data, data_len))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
return;
@@ -1080,7 +1251,7 @@ continue_processing:
*/
if (msg == SMK_ERROR) {
#ifdef CONFIG_PEERKEY
- wpa_smk_error(wpa_auth, sm, key);
+ wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
#endif /* CONFIG_PEERKEY */
return;
} else if (key_info & WPA_KEY_INFO_ERROR) {
@@ -1095,11 +1266,12 @@ continue_processing:
wpa_request_new_ptk(sm);
#ifdef CONFIG_PEERKEY
} else if (msg == SMK_M1) {
- wpa_smk_m1(wpa_auth, sm, key);
+ wpa_smk_m1(wpa_auth, sm, key, key_data,
+ key_data_length);
#endif /* CONFIG_PEERKEY */
} else if (key_data_length > 0 &&
- wpa_parse_kde_ies((const u8 *) (key + 1),
- key_data_length, &kde) == 0 &&
+ wpa_parse_kde_ies(key_data, key_data_length,
+ &kde) == 0 &&
kde.mac_addr) {
} else {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1137,7 +1309,7 @@ continue_processing:
#ifdef CONFIG_PEERKEY
if (msg == SMK_M3) {
- wpa_smk_m3(wpa_auth, sm, key);
+ wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
return;
}
#endif /* CONFIG_PEERKEY */
@@ -1212,17 +1384,25 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- size_t len;
+ struct wpa_eapol_key_192 *key192;
+ size_t len, mic_len, keyhdrlen;
int alg;
int key_data_len, pad_len = 0;
u8 *buf, *pos;
int version, pairwise;
int i;
+ u8 *key_data;
+
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+ keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
- len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+ len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
if (force_version)
version = force_version;
+ else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+ wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+ version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_use_aes_cmac(sm))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
@@ -1230,7 +1410,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
else
version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
- pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+ pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
"ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
@@ -1245,6 +1425,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+ wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1261,6 +1443,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
hdr->length = host_to_be16(len - sizeof(*hdr));
key = (struct wpa_eapol_key *) (hdr + 1);
+ key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+ key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
key->type = sm->wpa == WPA_VERSION_WPA2 ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
@@ -1286,6 +1470,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
os_memcpy(key->replay_counter, sm->key_replay[0].counter,
WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
sm->key_replay[0].valid = TRUE;
if (nonce)
@@ -1295,8 +1481,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
if (kde && !encr) {
- os_memcpy(key + 1, kde, kde_len);
- WPA_PUT_BE16(key->key_data_length, kde_len);
+ os_memcpy(key_data, kde, kde_len);
+ if (mic_len == 24)
+ WPA_PUT_BE16(key192->key_data_length, kde_len);
+ else
+ WPA_PUT_BE16(key->key_data_length, kde_len);
} else if (encr && kde) {
buf = os_zalloc(key_data_len);
if (buf == NULL) {
@@ -1313,29 +1502,47 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+ wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
- if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
- (u8 *) (key + 1))) {
+ if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+ (key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
os_free(buf);
return;
}
- WPA_PUT_BE16(key->key_data_length, key_data_len);
- } else {
+ if (mic_len == 24)
+ WPA_PUT_BE16(key192->key_data_length,
+ key_data_len);
+ else
+ WPA_PUT_BE16(key->key_data_length,
+ key_data_len);
+ } else if (sm->PTK.kek_len == 16) {
u8 ek[32];
os_memcpy(key->key_iv,
sm->group->Counter + WPA_NONCE_LEN - 16, 16);
inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
os_memcpy(ek, key->key_iv, 16);
- os_memcpy(ek + 16, sm->PTK.kek, 16);
- os_memcpy(key + 1, buf, key_data_len);
- rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
- WPA_PUT_BE16(key->key_data_length, key_data_len);
+ os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+ os_memcpy(key_data, buf, key_data_len);
+ rc4_skip(ek, 32, 256, key_data, key_data_len);
+ if (mic_len == 24)
+ WPA_PUT_BE16(key192->key_data_length,
+ key_data_len);
+ else
+ WPA_PUT_BE16(key->key_data_length,
+ key_data_len);
+ } else {
+ os_free(hdr);
+ os_free(buf);
+ return;
}
os_free(buf);
}
if (key_info & WPA_KEY_INFO_MIC) {
+ u8 *key_mic;
+
if (!sm->PTK_valid) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTK not valid when sending EAPOL-Key "
@@ -1343,8 +1550,21 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
os_free(hdr);
return;
}
- wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
- key->key_mic);
+
+ key_mic = key192->key_mic; /* same offset for key and key192 */
+ wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+ sm->wpa_key_mgmt, version,
+ (u8 *) hdr, len, key_mic);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!pairwise &&
+ wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
+ drand48() <
+ wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Corrupting group EAPOL-Key Key MIC");
+ key_mic[0]++;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx,
@@ -1386,27 +1606,32 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
}
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+ size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
+ struct wpa_eapol_key_192 *key192;
u16 key_info;
int ret = 0;
- u8 mic[16];
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = wpa_mic_len(akmp);
if (data_len < sizeof(*hdr) + sizeof(*key))
return -1;
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
+ key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
key_info = WPA_GET_BE16(key->key_info);
- os_memcpy(mic, key->key_mic, 16);
- os_memset(key->key_mic, 0, 16);
- if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
- data, data_len, key->key_mic) ||
- os_memcmp(mic, key->key_mic, 16) != 0)
+ os_memcpy(mic, key192->key_mic, mic_len);
+ os_memset(key192->key_mic, 0, mic_len);
+ if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+ key_info & WPA_KEY_INFO_TYPE_MASK,
+ data, data_len, key192->key_mic) ||
+ os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
ret = -1;
- os_memcpy(key->key_mic, mic, 16);
+ os_memcpy(key192->key_mic, mic, mic_len);
return ret;
}
@@ -1433,6 +1658,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
switch (event) {
case WPA_AUTH:
+#ifdef CONFIG_MESH
+ /* PTKs are derived through AMPE */
+ if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+ /* not mesh */
+ break;
+ }
+ return 0;
+#endif /* CONFIG_MESH */
case WPA_ASSOC:
break;
case WPA_DEAUTH:
@@ -1596,6 +1829,7 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
wpa_group_ensure_init(sm->wpa_auth, sm->group);
+ sm->ReAuthenticationRequest = FALSE;
/*
* Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
@@ -1609,12 +1843,11 @@ SM_STATE(WPA_PTK, AUTHENTICATION2)
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_ERROR, "WPA: Failed to get random data for "
"ANonce.");
- wpa_sta_disconnect(sm->wpa_auth, sm->addr);
+ sm->Disconnect = TRUE;
return;
}
wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
WPA_NONCE_LEN);
- sm->ReAuthenticationRequest = FALSE;
/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
* logical place than INITIALIZE since AUTHENTICATION2 can be
* re-entered on ReAuthenticationRequest without going through
@@ -1646,8 +1879,12 @@ SM_STATE(WPA_PTK, INITPMK)
}
#endif /* CONFIG_IEEE80211R */
} else {
- wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+ wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+ sm->wpa_auth->cb.get_msk);
+ sm->Disconnect = TRUE;
+ return;
}
+ os_memset(msk, 0, sizeof(msk));
sm->req_replay_counter_used = 0;
/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -1666,7 +1903,7 @@ SM_STATE(WPA_PTK, INITPSK)
{
const u8 *psk;
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
- psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+ psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, PMK_LEN);
#ifdef CONFIG_IEEE80211R
@@ -1686,6 +1923,7 @@ SM_STATE(WPA_PTK, PTKSTART)
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = FALSE;
sm->TimeoutEvt = FALSE;
+ sm->alt_snonce_valid = FALSE;
sm->TimeoutCtr++;
if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
@@ -1701,16 +1939,20 @@ SM_STATE(WPA_PTK, PTKSTART)
* one possible PSK for this STA.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
- wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+ wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+ sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
- if (sm->pmksa)
+ if (sm->pmksa) {
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmksa->pmkid, PMKID_LEN);
- else {
+ } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+ /* No KCK available to derive PMKID */
+ pmkid = NULL;
+ } else {
/*
* Calculate PMKID since no PMKSA cache entry was
* available with pre-calculated PMKID.
@@ -1726,21 +1968,17 @@ SM_STATE(WPA_PTK, PTKSTART)
}
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk)
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+ const u8 *pmk, struct wpa_ptk *ptk)
{
- size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
- return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+ return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
#endif /* CONFIG_IEEE80211R */
- wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
- sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
- (u8 *) ptk, ptk_len,
- wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
-
- return 0;
+ return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+ sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+ ptk, sm->wpa_key_mgmt, sm->pairwise);
}
@@ -1759,15 +1997,17 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
* the packet */
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
- pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+ pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+ sm->p2p_dev_addr, pmk);
if (pmk == NULL)
break;
} else
pmk = sm->PMK;
- wpa_derive_ptk(sm, pmk, &PTK);
+ wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
- if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+ if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+ sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
ok = 1;
break;
@@ -1789,8 +2029,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
* with the value we derived.
*/
- if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
- WPA_PMK_NAME_LEN) != 0) {
+ if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKR1Name mismatch in FT 4-way "
"handshake");
@@ -1833,7 +2073,9 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
if (sm->mgmt_frame_prot) {
- return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde);
+ size_t len;
+ len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+ return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
}
return 0;
@@ -1844,6 +2086,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_igtk_kde igtk;
struct wpa_group *gsm = sm->group;
+ u8 rsc[WPA_KEY_RSC_LEN];
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
return pos;
@@ -1851,19 +2095,22 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
igtk.keyid[0] = gsm->GN_igtk;
igtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
- wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+ wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
os_memset(igtk.pn, 0, sizeof(igtk.pn));
- os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ else
+ os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+ os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
if (sm->wpa_auth->conf.disable_gtk) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
*/
- if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0)
+ if (random_get_bytes(igtk.igtk, len) < 0)
return pos;
}
pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
- (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+ (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+ NULL, 0);
return pos;
}
@@ -1913,8 +2160,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
if (sm->wpa == WPA_VERSION_WPA &&
(sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
- /* WPA-only STA, remove RSN IE */
+ /* WPA-only STA, remove RSN IE and possible MDIE */
wpa_ie = wpa_ie + wpa_ie[1] + 2;
+ if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+ wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -1968,6 +2217,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
kde_len += 300; /* FTIE + 2 * TIE */
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0)
+ kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
kde = os_malloc(kde_len);
if (kde == NULL)
return;
@@ -2029,6 +2282,16 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
pos += 4;
}
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ if (WPA_GET_BE32(sm->ip_addr) > 0) {
+ u8 addr[3 * 4];
+ os_memcpy(addr, sm->ip_addr, 4);
+ os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+ os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+ pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+ addr, sizeof(addr), NULL, 0);
+ }
+#endif /* CONFIG_P2P */
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -2047,7 +2310,7 @@ SM_STATE(WPA_PTK, PTKINITDONE)
enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
int klen = wpa_cipher_key_len(sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
- sm->PTK.tk1, klen)) {
+ sm->PTK.tk, klen)) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
return;
}
@@ -2146,7 +2409,8 @@ SM_STEP(WPA_PTK)
}
break;
case WPA_PTK_INITPSK:
- if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
+ if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+ NULL))
SM_ENTER(WPA_PTK, PTKSTART);
else {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
@@ -2220,7 +2484,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_group *gsm = sm->group;
- u8 *kde, *pos, hdr[2];
+ const u8 *kde;
+ u8 *kde_buf = NULL, *pos, hdr[2];
size_t kde_len;
u8 *gtk, dummy_gtk[32];
@@ -2256,28 +2521,29 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
ieee80211w_kde_len(sm);
- kde = os_malloc(kde_len);
- if (kde == NULL)
+ kde_buf = os_malloc(kde_len);
+ if (kde_buf == NULL)
return;
- pos = kde;
+ kde = pos = kde_buf;
hdr[0] = gsm->GN & 0x03;
hdr[1] = 0;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
+ kde_len = pos - kde;
} else {
kde = gtk;
- pos = kde + gsm->GTK_len;
+ kde_len = gsm->GTK_len;
}
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
- rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
- if (sm->wpa == WPA_VERSION_WPA2)
- os_free(kde);
+ rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+
+ os_free(kde_buf);
}
@@ -2354,15 +2620,16 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
#ifdef CONFIG_IEEE80211W
if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ size_t len;
+ len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
wpa_auth->addr, group->GNonce,
- group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN) < 0)
+ group->IGTK[group->GN_igtk - 4], len) < 0)
ret = -1;
wpa_hexdump_key(MSG_DEBUG, "IGTK",
- group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+ group->IGTK[group->GN_igtk - 4], len);
}
#endif /* CONFIG_IEEE80211W */
@@ -2429,7 +2696,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
- if (sm->is_wnmsleep)
+ if (sm == NULL || sm->is_wnmsleep)
return;
wpa_group_update_sta(sm, NULL);
@@ -2438,7 +2705,8 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
{
- sm->is_wnmsleep = !!flag;
+ if (sm)
+ sm->is_wnmsleep = !!flag;
}
@@ -2478,26 +2746,27 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_group *gsm = sm->group;
u8 *start = pos;
+ size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/*
* IGTK subelement:
* Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
*/
*pos++ = WNM_SLEEP_SUBELEM_IGTK;
- *pos++ = 2 + 6 + WPA_IGTK_LEN;
+ *pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_igtk);
pos += 2;
if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
return 0;
pos += 6;
- os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
- pos += WPA_IGTK_LEN;
+ os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+ pos += len;
wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
gsm->GN_igtk);
wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
- gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+ gsm->IGTK[gsm->GN_igtk - 4], len);
return pos - start;
}
@@ -2552,18 +2821,48 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
ret = -1;
#ifdef CONFIG_IEEE80211W
- if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
- wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
- broadcast_ether_addr, group->GN_igtk,
- group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN) < 0)
- ret = -1;
+ if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ enum wpa_alg alg;
+ size_t len;
+
+ alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+ len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+ if (ret == 0 &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+ broadcast_ether_addr, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4], len) < 0)
+ ret = -1;
+ }
#endif /* CONFIG_IEEE80211W */
return ret;
}
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ if (sm->group == ctx) {
+ wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+ " for discconnection due to fatal failure",
+ MAC2STR(sm->addr));
+ sm->Disconnect = TRUE;
+ }
+
+ return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
+{
+ wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE");
+ group->changed = TRUE;
+ group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+ wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
@@ -2572,8 +2871,10 @@ static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
group->changed = TRUE;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
- if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+ if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+ wpa_group_fatal_failure(wpa_auth, group);
return -1;
+ }
return 0;
}
@@ -2584,6 +2885,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
{
if (group->GInit) {
wpa_group_gtk_init(wpa_auth, group);
+ } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+ /* Do not allow group operations */
} else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
group->GTKAuthenticator) {
wpa_group_setkeysdone(wpa_auth, group);
@@ -2711,7 +3014,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
wpa_bool_txt(preauth),
wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
wpa_bool_txt(wpa_auth->conf.rsn_preauth));
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2761,7 +3064,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
wpa_auth->dot11RSNA4WayHandshakeFailures);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2771,7 +3074,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
/* Private MIB */
ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
wpa_auth->group->wpa_group_state);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2813,7 +3116,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
RSN_SUITE_ARG(pairwise),
sm->dot11RSNAStatsTKIPLocalMICFailures,
sm->dot11RSNAStatsTKIPRemoteMICFailures);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2823,7 +3126,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
"hostapdWPAPTKGroupState=%d\n",
sm->wpa_ptk_state,
sm->wpa_ptk_group_state);
- if (ret < 0 || (size_t) ret >= buflen - len)
+ if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
@@ -2907,6 +3210,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
return -1;
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+ sm->PTK.kck, sm->PTK.kck_len,
sm->wpa_auth->addr, sm->addr, session_timeout,
eapol, sm->wpa_key_mgmt))
return 0;
@@ -2923,7 +3227,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
if (wpa_auth == NULL)
return -1;
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+ NULL, 0,
+ wpa_auth->addr,
sta_addr, session_timeout, eapol,
WPA_KEY_MGMT_IEEE8021X))
return 0;
@@ -2932,6 +3238,38 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
}
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk)
+{
+ if (wpa_auth->conf.disable_pmksa_caching)
+ return -1;
+
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+ NULL, 0,
+ wpa_auth->addr, addr, 0, NULL,
+ WPA_KEY_MGMT_SAE))
+ return 0;
+
+ return -1;
+}
+
+
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
+ return;
+ pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
+ MACSTR " based on request", MAC2STR(sta_addr));
+ pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
+ }
+}
+
+
static struct wpa_group *
wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
{
@@ -2976,6 +3314,9 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
if (sm->group == group)
return 0;
+ if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+ return -1;
+
wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
"machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
@@ -3020,3 +3361,40 @@ int wpa_auth_uses_sae(struct wpa_state_machine *sm)
return 0;
return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
}
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+ if (sm == NULL)
+ return 0;
+ return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+ if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+ return -1;
+ os_memcpy(addr, sm->ip_addr, 4);
+ return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr)
+{
+ return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+ struct wpa_group *group;
+
+ if (!wpa_auth)
+ return;
+ for (group = wpa_auth->group; group; group = group->next)
+ wpa_group_config_group_keys(wpa_auth, group);
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 465eec6a53301..2788e657435d5 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -42,6 +42,7 @@ struct ft_rrb_frame {
#define FT_R0KH_R1KH_PULL_DATA_LEN 44
#define FT_R0KH_R1KH_RESP_DATA_LEN 76
#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
struct ft_r0kh_r1kh_pull_frame {
u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -49,7 +50,7 @@ struct ft_r0kh_r1kh_pull_frame {
le16 data_length; /* little endian length of data (44) */
u8 ap_address[ETH_ALEN];
- u8 nonce[16];
+ u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 r1kh_id[FT_R1KH_ID_LEN];
u8 s1kh_id[ETH_ALEN];
@@ -63,7 +64,7 @@ struct ft_r0kh_r1kh_resp_frame {
le16 data_length; /* little endian length of data (76) */
u8 ap_address[ETH_ALEN];
- u8 nonce[16]; /* copied from pull */
+ u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
u8 s1kh_id[ETH_ALEN]; /* copied from pull */
u8 pmk_r1[PMK_LEN];
@@ -142,6 +143,7 @@ struct wpa_auth_config {
int tx_status;
#ifdef CONFIG_IEEE80211W
enum mfp_options ieee80211w;
+ int group_mgmt_cipher;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
#define SSID_LEN 32
@@ -160,6 +162,15 @@ struct wpa_auth_config {
#endif /* CONFIG_IEEE80211R */
int disable_gtk;
int ap_mlme;
+#ifdef CONFIG_TESTING_OPTIONS
+ double corrupt_gtk_rekey_mic_probability;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+ u8 ip_addr_go[4];
+ u8 ip_addr_mask[4];
+ u8 ip_addr_start[4];
+ u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
};
typedef enum {
@@ -181,7 +192,8 @@ struct wpa_auth_callbacks {
void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
- const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk);
+ const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+ const u8 *prev_psk);
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -199,8 +211,11 @@ struct wpa_auth_callbacks {
int (*send_ft_action)(void *ctx, const u8 *dst,
const u8 *data, size_t data_len);
int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
- size_t tspec_ielen);
+ size_t tspec_ielen);
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+ int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
};
struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -222,9 +237,13 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *mdie, size_t mdie_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr);
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *p2p_dev_addr);
int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm);
void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
@@ -260,6 +279,10 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
const u8 *pmk, size_t len, const u8 *sta_addr,
int session_timeout,
struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+ const u8 *pmk);
+void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
+ const u8 *sta_addr);
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack);
@@ -288,5 +311,13 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+ struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 48bf79b9b9b11..ef3249a3eb9b4 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,6 +9,8 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "crypto/aes_wrap.h"
@@ -22,6 +24,12 @@
#ifdef CONFIG_IEEE80211R
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len);
+
+
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
@@ -57,7 +65,7 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
u8 *tspec_ie, size_t tspec_ielen)
{
if (wpa_auth->cb.add_tspec == NULL) {
- wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+ wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
return -1;
}
return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
@@ -228,8 +236,8 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
r0 = cache->pmk_r0;
while (r0) {
if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
- os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN)
- == 0) {
+ os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+ WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
if (pairwise)
*pairwise = r0->pairwise;
@@ -278,8 +286,8 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
r1 = cache->pmk_r1;
while (r1) {
if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
- os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN)
- == 0) {
+ os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+ WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
if (pairwise)
*pairwise = r1->pairwise;
@@ -293,22 +301,26 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
}
-static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
- const u8 *s1kh_id, const u8 *r0kh_id,
- size_t r0kh_id_len, const u8 *pmk_r0_name)
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+ const u8 *ies, size_t ies_len,
+ const u8 *pmk_r0_name)
{
struct ft_remote_r0kh *r0kh;
struct ft_r0kh_r1kh_pull_frame frame, f;
- r0kh = wpa_auth->conf.r0kh_list;
+ r0kh = sm->wpa_auth->conf.r0kh_list;
while (r0kh) {
- if (r0kh->id_len == r0kh_id_len &&
- os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
+ if (r0kh->id_len == sm->r0kh_id_len &&
+ os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+ 0)
break;
r0kh = r0kh->next;
}
- if (r0kh == NULL)
+ if (r0kh == NULL) {
+ wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
return -1;
+ }
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
"address " MACSTR, MAC2STR(r0kh->addr));
@@ -317,31 +329,40 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
- os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+ os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
/* aes_wrap() does not support inplace encryption, so use a temporary
* buffer for the data. */
- if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
+ if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
+ os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+ FT_R0KH_R1KH_PULL_NONCE_LEN);
os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
- os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
- os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+ os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+ os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
+ os_memset(f.pad, 0, sizeof(f.pad));
- if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+ if (aes_wrap(r0kh->key, sizeof(r0kh->key),
+ (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
f.nonce, frame.nonce) < 0)
return -1;
- wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+ if (sm->ft_pending_req_ies == NULL)
+ return -1;
+
+ wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
return 0;
}
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk, size_t ptk_len)
+ struct wpa_ptk *ptk)
{
u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN];
@@ -353,7 +374,6 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
const u8 *ssid = sm->wpa_auth->conf.ssid;
size_t ssid_len = sm->wpa_auth->conf.ssid_len;
-
if (sm->xxkey_len == 0) {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
"derivation");
@@ -375,13 +395,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
sm->pairwise);
- wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, sm->pmk_r1_name,
- (u8 *) ptk, ptk_len, ptk_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
- wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
-
- return 0;
+ return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+ sm->wpa_auth->addr, sm->pmk_r1_name,
+ ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
}
@@ -416,7 +432,7 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
pad_len = 8 - pad_len;
if (key_len + pad_len < 16)
pad_len += 8;
- if (pad_len) {
+ if (pad_len && key_len < sizeof(keybuf)) {
os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
os_memset(keybuf + key_len, 0, pad_len);
keybuf[key_len] = 0xdd;
@@ -440,7 +456,8 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
subelem[4] = gsm->GTK_len;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
- if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+ if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
+ subelem + 13)) {
os_free(subelem);
return NULL;
}
@@ -472,7 +489,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
*pos++ = WPA_IGTK_LEN;
- if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+ if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
gsm->IGTK[gsm->GN_igtk - 4], pos)) {
os_free(subelem);
return NULL;
@@ -569,8 +586,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
else {
/* TSPEC accepted; include updated TSPEC in
* response */
- rdie->descr_count = 1;
- pos += sizeof(*tspec);
+ rdie->descr_count = 1;
+ pos += sizeof(*tspec);
}
return pos;
}
@@ -632,8 +649,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
conf = &sm->wpa_auth->conf;
- if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return pos;
end = pos + max_len;
@@ -725,7 +741,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
ric_start = NULL;
if (auth_alg == WLAN_AUTH_FT &&
- wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+ wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+ sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
@@ -769,7 +786,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
* optimized by adding the STA entry earlier.
*/
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
- sm->PTK.tk1, klen))
+ sm->PTK.tk, klen))
return;
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -777,7 +794,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
}
-static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
@@ -787,7 +804,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
- size_t buflen, ptk_len;
+ size_t buflen;
int ret;
u8 *pos, *end;
int pairwise;
@@ -848,19 +865,13 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
&pairwise) < 0) {
- if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
- sm->r0kh_id_len, parse.rsn_pmkid) < 0) {
+ if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG, "FT: Did not have matching "
"PMK-R1 and unknown R0KH-ID");
return WLAN_STATUS_INVALID_PMKID;
}
- /*
- * TODO: Should return "status pending" (and the caller should
- * not send out response now). The real response will be sent
- * once the response from R0KH is received.
- */
- return WLAN_STATUS_INVALID_PMKID;
+ return -1; /* Status pending */
}
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
@@ -878,15 +889,14 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
- ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48;
- wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
- sm->wpa_auth->addr, pmk_r1_name,
- (u8 *) &sm->PTK, ptk_len, ptk_name);
- wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
- (u8 *) &sm->PTK, ptk_len);
- wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+ if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+ sm->wpa_auth->addr, pmk_r1_name,
+ &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+ pairwise) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
sm->pairwise = pairwise;
+ sm->PTK_valid = TRUE;
wpa_ft_install_ptk(sm);
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
@@ -940,6 +950,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
u16 status;
u8 *resp_ies;
size_t resp_ies_len;
+ int res;
if (sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
@@ -950,8 +961,16 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
" BSSID=" MACSTR " transaction=%d",
MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
- status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
- &resp_ies_len);
+ sm->ft_pending_cb = cb;
+ sm->ft_pending_cb_ctx = ctx;
+ sm->ft_pending_auth_transaction = auth_transaction;
+ res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+ return;
+ }
+ status = res;
wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
" auth_transaction=%d status=%d",
@@ -969,7 +988,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
struct rsn_ftie *ftie;
- u8 mic[16];
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = 16;
unsigned int count;
if (sm == NULL)
@@ -992,8 +1012,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_INVALID_PMKID;
}
- if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
- {
+ if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
+ != 0) {
wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
"with the PMKR1Name derived from auth request");
return WLAN_STATUS_INVALID_PMKID;
@@ -1039,7 +1059,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
- os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+ {
wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
"the current R0KH-ID");
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -1054,8 +1075,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return -1;
}
- if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
- FT_R1KH_ID_LEN) != 0) {
+ if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
"ReassocReq");
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
@@ -1066,7 +1087,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
}
if (parse.rsn_pmkid == NULL ||
- os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+ os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+ {
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
return -1;
@@ -1082,7 +1104,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return -1;
}
- if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5,
+ if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+ sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
@@ -1092,12 +1115,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
- if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
- wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
- wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ ftie->mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
@@ -1166,6 +1190,8 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
/* RRB - Forward action frame to the target AP */
frame = os_malloc(sizeof(*frame) + len);
+ if (frame == NULL)
+ return -1;
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame->packet_type = FT_PACKET_REQUEST;
frame->action_length = host_to_le16(len);
@@ -1180,15 +1206,27 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
}
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 resp,
+ const u8 *ies, size_t ies_len)
+{
+ struct wpa_state_machine *sm = ctx;
+ wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+ MAC2STR(sm->addr));
+ wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+ WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
const u8 *current_ap, const u8 *sta_addr,
const u8 *body, size_t len)
{
struct wpa_state_machine *sm;
u16 status;
- u8 *resp_ies, *pos;
- size_t resp_ies_len, rlen;
- struct ft_rrb_frame *frame;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ int res;
sm = wpa_ft_add_sta(wpa_auth, sta_addr);
if (sm == NULL) {
@@ -1199,8 +1237,33 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
- status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
- &resp_ies_len);
+ sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+ sm->ft_pending_cb_ctx = sm;
+ os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+ res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+ &resp_ies_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+ return 0;
+ }
+ status = res;
+
+ res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+ return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+ const u8 *current_ap, const u8 *sta_addr,
+ u16 status, const u8 *resp_ies,
+ size_t resp_ies_len)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ size_t rlen;
+ struct ft_rrb_frame *frame;
+ u8 *pos;
wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
" CurrentAP=" MACSTR " status=%d",
@@ -1216,6 +1279,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
frame = os_malloc(sizeof(*frame) + rlen);
+ if (frame == NULL)
+ return -1;
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame->packet_type = FT_PACKET_RESPONSE;
frame->action_length = host_to_le16(rlen);
@@ -1229,10 +1294,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
pos += ETH_ALEN;
WPA_PUT_LE16(pos, status);
pos += 2;
- if (resp_ies) {
+ if (resp_ies)
os_memcpy(pos, resp_ies, resp_ies_len);
- os_free(resp_ies);
- }
wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
sizeof(*frame) + rlen);
@@ -1246,7 +1309,9 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *data, size_t data_len)
{
- struct ft_r0kh_r1kh_pull_frame *frame, f;
+ struct ft_r0kh_r1kh_pull_frame f;
+ const u8 *crypt;
+ u8 *plain;
struct ft_remote_r1kh *r1kh;
struct ft_r0kh_r1kh_resp_frame resp, r;
u8 pmk_r0[PMK_LEN];
@@ -1254,7 +1319,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
- if (data_len < sizeof(*frame))
+ if (data_len < sizeof(f))
return -1;
r1kh = wpa_auth->conf.r1kh_list;
@@ -1270,11 +1335,14 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
return -1;
}
- frame = (struct ft_r0kh_r1kh_pull_frame *) data;
+ crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+ os_memset(&f, 0, sizeof(f));
+ plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
- frame->nonce, f.nonce) < 0) {
+ if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+ (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+ crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
"request from " MACSTR, MAC2STR(src_addr));
return -1;
@@ -1284,7 +1352,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
f.nonce, sizeof(f.nonce));
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
f.pmk_r0_name, WPA_PMK_NAME_LEN);
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
os_memset(&resp, 0, sizeof(resp));
@@ -1311,8 +1379,10 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
WPA_PMK_NAME_LEN);
r.pairwise = host_to_le16(pairwise);
+ os_memset(r.pad, 0, sizeof(r.pad));
- if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+ if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+ (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
r.nonce, resp.nonce) < 0) {
os_memset(pmk_r0, 0, PMK_LEN);
return -1;
@@ -1326,17 +1396,64 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
}
+static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_state_machine *sm = eloop_ctx;
+ int res;
+ u8 *resp_ies;
+ size_t resp_ies_len;
+ u16 status;
+
+ res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+ wpabuf_len(sm->ft_pending_req_ies),
+ &resp_ies, &resp_ies_len);
+ wpabuf_free(sm->ft_pending_req_ies);
+ sm->ft_pending_req_ies = NULL;
+ if (res < 0)
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ status = res;
+ wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+ " - status %u", MAC2STR(sm->addr), status);
+
+ sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+ sm->ft_pending_auth_transaction + 1, status,
+ resp_ies, resp_ies_len);
+ os_free(resp_ies);
+}
+
+
+static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+{
+ struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+
+ if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+ return 0;
+ if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+ FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+ return 0;
+ if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+ MACSTR " - process from timeout", MAC2STR(sm->addr));
+ eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+ return 1;
+}
+
+
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *data, size_t data_len)
{
- struct ft_r0kh_r1kh_resp_frame *frame, f;
+ struct ft_r0kh_r1kh_resp_frame f;
+ const u8 *crypt;
+ u8 *plain;
struct ft_remote_r0kh *r0kh;
- int pairwise;
+ int pairwise, res;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
- if (data_len < sizeof(*frame))
+ if (data_len < sizeof(f))
return -1;
r0kh = wpa_auth->conf.r0kh_list;
@@ -1352,31 +1469,30 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
return -1;
}
- frame = (struct ft_r0kh_r1kh_resp_frame *) data;
+ crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+ os_memset(&f, 0, sizeof(f));
+ plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
- frame->nonce, f.nonce) < 0) {
+ if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+ (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+ crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
"response from " MACSTR, MAC2STR(src_addr));
return -1;
}
- if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
- != 0) {
+ if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
"matching R1KH-ID");
return -1;
}
- /* TODO: verify that <nonce,s1kh_id> matches with a pending request
- * and call this requests callback function to finish request
- * processing */
-
pairwise = le_to_host16(f.pairwise);
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
f.nonce, sizeof(f.nonce));
- wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+ wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
MACSTR " pairwise=0x%x",
MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
@@ -1384,11 +1500,13 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
f.pmk_r1_name, WPA_PMK_NAME_LEN);
- wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
- pairwise);
+ res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+ pairwise);
+ wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+ wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
os_memset(f.pmk_r1, 0, PMK_LEN);
- return 0;
+ return res ? 0 : -1;
}
@@ -1396,7 +1514,9 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *data, size_t data_len)
{
- struct ft_r0kh_r1kh_push_frame *frame, f;
+ struct ft_r0kh_r1kh_push_frame f;
+ const u8 *crypt;
+ u8 *plain;
struct ft_remote_r0kh *r0kh;
struct os_time now;
os_time_t tsend;
@@ -1404,7 +1524,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
- if (data_len < sizeof(*frame))
+ if (data_len < sizeof(f))
return -1;
r0kh = wpa_auth->conf.r0kh_list;
@@ -1420,11 +1540,15 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
return -1;
}
- frame = (struct ft_r0kh_r1kh_push_frame *) data;
+ crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+ os_memset(&f, 0, sizeof(f));
+ plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+ timestamp);
/* aes_unwrap() does not support inplace decryption, so use a temporary
* buffer for the data. */
- if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- frame->timestamp, f.timestamp) < 0) {
+ if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+ (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+ crypt, plain) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
MACSTR, MAC2STR(src_addr));
return -1;
@@ -1440,8 +1564,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
return -1;
}
- if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
- != 0) {
+ if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+ FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
"R1KH-ID (received " MACSTR " own " MACSTR ")",
MAC2STR(f.r1kh_id),
@@ -1582,6 +1706,11 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
return -1;
}
+ if (end > pos) {
+ wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+ pos, end - pos);
+ }
+
return 0;
}
@@ -1593,6 +1722,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
{
struct ft_r0kh_r1kh_push_frame frame, f;
struct os_time now;
+ const u8 *plain;
+ u8 *crypt;
os_memset(&frame, 0, sizeof(frame));
frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
@@ -1614,8 +1745,14 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
os_get_time(&now);
WPA_PUT_LE32(f.timestamp, now.sec);
f.pairwise = host_to_le16(pairwise);
- if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
- f.timestamp, frame.timestamp) < 0)
+ os_memset(f.pad, 0, sizeof(f.pad));
+ plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+ timestamp);
+ crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+ timestamp);
+ if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+ (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+ plain, crypt) < 0)
return;
wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 76c61ea18e06b..7f8320708c396 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1,6 +1,6 @@
/*
* hostapd / WPA authenticator glue code
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,11 +10,11 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "l2_packet/l2_packet.h"
-#include "drivers/driver.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "preauth_auth.h"
@@ -27,6 +27,7 @@
static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
+ struct hostapd_config *iconf,
struct wpa_auth_config *wconf)
{
os_memset(wconf, 0, sizeof(*wconf));
@@ -48,6 +49,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
+ wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211R
wconf->ssid_len = conf->ssid.ssid_len;
@@ -72,7 +74,30 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_HS20
wconf->disable_gtk = conf->disable_dgaf;
+ if (conf->osen) {
+ wconf->disable_gtk = 1;
+ wconf->wpa = WPA_PROTO_OSEN;
+ wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ wconf->wpa_pairwise = 0;
+ wconf->wpa_group = WPA_CIPHER_CCMP;
+ wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+ wconf->rsn_preauth = 0;
+ wconf->disable_pmksa_caching = 1;
+#ifdef CONFIG_IEEE80211W
+ wconf->ieee80211w = 1;
+#endif /* CONFIG_IEEE80211W */
+ }
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_TESTING_OPTIONS
+ wconf->corrupt_gtk_rekey_mic_probability =
+ iconf->corrupt_gtk_rekey_mic_probability;
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+ os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+ os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+ os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+ os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
}
@@ -180,11 +205,22 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr,
const u8 *prev_psk)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
- const u8 *psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+ const u8 *psk;
+
+#ifdef CONFIG_SAE
+ if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+ if (!sta->sae || prev_psk)
+ return NULL;
+ return sta->sae->pmk;
+ }
+#endif /* CONFIG_SAE */
+
+ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
/*
* This is about to iterate over all psks, prev_psk gives the last
* returned psk which should not be returned again.
@@ -213,12 +249,17 @@ static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
struct sta_info *sta;
sta = ap_get_sta(hapd, addr);
- if (sta == NULL)
+ if (sta == NULL) {
+ wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
return -1;
+ }
key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
- if (key == NULL)
+ if (key == NULL) {
+ wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+ sta->eapol_sm);
return -1;
+ }
if (keylen > *len)
keylen = *len;
@@ -263,6 +304,21 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
struct sta_info *sta;
u32 flags = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io) {
+ size_t hex_len = 2 * data_len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex == NULL)
+ return -1;
+ wpa_snprintf_hex(hex, hex_len, data, data_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+ MAC2STR(addr), hex);
+ os_free(hex);
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
sta = ap_get_sta(hapd, addr);
if (sta)
flags = hostapd_sta_flags_to_drv(sta->flags);
@@ -368,6 +424,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
struct l2_ethhdr *buf;
int ret;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+ size_t hex_len = 2 * data_len + 1;
+ char *hex = os_malloc(hex_len);
+
+ if (hex == NULL)
+ return -1;
+ wpa_snprintf_hex(hex, hex_len, data, data_len);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+ MAC2STR(dst), hex);
+ os_free(hex);
+ return 0;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
#ifdef CONFIG_IEEE80211R
if (proto == ETH_P_RRB && hapd->iface->interfaces &&
hapd->iface->interfaces->for_each_interface) {
@@ -455,7 +526,7 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
return sta->wpa_sm;
}
- sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr);
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
if (sta->wpa_sm == NULL) {
ap_free_sta(hapd, sta);
return NULL;
@@ -498,7 +569,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
const u8 *wpa_ie;
size_t wpa_ie_len;
- hostapd_wpa_auth_conf(hapd->conf, &_conf);
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf);
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EAPOL_TX_STATUS)
_conf.tx_status = 1;
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
@@ -572,7 +643,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
void hostapd_reconfig_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config wpa_auth_conf;
- hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf);
+ hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &wpa_auth_conf);
wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
}
@@ -601,5 +672,6 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd)
#ifdef CONFIG_IEEE80211R
l2_packet_deinit(hapd->l2);
+ hapd->l2 = NULL;
#endif /* CONFIG_IEEE80211R */
}
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 97489d343d86f..7b2cd3ea8ed44 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -1,6 +1,6 @@
/*
* hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -26,6 +26,7 @@ struct wpa_state_machine {
struct wpa_group *group;
u8 addr[ETH_ALEN];
+ u8 p2p_dev_addr[ETH_ALEN];
enum {
WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
@@ -57,6 +58,8 @@ struct wpa_state_machine {
Boolean GUpdateStationKeys;
u8 ANonce[WPA_NONCE_LEN];
u8 SNonce[WPA_NONCE_LEN];
+ u8 alt_SNonce[WPA_NONCE_LEN];
+ u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 PMK[PMK_LEN];
struct wpa_ptk PTK;
Boolean PTK_valid;
@@ -83,6 +86,7 @@ struct wpa_state_machine {
unsigned int mgmt_frame_prot:1;
unsigned int rx_eapol_key_secure:1;
unsigned int update_snonce:1;
+ unsigned int alt_snonce_valid:1;
#ifdef CONFIG_IEEE80211R
unsigned int ft_completed:1;
unsigned int pmk_r1_name_valid:1;
@@ -117,9 +121,22 @@ struct wpa_state_machine {
u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
* message 2/4 */
u8 *assoc_resp_ftie;
+
+ void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+ u16 auth_transaction, u16 status,
+ const u8 *ies, size_t ies_len);
+ void *ft_pending_cb_ctx;
+ struct wpabuf *ft_pending_req_ies;
+ u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+ u8 ft_pending_auth_transaction;
+ u8 ft_pending_current_ap[ETH_ALEN];
#endif /* CONFIG_IEEE80211R */
int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+ u8 ip_addr[4];
+#endif /* CONFIG_P2P */
};
@@ -138,7 +155,8 @@ struct wpa_group {
enum {
WPA_GROUP_GTK_INIT = 0,
- WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+ WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+ WPA_GROUP_FATAL_FAILURE
} wpa_group_state;
u8 GMK[WPA_GMK_LEN];
@@ -148,7 +166,7 @@ struct wpa_group {
Boolean first_sta_seen;
Boolean reject_4way_hs_for_entropy;
#ifdef CONFIG_IEEE80211W
- u8 IGTK[2][WPA_IGTK_LEN];
+ u8 IGTK[2][WPA_IGTK_MAX_LEN];
int GN_igtk, GM_igtk;
#endif /* CONFIG_IEEE80211W */
};
@@ -183,6 +201,10 @@ struct wpa_authenticator {
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+ struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
};
@@ -208,11 +230,14 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
struct wpa_stsl_negotiation *neg);
void wpa_smk_error(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+ struct wpa_state_machine *sm,
+ const u8 *key_data, size_t key_data_len);
void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+ struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+ const u8 *key_data, size_t key_data_len);
void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+ struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+ const u8 *key_data, size_t key_data_len);
#endif /* CONFIG_PEERKEY */
#ifdef CONFIG_IEEE80211R
@@ -223,7 +248,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len);
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk, size_t ptk_len);
+ struct wpa_ptk *ptk);
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
void wpa_ft_install_ptk(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 4fd0135fe6ea0..f2872970affee 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
/*
* hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -200,6 +200,16 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
num_suites++;
}
#endif /* CONFIG_SAE */
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
#ifdef CONFIG_RSN_TESTING
if (rsn_testing) {
@@ -261,7 +271,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
/* Management Group Cipher Suite */
- RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ switch (conf->group_mgmt_cipher) {
+ case WPA_CIPHER_AES_128_CMAC:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ break;
+ case WPA_CIPHER_BIP_GMAC_128:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+ break;
+ case WPA_CIPHER_BIP_GMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+ break;
+ case WPA_CIPHER_BIP_CMAC_256:
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG,
+ "Invalid group management cipher (0x%x)",
+ conf->group_mgmt_cipher);
+ return -1;
+ }
pos += RSN_SELECTOR_LEN;
}
#endif /* CONFIG_IEEE80211W */
@@ -295,6 +323,55 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+ u8 *len;
+ u16 capab;
+
+ *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = eid++; /* to be filled */
+ WPA_PUT_BE24(eid, OUI_WFA);
+ eid += 3;
+ *eid++ = HS20_OSEN_OUI_TYPE;
+
+ /* Group Data Cipher Suite */
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ eid += RSN_SELECTOR_LEN;
+
+ /* Pairwise Cipher Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+ eid += RSN_SELECTOR_LEN;
+
+ /* AKM Suite Count and List */
+ WPA_PUT_LE16(eid, 1);
+ eid += 2;
+ RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+ eid += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+ if (conf->wmm_enabled) {
+ /* 4 PTKSA replay counters when using WMM */
+ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+ }
+#ifdef CONFIG_IEEE80211W
+ if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+ capab |= WPA_CAPABILITY_MFPC;
+ if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+ capab |= WPA_CAPABILITY_MFPR;
+ }
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(eid, capab);
+ eid += 2;
+
+ *len = eid - len - 1;
+
+ return eid;
+}
+
+
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
u8 *pos, buf[128];
@@ -302,6 +379,9 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
pos = buf;
+ if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+ pos = wpa_write_osen(&wpa_auth->conf, pos);
+ }
if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
res = wpa_write_rsn_ie(&wpa_auth->conf,
pos, buf + sizeof(buf) - pos, NULL);
@@ -407,6 +487,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (0) {
}
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
#ifdef CONFIG_IEEE80211R
else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
@@ -485,6 +569,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
if (0) {
}
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
#ifdef CONFIG_IEEE80211R
else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -534,7 +622,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
}
- if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+ if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+ {
wpa_printf(MSG_DEBUG, "Unsupported management group "
"cipher %d", data.mgmt_group_cipher);
return WPA_INVALID_MGMT_GROUP_CIPHER;
@@ -564,12 +653,9 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
#endif /* CONFIG_IEEE80211R */
- if (ciphers & WPA_CIPHER_CCMP)
- sm->pairwise = WPA_CIPHER_CCMP;
- else if (ciphers & WPA_CIPHER_GCMP)
- sm->pairwise = WPA_CIPHER_GCMP;
- else
- sm->pairwise = WPA_CIPHER_TKIP;
+ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+ if (sm->pairwise < 0)
+ return WPA_INVALID_PAIRWISE;
/* TODO: clear WPA/WPA2 state if STA changes from one to another */
if (wpa_ie[0] == WLAN_EID_RSN)
@@ -607,7 +693,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
break;
}
}
- if (sm->pmksa) {
+ if (sm->pmksa && pmkid) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKID found from PMKSA cache "
"eap_type=%d vlan_id=%d",
@@ -629,6 +715,36 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ const u8 *osen_ie, size_t osen_ie_len)
+{
+ if (wpa_auth == NULL || sm == NULL)
+ return -1;
+
+ /* TODO: parse OSEN element */
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+ sm->mgmt_frame_prot = 1;
+ sm->pairwise = WPA_CIPHER_CCMP;
+ sm->wpa = WPA_VERSION_WPA2;
+
+ if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+ os_free(sm->wpa_ie);
+ sm->wpa_ie = os_malloc(osen_ie_len);
+ if (sm->wpa_ie == NULL)
+ return -1;
+ }
+
+ os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+ sm->wpa_ie_len = osen_ie_len;
+
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
/**
* wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
* @pos: Pointer to the IE header
@@ -651,6 +767,12 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
return 0;
}
+ if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+ ie->osen = pos;
+ ie->osen_len = pos[1] + 2;
+ return 0;
+ }
+
if (pos + 1 + RSN_SELECTOR_LEN < end &&
pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
@@ -711,6 +833,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_P2P
+ if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+ ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+ ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+
+ if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+ RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+ ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: IP Address Allocation in EAPOL-Key",
+ ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+ return 0;
+ }
+#endif /* CONFIG_P2P */
+
return 0;
}
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 4999139510ec3..d2067ba3112cf 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -39,6 +39,13 @@ struct wpa_eapol_ie_parse {
const u8 *ftie;
size_t ftie_len;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+ const u8 *ip_addr_req;
+ const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+
+ const u8 *osen;
+ size_t osen_len;
};
int wpa_parse_kde_ies(const u8 *buf, size_t len,
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 5ce4f1be352e3..b0e8b0bfcac34 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -40,11 +40,13 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
const u8 *ie, size_t ie_len,
int ssi_signal);
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
struct wps_for_each_data {
int (*func)(struct hostapd_data *h, void *ctx);
void *ctx;
+ struct hostapd_data *calling_hapd;
};
@@ -57,7 +59,14 @@ static int wps_for_each(struct hostapd_iface *iface, void *ctx)
return 0;
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
- int ret = data->func(hapd, data->ctx);
+ int ret;
+
+ if (hapd != data->calling_hapd &&
+ (hapd->conf->wps_independent ||
+ data->calling_hapd->conf->wps_independent))
+ continue;
+
+ ret = data->func(hapd, data->ctx);
if (ret)
return ret;
}
@@ -74,6 +83,7 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd,
struct wps_for_each_data data;
data.func = func;
data.ctx = ctx;
+ data.calling_hapd = hapd;
if (iface->interfaces == NULL ||
iface->interfaces->for_each_interface == NULL)
return wps_for_each(iface, &data);
@@ -82,15 +92,24 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd,
}
-static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+ const u8 *p2p_dev_addr, const u8 *psk,
size_t psk_len)
{
struct hostapd_data *hapd = ctx;
struct hostapd_wpa_psk *p;
struct hostapd_ssid *ssid = &hapd->conf->ssid;
- wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
- MACSTR, MAC2STR(mac_addr));
+ if (is_zero_ether_addr(p2p_dev_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+ MAC2STR(mac_addr));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+ " P2P Device Addr " MACSTR,
+ MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+ }
wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
if (psk_len != PMK_LEN) {
@@ -104,8 +123,14 @@ static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
if (p == NULL)
return -1;
os_memcpy(p->addr, mac_addr, ETH_ALEN);
+ os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
os_memcpy(p->psk, psk, PMK_LEN);
+ if (hapd->new_psk_cb) {
+ hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+ psk, psk_len);
+ }
+
p->next = ssid->wpa_psk;
ssid->wpa_psk = p;
@@ -160,7 +185,7 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
dev->model_number, dev->serial_number,
wps_dev_type_bin2str(dev->pri_dev_type, devtype,
sizeof(devtype)));
- if (len > 0 && len < (int) sizeof(txt))
+ if (!os_snprintf_error(sizeof(txt), len))
wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
if (hapd->conf->wps_pin_requests) {
@@ -263,6 +288,20 @@ static void wps_reload_config(void *eloop_data, void *user_ctx)
}
+void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+ /*
+ * Reduce race condition of the station trying to reconnect immediately
+ * after AP reconfiguration through WPS by rescheduling the reload
+ * timeout to happen after EAP completion rather than the originally
+ * scheduled 100 ms after new configuration became known.
+ */
+ if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
+ 1)
+ wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
+}
+
+
static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
size_t attr_len)
{
@@ -277,6 +316,84 @@ static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
}
+static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
+ const struct wps_credential *cred)
+{
+ struct hostapd_bss_config *bss = hapd->conf;
+
+ wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
+
+ bss->wps_state = 2;
+ if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) {
+ os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
+ bss->ssid.ssid_len = cred->ssid_len;
+ bss->ssid.ssid_set = 1;
+ }
+
+ if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
+ (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
+ bss->wpa = 3;
+ else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
+ bss->wpa = 2;
+ else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
+ bss->wpa = 1;
+ else
+ bss->wpa = 0;
+
+ if (bss->wpa) {
+ if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+ bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+
+ bss->wpa_pairwise = 0;
+ if (cred->encr_type & WPS_ENCR_AES)
+ bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+ if (cred->encr_type & WPS_ENCR_TKIP)
+ bss->wpa_pairwise |= WPA_CIPHER_TKIP;
+ bss->rsn_pairwise = bss->wpa_pairwise;
+ bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
+ bss->wpa_pairwise,
+ bss->rsn_pairwise);
+
+ if (cred->key_len >= 8 && cred->key_len < 64) {
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
+ if (bss->ssid.wpa_passphrase)
+ os_memcpy(bss->ssid.wpa_passphrase, cred->key,
+ cred->key_len);
+ hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+ } else if (cred->key_len == 64) {
+ hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+ bss->ssid.wpa_psk =
+ os_zalloc(sizeof(struct hostapd_wpa_psk));
+ if (bss->ssid.wpa_psk &&
+ hexstr2bin((const char *) cred->key,
+ bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
+ bss->ssid.wpa_psk->group = 1;
+ os_free(bss->ssid.wpa_passphrase);
+ bss->ssid.wpa_passphrase = NULL;
+ }
+ }
+ bss->auth_algs = 1;
+ } else {
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ bss->auth_algs = 1;
+ }
+
+ /* Schedule configuration reload after short period of time to allow
+ * EAP-WSC to be finished.
+ */
+ eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
+ NULL);
+
+ return 0;
+}
+
+
static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
{
const struct wps_credential *cred = ctx;
@@ -325,6 +442,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
hapd->wps->ssid_len = cred->ssid_len;
hapd->wps->encr_types = cred->encr_type;
hapd->wps->auth_types = cred->auth_type;
+ hapd->wps->ap_encr_type = cred->encr_type;
+ hapd->wps->ap_auth_type = cred->auth_type;
if (cred->key_len == 0) {
os_free(hapd->wps->network_key);
hapd->wps->network_key = NULL;
@@ -344,7 +463,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
hapd->wps->wps_state = WPS_STATE_CONFIGURED;
if (hapd->iface->config_fname == NULL)
- return 0;
+ return hapd_wps_reconfig_in_memory(hapd, cred);
len = os_strlen(hapd->iface->config_fname) + 5;
tmp_fname = os_malloc(len);
if (tmp_fname == NULL)
@@ -437,31 +556,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
fprintf(nconf, "auth_algs=1\n");
} else {
- if ((cred->auth_type & WPS_AUTH_OPEN) &&
- (cred->auth_type & WPS_AUTH_SHARED))
- fprintf(nconf, "auth_algs=3\n");
- else if (cred->auth_type & WPS_AUTH_SHARED)
- fprintf(nconf, "auth_algs=2\n");
- else
- fprintf(nconf, "auth_algs=1\n");
-
- if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) {
- int key_idx = cred->key_idx;
- if (key_idx)
- key_idx--;
- fprintf(nconf, "wep_default_key=%d\n", key_idx);
- fprintf(nconf, "wep_key%d=", key_idx);
- if (cred->key_len == 10 || cred->key_len == 26) {
- /* WEP key as a hex string */
- for (i = 0; i < cred->key_len; i++)
- fputc(cred->key[i], nconf);
- } else {
- /* Raw WEP key; convert to hex */
- for (i = 0; i < cred->key_len; i++)
- fprintf(nconf, "%02x", cred->key[i]);
- }
- fprintf(nconf, "\n");
- }
+ /*
+ * WPS 2.0 does not allow WEP to be configured, so no need to
+ * process that option here either.
+ */
+ fprintf(nconf, "auth_algs=1\n");
}
fprintf(nconf, "# WPS configuration - END\n");
@@ -590,6 +689,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
struct wps_event_pwd_auth_fail *data)
{
+ /* Update WPS Status - Authentication Failure */
+ wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+ hapd->wps_stats.status = WPS_STATUS_FAILURE;
+ hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+ os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
}
@@ -617,21 +722,59 @@ static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
}
-static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
- "No Error", /* WPS_EI_NO_ERROR */
- "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
- "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
-};
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+ /* Update WPS Status - PBC Overlap */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC Status:PBC Timeout */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC status - Active */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+ /* Update WPS PBC status - Active */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+ struct wps_event_success *success)
+{
+ /* Update WPS status - Success */
+ hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+ hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+ os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
static void hostapd_wps_event_fail(struct hostapd_data *hapd,
struct wps_event_fail *fail)
{
+ /* Update WPS status - Failure */
+ hapd->wps_stats.status = WPS_STATUS_FAILURE;
+ os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+ hapd->wps_stats.failure_reason = fail->error_indication;
+
if (fail->error_indication > 0 &&
fail->error_indication < NUM_WPS_EI_VALUES) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
fail->msg, fail->config_error, fail->error_indication,
- wps_event_fail_reason[fail->error_indication]);
+ wps_ei_str(fail->error_indication));
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d",
@@ -653,17 +796,28 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
hostapd_wps_event_fail(hapd, &data->fail);
break;
case WPS_EV_SUCCESS:
+ hostapd_wps_event_success(hapd, &data->success);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
break;
case WPS_EV_PWD_AUTH_FAIL:
hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
break;
case WPS_EV_PBC_OVERLAP:
+ hostapd_wps_event_pbc_overlap(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
break;
case WPS_EV_PBC_TIMEOUT:
+ hostapd_wps_event_pbc_timeout(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
break;
+ case WPS_EV_PBC_ACTIVE:
+ hostapd_wps_event_pbc_active(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+ break;
+ case WPS_EV_PBC_DISABLE:
+ hostapd_wps_event_pbc_disable(hapd);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+ break;
case WPS_EV_ER_AP_ADD:
break;
case WPS_EV_ER_AP_REMOVE:
@@ -685,7 +839,16 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
}
-static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+ WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
{
wpabuf_free(hapd->wps_beacon_ie);
hapd->wps_beacon_ie = NULL;
@@ -693,6 +856,9 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = NULL;
+ if (deinit_only)
+ return;
+
hostapd_set_ap_wps_ie(hapd);
}
@@ -706,7 +872,8 @@ static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
return 0;
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
- if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) {
+ if (hapd->wps && !hapd->conf->wps_independent &&
+ !is_nil_uuid(hapd->wps->uuid)) {
*uuid = hapd->wps->uuid;
return 1;
}
@@ -774,6 +941,21 @@ static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
}
+static void hostapd_free_wps(struct wps_context *wps)
+{
+ int i;
+
+ for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+ wpabuf_free(wps->dev.vendor_ext[i]);
+ wps_device_data_free(&wps->dev);
+ os_free(wps->network_key);
+ hostapd_wps_nfc_clear(wps);
+ wpabuf_free(wps->dh_pubkey);
+ wpabuf_free(wps->dh_privkey);
+ os_free(wps);
+}
+
+
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf)
{
@@ -781,7 +963,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
struct wps_registrar_config cfg;
if (conf->wps_state == 0) {
- hostapd_wps_clear_ies(hapd);
+ hostapd_wps_clear_ies(hapd, 0);
return 0;
}
@@ -791,6 +973,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->cred_cb = hostapd_wps_cred_cb;
wps->event_cb = hostapd_wps_event_cb;
+ wps->rf_band_cb = hostapd_wps_rf_band_cb;
wps->cb_ctx = hapd;
os_memset(&cfg, 0, sizeof(cfg));
@@ -799,7 +982,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
if (is_nil_uuid(hapd->conf->uuid)) {
const u8 *uuid;
uuid = get_own_uuid(hapd->iface);
- if (uuid) {
+ if (uuid && !conf->wps_independent) {
os_memcpy(wps->uuid, uuid, UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
"interface", wps->uuid, UUID_LEN);
@@ -829,7 +1012,6 @@ int hostapd_init_wps(struct hostapd_data *hapd,
os_strdup(hapd->conf->serial_number) : NULL;
wps->config_methods =
wps_config_methods_str2bin(hapd->conf->config_methods);
-#ifdef CONFIG_WPS2
if ((wps->config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -844,14 +1026,11 @@ int hostapd_init_wps(struct hostapd_data *hapd,
"virtual_push_button for WPS 2.0 compliance");
wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
}
-#endif /* CONFIG_WPS2 */
os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
WPS_DEV_TYPE_LEN);
- if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
- os_free(wps);
- return -1;
- }
+ if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
+ goto fail;
wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
@@ -869,7 +1048,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA2;
- if (conf->rsn_pairwise & WPA_CIPHER_CCMP)
+ if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
wps->encr_types |= WPS_ENCR_AES;
if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
wps->encr_types |= WPS_ENCR_TKIP;
@@ -890,18 +1069,6 @@ int hostapd_init_wps(struct hostapd_data *hapd,
if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
wps->encr_types |= WPS_ENCR_NONE;
wps->auth_types |= WPS_AUTH_OPEN;
- } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) {
- wps->encr_types |= WPS_ENCR_WEP;
- if (conf->auth_algs & WPA_AUTH_ALG_OPEN)
- wps->auth_types |= WPS_AUTH_OPEN;
- if (conf->auth_algs & WPA_AUTH_ALG_SHARED)
- wps->auth_types |= WPS_AUTH_SHARED;
- } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) {
- wps->auth_types |= WPS_AUTH_OPEN;
- if (conf->default_wep_key_len)
- wps->encr_types |= WPS_ENCR_WEP;
- else
- wps->encr_types |= WPS_ENCR_NONE;
}
if (conf->ssid.wpa_psk_file) {
@@ -911,19 +1078,15 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
} else if (conf->ssid.wpa_psk) {
wps->network_key = os_malloc(2 * PMK_LEN + 1);
- if (wps->network_key == NULL) {
- os_free(wps);
- return -1;
- }
+ if (wps->network_key == NULL)
+ goto fail;
wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
conf->ssid.wpa_psk->psk, PMK_LEN);
wps->network_key_len = 2 * PMK_LEN;
} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
wps->network_key = os_malloc(conf->ssid.wep.len[0]);
- if (wps->network_key == NULL) {
- os_free(wps);
- return -1;
- }
+ if (wps->network_key == NULL)
+ goto fail;
os_memcpy(wps->network_key, conf->ssid.wep.key[0],
conf->ssid.wep.len[0]);
wps->network_key_len = conf->ssid.wep.len[0];
@@ -934,6 +1097,8 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->psk_set = 1;
}
+ wps->ap_auth_type = wps->auth_types;
+ wps->ap_encr_type = wps->encr_types;
if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
/* Override parameters to enable security by default */
wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
@@ -962,13 +1127,12 @@ int hostapd_init_wps(struct hostapd_data *hapd,
cfg.dualband = 1;
if (cfg.dualband)
wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+ cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
- os_free(wps->network_key);
- os_free(wps);
- return -1;
+ goto fail;
}
#ifdef CONFIG_WPS_UPNP
@@ -984,6 +1148,10 @@ int hostapd_init_wps(struct hostapd_data *hapd,
hapd->wps = wps;
return 0;
+
+fail:
+ hostapd_free_wps(wps);
+ return -1;
}
@@ -998,8 +1166,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd)
if (hostapd_wps_upnp_init(hapd, wps) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
wps_registrar_deinit(wps->registrar);
- os_free(wps->network_key);
- os_free(wps);
+ hostapd_free_wps(wps);
hapd->wps = NULL;
return -1;
}
@@ -1012,6 +1179,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd)
static void hostapd_wps_nfc_clear(struct wps_context *wps)
{
#ifdef CONFIG_WPS_NFC
+ wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
wps->ap_nfc_dev_pw_id = 0;
wpabuf_free(wps->ap_nfc_dh_pubkey);
wps->ap_nfc_dh_pubkey = NULL;
@@ -1027,21 +1195,19 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
{
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
- if (hapd->wps == NULL)
+ eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+ if (hapd->wps == NULL) {
+ hostapd_wps_clear_ies(hapd, 1);
return;
+ }
#ifdef CONFIG_WPS_UPNP
hostapd_wps_upnp_deinit(hapd);
#endif /* CONFIG_WPS_UPNP */
wps_registrar_deinit(hapd->wps->registrar);
- os_free(hapd->wps->network_key);
- wps_device_data_free(&hapd->wps->dev);
- wpabuf_free(hapd->wps->dh_pubkey);
- wpabuf_free(hapd->wps->dh_privkey);
wps_free_pending_msgs(hapd->wps->upnp_msgs);
- hostapd_wps_nfc_clear(hapd->wps);
- os_free(hapd->wps);
+ hostapd_free_wps(hapd->wps);
hapd->wps = NULL;
- hostapd_wps_clear_ies(hapd);
+ hostapd_wps_clear_ies(hapd, 1);
}
@@ -1123,7 +1289,7 @@ static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
{
const u8 *p2p_dev_addr = ctx;
if (hapd->wps == NULL)
- return 0;
+ return -1;
return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
}
@@ -1139,7 +1305,7 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd,
static int wps_cancel(struct hostapd_data *hapd, void *ctx)
{
if (hapd->wps == NULL)
- return 0;
+ return -1;
wps_registrar_wps_cancel(hapd->wps->registrar);
ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
@@ -1260,6 +1426,16 @@ static int hostapd_rx_req_put_wlan_response(
return 0;
}
+ if (!sta->eapol_sm) {
+ /*
+ * This can happen, e.g., if an ER sends an extra message after
+ * the station has disassociated (but not fully
+ * deauthenticated).
+ */
+ wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+ return 0;
+ }
+
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
@@ -1406,7 +1582,7 @@ int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int ret;
ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
- if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
+ if (os_snprintf_error(sizeof(data.pin_txt), ret))
return -1;
data.timeout = timeout;
return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
@@ -1453,8 +1629,6 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
if (encr) {
if (os_strncmp(encr, "NONE", 4) == 0)
cred.encr_type = WPS_ENCR_NONE;
- else if (os_strncmp(encr, "WEP", 3) == 0)
- cred.encr_type = WPS_ENCR_WEP;
else if (os_strncmp(encr, "TKIP", 4) == 0)
cred.encr_type = WPS_ENCR_TKIP;
else if (os_strncmp(encr, "CCMP", 4) == 0)
@@ -1569,7 +1743,8 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
if (hapd->wps == NULL)
return NULL;
- ret = wps_get_oob_cred(hapd->wps);
+ ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+ hapd->iconf->channel);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
@@ -1583,8 +1758,150 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
}
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
+{
+ struct wpabuf *ret;
+
+ if (hapd->wps == NULL)
+ return NULL;
+
+ if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+ struct wps_context *wps = hapd->wps;
+ if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+ &hapd->conf->wps_nfc_dh_privkey) < 0)
+ return NULL;
+ hostapd_wps_nfc_clear(wps);
+ wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+ wps->ap_nfc_dh_pubkey =
+ wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+ wps->ap_nfc_dh_privkey =
+ wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+ if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+ hostapd_wps_nfc_clear(wps);
+ return NULL;
+ }
+ }
+
+ ret = wps_build_nfc_handover_sel(hapd->wps,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->own_addr, hapd->iface->freq);
+
+ if (ndef && ret) {
+ struct wpabuf *tmp;
+ tmp = ndef_build_wifi(ret);
+ wpabuf_free(ret);
+ if (tmp == NULL)
+ return NULL;
+ ret = tmp;
+ }
+
+ return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel)
+{
+ struct wpabuf *wps;
+ int ret = -1;
+ u16 wsc_len;
+ const u8 *pos;
+ struct wpabuf msg;
+ struct wps_parse_attr attr;
+ u16 dev_pw_id;
+
+ /*
+ * Enrollee/station is always initiator of the NFC connection handover,
+ * so use the request message here to find Enrollee public key hash.
+ */
+ wps = ndef_parse_wifi(req);
+ if (wps == NULL)
+ return -1;
+ wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+ "payload from NFC connection handover");
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+ if (wpabuf_len(wps) < 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+ "Message");
+ goto out;
+ }
+ pos = wpabuf_head(wps);
+ wsc_len = WPA_GET_BE16(pos);
+ if (wsc_len > wpabuf_len(wps) - 2) {
+ wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+ "in rt Wi-Fi Handover Request Message", wsc_len);
+ goto out;
+ }
+ pos += 2;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: WSC attributes in Wi-Fi Handover Request Message",
+ pos, wsc_len);
+ if (wsc_len < wpabuf_len(wps) - 2) {
+ wpa_hexdump(MSG_DEBUG,
+ "WPS: Ignore extra data after WSC attributes",
+ pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+ }
+
+ wpabuf_set(&msg, pos, wsc_len);
+ ret = wps_parse_msg(&msg, &attr);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+ "Wi-Fi Handover Request Message");
+ goto out;
+ }
+
+ if (attr.oob_dev_password == NULL ||
+ attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+ wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+ "included in Wi-Fi Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ if (attr.uuid_e == NULL) {
+ wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+ "Handover Request Message");
+ ret = -1;
+ goto out;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+ attr.oob_dev_password, attr.oob_dev_password_len);
+ dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+ WPS_OOB_PUBKEY_HASH_LEN);
+ if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+ wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+ "%u in Wi-Fi Handover Request Message", dev_pw_id);
+ ret = -1;
+ goto out;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+ attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+ ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+ attr.oob_dev_password,
+ DEV_PW_NFC_CONNECTION_HANDOVER,
+ NULL, 0, 1);
+
+out:
+ wpabuf_free(wps);
+ return ret;
+}
+
+
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
{
+ if (hapd->conf->wps_nfc_pw_from_config) {
+ return wps_nfc_token_build(ndef,
+ hapd->conf->wps_nfc_dev_pw_id,
+ hapd->conf->wps_nfc_dh_pubkey,
+ hapd->conf->wps_nfc_dev_pw);
+ }
+
return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
&hapd->conf->wps_nfc_dh_pubkey,
&hapd->conf->wps_nfc_dh_privkey,
@@ -1595,6 +1912,7 @@ struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
{
struct wps_context *wps = hapd->wps;
+ struct wpabuf *pw;
if (wps == NULL)
return -1;
@@ -1606,10 +1924,22 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
return -1;
hostapd_wps_nfc_clear(wps);
+ wpa_printf(MSG_DEBUG,
+ "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
+ hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
- wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+ pw = hapd->conf->wps_nfc_dev_pw;
+ wps->ap_nfc_dev_pw = wpabuf_alloc(
+ wpabuf_len(pw) * 2 + 1);
+ if (wps->ap_nfc_dev_pw) {
+ wpa_snprintf_hex_uppercase(
+ (char *) wpabuf_put(wps->ap_nfc_dev_pw,
+ wpabuf_len(pw) * 2),
+ wpabuf_len(pw) * 2 + 1,
+ wpabuf_head(pw), wpabuf_len(pw));
+ }
if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
!wps->ap_nfc_dev_pw) {
@@ -1623,6 +1953,8 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
{
+ wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
+ hapd->conf->iface);
hostapd_wps_nfc_clear(hapd->wps);
}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
index 4e5026b45b6c6..204bd820a5474 100644
--- a/src/ap/wps_hostapd.h
+++ b/src/ap/wps_hostapd.h
@@ -16,6 +16,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
int hostapd_init_wps_complete(struct hostapd_data *hapd);
void hostapd_deinit_wps(struct hostapd_data *hapd);
void hostapd_update_wps(struct hostapd_data *hapd);
+void hostapd_wps_eap_completed(struct hostapd_data *hapd);
int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
const char *uuid, const char *pin, int timeout);
int hostapd_wps_button_pushed(struct hostapd_data *hapd,
@@ -35,6 +36,10 @@ int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
const struct wpabuf *data);
struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
int ndef);
+struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+ const struct wpabuf *req,
+ const struct wpabuf *sel);
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
@@ -60,6 +65,10 @@ static inline void hostapd_update_wps(struct hostapd_data *hapd)
{
}
+static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+}
+
static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
const u8 *addr,
char *buf, size_t buflen)
diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
new file mode 100644
index 0000000000000..8f77015ef57fb
--- /dev/null
+++ b/src/ap/x_snoop.c
@@ -0,0 +1,123 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ if (!conf->isolate) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: ap_isolate must be enabled for x_snoop");
+ return -1;
+ }
+
+ if (conf->bridge[0] == '\0') {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Bridge must be configured for x_snoop");
+ return -1;
+ }
+
+ if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable hairpin_mode on the bridge port");
+ return -1;
+ }
+
+ if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable proxyarp on the bridge port");
+ return -1;
+ }
+
+ if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+ 1)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type)
+{
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct l2_packet_data *l2;
+
+ l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
+ if (l2 == NULL) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to initialize L2 packet processing %s",
+ strerror(errno));
+ return NULL;
+ }
+
+ if (l2_packet_set_packet_filter(l2, type)) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to set L2 packet filter for type: %d",
+ type);
+ l2_packet_deinit(l2);
+ return NULL;
+ }
+
+ return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *buf,
+ size_t len)
+{
+ int res;
+ u8 addr[ETH_ALEN];
+ u8 *dst_addr = buf;
+
+ if (!(dst_addr[0] & 0x01))
+ return;
+
+ wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+ MACSTR " -> " MACSTR " (len %u)",
+ MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+ /* save the multicast destination address for restoring it later */
+ os_memcpy(addr, buf, ETH_ALEN);
+
+ os_memcpy(buf, sta->addr, ETH_ALEN);
+ res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "x_snoop: Failed to send mcast to ucast converted packet to "
+ MACSTR, MAC2STR(sta->addr));
+ }
+
+ /* restore the multicast destination address */
+ os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+ hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+ hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+}
diff --git a/src/ap/x_snoop.h b/src/ap/x_snoop.h
new file mode 100644
index 0000000000000..e43a78d0d83d7
--- /dev/null
+++ b/src/ap/x_snoop.h
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, u8 *buf,
+ size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+ return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+ void (*handler)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ enum l2_packet_filter_type type)
+{
+ return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+ struct sta_info *sta, void *buf,
+ size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */