Merge hostapd upstream into master.
Current as of:
http://w1.fi/cgit/hostap/commit/?id=ca68a8b561c48393c8ba25055ce294caaa3ac008
Note that this resolves the merge conflicts, but does not compile.
A second commit on top of this one is needed to get a working hostapd.
Change-Id: Ib1f533ef8f3399aaec7d4bc6708f7ea126a2c39c
diff --git a/README.google b/README.google
new file mode 100644
index 0000000..ab37ab8
--- /dev/null
+++ b/README.google
@@ -0,0 +1,33 @@
+Instructions to sync with the upstream hostap.
+
+Do this once:
+ git remote add upstream git://w1.fi/hostap.git
+
+You can use "git remote -v" to check if the upstream
+remote already exists and points to the right place.
+
+To merge with upstream:
+ git fetch -t upstream
+ git checkout upstream/master
+ git push gfiber-internal HEAD:refs/heads/upstream
+
+At this point, upstream/master is a pristine copy of the
+hostapd upstream. So now we want to merge the changes into
+our local, modified tree.
+
+ repo start merge_from_upstream .
+ git merge upstream/master
+
+Resolve any conflicts, and commit.
+
+ repo upload .
+
+This will almost certainly warn that there are an exceptionally
+large number of commits and make you type 'yes'. The vast, vast
+majority of those commits will already exist in Gerrit, from
+when we pushed HEAD:refs/heads/upstream. These will be detected
+as duplicates and not appear in your code review list. So type
+'yes' to upload, and expect just the merge commit (where any
+conflicts were resolved) to appear in your code review list.
+
+You send the merge commit for review.
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 3c7bd6f..b95cbcd 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -829,6 +829,7 @@
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
+OBJS += ../src/ap/rm.o
CFLAGS += -DNEED_AP_MLME
endif
ifdef CONFIG_IEEE80211N
@@ -897,6 +898,13 @@
LIBS_h += -lsqlite3
endif
+ifdef CONFIG_CLIENT_TAXONOMY
+CFLAGS += -DCONFIG_CLIENT_TAXONOMY
+OBJS += ../src/ap/taxonomy.o
+endif
+
+OBJS += ../src/ap/steering.o
+
ALL=hostapd hostapd_cli
all: verify_config $(ALL)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 1186644..6846b99 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3248,9 +3248,12 @@
pos++;
WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
bss->bss_load_test_set = 1;
+#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strcmp(buf, "radio_measurements") == 0) {
bss->radio_measurements = atoi(pos);
-#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strcmp(buf, "neighbor_ap_list_file") == 0) {
+ os_free(bss->neighbor_ap_list_file);
+ bss->neighbor_ap_list_file = os_strdup(pos);
} else if (os_strcmp(buf, "vendor_elements") == 0) {
struct wpabuf *elems;
size_t len = os_strlen(pos);
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4cde2b5..88c2dbc 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -314,3 +314,8 @@
# http://wireless.kernel.org/en/users/Documentation/acs
#
#CONFIG_ACS=y
+#
+# Client Taxonomy
+# Keeps track of signatures derived from Assoc and Probe frames per-STA.
+# These can be used to identfy the Wifi chipset/driver.
+#CONFIG_CLIENT_TAXONOMY=y
diff --git a/hostapd/main.c b/hostapd/main.c
index dd389a8..0c8686a 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -35,6 +35,10 @@
};
static struct hapd_global global;
+char *alivemon_path = NULL;
+char *taxonomy_path = NULL;
+char *steering_timestamp_path = NULL;
+char *request_logging_path = NULL;
#ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -442,6 +446,7 @@
"options:\n"
" -h show this usage\n"
" -d show more debug messages (-dd for even more)\n"
+ " -A alivemonitor file\n"
" -B run daemon in the background\n"
" -e entropy file\n"
" -g global control interface path\n"
@@ -456,6 +461,10 @@
" (records all messages regardless of debug verbosity)\n"
#endif /* CONFIG_DEBUG_LINUX_TRACING */
" -t include timestamps in some debug messages\n"
+ " -L log probe and assoc requests in this directory, with\n"
+ " a timestamp in each file of the write time plus\n"
+ " BANDSTEERING_DELAY_SECONDS\n"
+ " -S bandsteer based on logged probes in this directory\n"
" -v show hostapd version\n");
exit(1);
@@ -563,7 +572,7 @@
interfaces.global_ctrl_sock = -1;
for (;;) {
- c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
+ c = getopt(argc, argv, "b:A:Bde:f:F:hKP:Ttu:vg:G:S:L:");
if (c < 0)
break;
switch (c) {
@@ -575,6 +584,9 @@
if (wpa_debug_level > 0)
wpa_debug_level--;
break;
+ case 'A':
+ alivemon_path = optarg;
+ break;
case 'B':
daemonize++;
break;
@@ -584,6 +596,11 @@
case 'f':
log_file = optarg;
break;
+#ifdef CONFIG_CLIENT_TAXONOMY
+ case 'F':
+ taxonomy_path = optarg;
+ break;
+#endif /* CONFIG_CLIENT_TAXONOMY */
case 'K':
wpa_debug_show_keys++;
break;
@@ -624,6 +641,12 @@
case 'u':
return gen_uuid(optarg);
#endif /* CONFIG_WPS */
+ case 'S':
+ steering_timestamp_path = optarg;
+ break;
+ case 'L':
+ request_logging_path = optarg;
+ break;
default:
usage();
break;
@@ -634,7 +657,8 @@
num_bss_configs == 0)
usage();
- wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
+ // Not needed on our system since we add a log prefix elsewhere
+ //wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
if (log_file)
wpa_debug_open_file(log_file);
@@ -734,7 +758,7 @@
goto out;
}
- ret = 0;
+ ret = interfaces.terminate_on_error;
out:
hostapd_global_ctrl_iface_deinit(&interfaces);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 7b4a7ea..00848ba 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -553,6 +553,8 @@
int radio_measurements;
int vendor_vht;
+
+ char *neighbor_ap_list_file;
};
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 78a1f7c..2f007c5 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -222,7 +222,7 @@
if (!iface->olbc &&
ap_list_beacon_olbc(iface, ap)) {
iface->olbc = 1;
- wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
+ wpa_printf(MSG_INFO, "OLBC AP detected: " MACSTR
" (channel %d) - enable protection",
MAC2STR(ap->addr), ap->channel);
set_beacon++;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 7009855..1a46d4d 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -29,6 +29,10 @@
#include "beacon.h"
#include "hs20.h"
#include "dfs.h"
+#include "ap/steering.h"
+#ifdef CONFIG_CLIENT_TAXONOMY
+#include "taxonomy.h"
+#endif /* CONFIG_CLIENT_TAXONOMY */
#ifdef NEED_AP_MLME
@@ -551,6 +555,10 @@
int noack;
enum ssid_match_result res;
+ if (request_logging_path) {
+ maybe_write_timestamp_file(mgmt->sa, hapd, LOG_PROBE);
+ }
+
ie = mgmt->u.probe_req.variable;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
return;
@@ -643,6 +651,13 @@
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_CLIENT_TAXONOMY
+ if (sta) {
+ hostapd_taxonomy_probe_req(sta, ie, ie_len);
+ hostapd_write_sta_taxonomy(sta);
+ }
+#endif /* CONFIG_CLIENT_TAXONOMY */
+
res = ssid_match(hapd, elems.ssid, elems.ssid_len,
elems.ssid_list, elems.ssid_list_len);
if (res == NO_SSID_MATCH) {
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 80e4c2e..55f429a 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -5,7 +5,7 @@
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
-
+#define _GNU_SOURCE
#include "utils/includes.h"
#include "utils/common.h"
@@ -32,7 +32,9 @@
#include "hw_features.h"
#include "dfs.h"
#include "beacon.h"
-
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *req_ies, size_t req_ies_len, int reassoc)
@@ -637,7 +639,7 @@
return;
hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ HOSTAPD_LEVEL_INFO, "authentication OK (FT)");
sta->flags |= WLAN_STA_AUTH;
hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
@@ -1076,10 +1078,16 @@
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
+ static struct {
+ int all, beacon, probe_req, tx_status, tx_status_mgmt;
+ } counts;
+ static struct os_reltime last_count_print_time;
+ struct os_reltime now;
struct hostapd_data *hapd = ctx;
#ifndef CONFIG_NO_STDOUT_DEBUG
- int level = MSG_DEBUG;
+ int level = MSG_INFO;
+ counts.all++;
if (event == EVENT_RX_MGMT && data->rx_mgmt.frame &&
data->rx_mgmt.frame_len >= 24) {
const struct ieee80211_hdr *hdr;
@@ -1087,11 +1095,39 @@
hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
fc = le_to_host16(hdr->frame_control);
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
- WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
level = MSG_EXCESSIVE;
+ counts.beacon++;
+ }
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
- WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ)
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
level = MSG_EXCESSIVE;
+ counts.probe_req++;
+ }
+ }
+ if (event == EVENT_TX_STATUS) {
+ if (data->tx_status.type == WLAN_FC_TYPE_MGMT) {
+ level = MSG_EXCESSIVE;
+ counts.tx_status_mgmt++;
+ } else {
+ counts.tx_status++;
+ }
+ }
+
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &last_count_print_time, 5)) {
+ wpa_dbg(hapd->msg_ctx, MSG_INFO,
+ "Events: %d beacon=%d probe=%d tx=%d txm=%d",
+ counts.all, counts.beacon, counts.probe_req,
+ counts.tx_status, counts.tx_status_mgmt);
+ if (counts.all && alivemon_path) {
+ int fd = open(alivemon_path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW,
+ 0666);
+ if (fd >= 0) close(fd);
+ }
+ last_count_print_time = now;
+ memset(&counts, 0, sizeof(counts));
}
wpa_dbg(hapd->msg_ctx, level, "Event %s (%d) received",
@@ -1245,6 +1281,13 @@
hostapd_channel_list_updated(
hapd->iface, data->channel_list_changed.initiator);
break;
+ case EVENT_INTERFACE_STATUS:
+ if (data->interface_status.ievent == EVENT_INTERFACE_REMOVED) {
+ wpa_printf(MSG_ERROR, "Interface removed by someone; exiting.\n");
+ hapd->iface->interfaces->terminate_on_error++;
+ eloop_terminate();
+ }
+ break;
case EVENT_DFS_CAC_STARTED:
if (!data)
break;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 75cc24e..788b118 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -14,6 +14,9 @@
#include "ap_config.h"
#include "drivers/driver.h"
+extern char *alivemon_path;
+extern char *taxonomy_path;
+
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 96744c4..6d84ee4 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -221,6 +221,15 @@
}
+static int experiment(const char *name)
+{
+ char filename[128];
+ snprintf(filename, sizeof(filename), "/config/experiments/%s.active",
+ name);
+ return access(filename, F_OK) == 0;
+}
+
+
#ifdef CONFIG_IEEE80211N
static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
{
@@ -305,18 +314,22 @@
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",
- iface->conf->channel,
- iface->conf->channel +
- iface->conf->secondary_channel * 4);
- iface->conf->secondary_channel = 0;
- 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.
- */
+ if (!experiment("NoAutoNarrowWifiChannel")) {
+ wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+ "channel pri=%d sec=%d based on overlapping BSSes",
+ iface->conf->channel,
+ iface->conf->channel +
+ iface->conf->secondary_channel * 4);
+ iface->conf->secondary_channel = 0;
+ 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.
+ */
+ }
+ } else {
+ wpa_printf(MSG_INFO, "NoAutoNarrowWifiChannel: would have reduced to 20 MHz.");
}
}
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index 99aa04d..b203b5e 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -292,7 +292,7 @@
* this is not really a reliable verification. */
hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP,
- HOSTAPD_LEVEL_DEBUG,
+ HOSTAPD_LEVEL_INFO,
"Removing STA due to IAPP ADD-notify");
ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 5b26558..62b40d2 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -40,6 +40,11 @@
#include "wnm_ap.h"
#include "ieee802_11.h"
#include "dfs.h"
+#include "rm.h"
+#include "ap/steering.h"
+#ifdef CONFIG_CLIENT_TAXONOMY
+#include "taxonomy.h"
+#endif /* CONFIG_CLIENT_TAXONOMY */
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -235,7 +240,7 @@
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
+ HOSTAPD_LEVEL_INFO,
"authentication OK (shared key)");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
@@ -304,7 +309,7 @@
return;
hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
+ HOSTAPD_LEVEL_INFO, "authentication OK (FT)");
sta->flags |= WLAN_STA_AUTH;
mlme_authenticate_indication(hapd, sta);
}
@@ -1056,10 +1061,16 @@
else
ap_sta_no_session_timeout(hapd, sta);
+#ifdef CONFIG_FINGERPRINT
+ sta->duration_auth[sta->duration_auth_idx++] =
+ FINGERPRINT_DURATION_PRESENT | le_to_host16(mgmt->duration);
+ sta->duration_auth_idx %= FINGERPRINT_NSAMPLES;
+#endif /* CONFIG_FINGERPRINT */
+
switch (auth_alg) {
case WLAN_AUTH_OPEN:
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
- HOSTAPD_LEVEL_DEBUG,
+ HOSTAPD_LEVEL_INFO,
"authentication OK (open system)");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
@@ -1684,6 +1695,7 @@
const u8 *pos;
int left, i;
struct sta_info *sta;
+ struct os_reltime now, probe_time, bandsteer_until;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
@@ -1692,6 +1704,11 @@
return;
}
+ if (request_logging_path) {
+ maybe_write_timestamp_file(mgmt->sa, hapd, LOG_ASSOC);
+ }
+
+
#ifdef CONFIG_TESTING_OPTIONS
if (reassoc) {
if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
@@ -1741,6 +1758,34 @@
}
sta = ap_get_sta(hapd, mgmt->sa);
+
+ if (steering_timestamp_path) {
+ /* If the STA has successfully associated on the bandsteering target
+ * interface, reset the bandsteering state so that we try to bandsteer it
+ * again.
+ */
+ if (read_timestamp_file(mgmt->sa, LOG_ASSOC_SUCCESSFUL, STEERING_PATH, NULL)) {
+ delete_timestamp_file(mgmt->sa, LOG_ASSOC_SUCCESSFUL, STEERING_PATH);
+ delete_timestamp_file(mgmt->sa, LOG_ASSOC, LOGGING_PATH);
+ }
+
+ os_get_reltime(&now);
+ if (read_timestamp_file(mgmt->sa, LOG_PROBE, STEERING_PATH, &probe_time)) {
+ if (!read_timestamp_file(mgmt->sa, LOG_ASSOC, LOGGING_PATH,
+ &bandsteer_until) ||
+ os_reltime_before(&now, &bandsteer_until)) {
+ wpa_printf(MSG_INFO, "Rejecting " MACSTR " until %d sec %d usec",
+ MAC2STR(mgmt->sa), bandsteer_until.sec,
+ bandsteer_until.usec);
+ resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+ goto fail;
+ } else {
+ write_timestamp_file(mgmt->sa, hapd, LOG_BANDSTEERING_FAILED, &now);
+ }
+ }
+ }
+
+
#ifdef CONFIG_IEEE80211R
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
@@ -1873,6 +1918,11 @@
* remove the STA immediately. */
sta->timeout_next = STA_NULLFUNC;
+#ifdef CONFIG_CLIENT_TAXONOMY
+ hostapd_taxonomy_assoc_req(sta, pos, left);
+ hostapd_write_sta_taxonomy(sta);
+#endif /* CONFIG_CLIENT_TAXONOMY */
+
fail:
send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
}
@@ -1889,7 +1939,7 @@
return;
}
- wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
+ wpa_printf(MSG_INFO, "disassocation: STA=" MACSTR " reason_code=%d",
MAC2STR(mgmt->sa),
le_to_host16(mgmt->u.disassoc.reason_code));
@@ -1941,7 +1991,7 @@
return;
}
- wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
+ wpa_msg(hapd->msg_ctx, MSG_INFO, "deauthentication: STA=" MACSTR
" reason_code=%d",
MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
@@ -2119,6 +2169,9 @@
if (hapd->public_action_cb || hapd->public_action_cb2)
return 1;
break;
+ case WLAN_ACTION_RADIO_MEASUREMENT:
+ hostapd_rm_action(hapd, mgmt, len);
+ return 1;
case WLAN_ACTION_VENDOR_SPECIFIC:
if (hapd->vendor_action_cb) {
if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
@@ -2399,6 +2452,13 @@
ap_sta_set_authorized(hapd, sta, 1);
}
+
+ if (request_logging_path && !steering_timestamp_path) {
+ /* Log the successful associate, so that the interface steering to this one
+ * knows it can keep bandsteering if it sees another assoc request. */
+ write_timestamp_file(mgmt->da, hapd, LOG_ASSOC_SUCCESSFUL, NULL);
+ }
+
if (reassoc)
mlme_reassociate_indication(hapd, sta);
else
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 863a539..5a66291 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1774,7 +1774,7 @@
* request and we cannot continue EAP processing (EAP-Failure
* could only be sent if the EAP peer actually replied).
*/
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR,
+ wpa_dbg(hapd->msg_ctx, MSG_INFO, "EAP Timeout, STA " MACSTR,
MAC2STR(sta->addr));
sm->eap_if->portEnabled = FALSE;
@@ -2590,7 +2590,7 @@
* EAP-FAST with anonymous provisioning, may require another
* EAPOL authentication to be started to complete connection.
*/
- wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
+ wpa_dbg(hapd->msg_ctx, MSG_INFO, "IEEE 802.1X: Force "
"disconnection after EAP-Failure");
/* Add a small sleep to increase likelihood of previously
* requested EAP-Failure TX getting out before this should the
diff --git a/src/ap/rm.c b/src/ap/rm.c
new file mode 100644
index 0000000..4c9279e
--- /dev/null
+++ b/src/ap/rm.c
@@ -0,0 +1,172 @@
+/*
+ * hostapd / RM (Radio Management)
+ * Copyright (c) 2015, Google, 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/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+
+/* Common clients only look at the first 6 APs in the list anyway. */
+#define NR_MAX_APS 6
+
+/*
+ * bssinfo is defined in 802.11-2012 Figure 8-216,
+ * Table 8-114, and Figure 8-217.
+ */
+static u32 cap_to_bssinfo(u32 cap)
+{
+ u32 ap_reach = 2 << 0; // Reachability unknown
+ u32 security = 1 << 2;
+ u32 key_scope = 0 << 3;
+ u32 capabilities = 0;
+
+ if (cap & (1 << 8)) capabilities |= (1 << 4); // Spectrum Management
+ capabilities |= (0 << 5); // QoS
+ if (cap & (1 << 11)) capabilities |= (1 << 6); // APSD
+ if (cap & (1 << 12)) capabilities |= (1 << 7); // Radio Measurement
+ if (cap & (1 << 14)) capabilities |= (1 << 8); // Delayed Block Ack
+ if (cap & (1 << 15)) capabilities |= (1 << 9); // Immed Block Ack
+
+ return ap_reach | security | key_scope | capabilities;
+}
+
+static void rm_send_neighbor_report(struct hostapd_data *hapd, const u8 *addr,
+ u8 dialog_token)
+{
+ #define ELEMSIZ sizeof(struct rrm_neighbor_report_element)
+ u8 buf[sizeof(struct ieee80211_mgmt) + NR_MAX_APS * ELEMSIZ];
+ struct ieee80211_mgmt *m = (struct ieee80211_mgmt *)buf;
+ u8 *v = m->u.action.u.rm_action.variable;
+ struct rrm_neighbor_report_element *elements =
+ (struct rrm_neighbor_report_element *) v;
+ int num_aps = 0;
+ size_t len;
+
+ /* TODO: if the neighbor request included a specific SSID,
+ * we need to compare with hapd->conf->ssid. If the SSID
+ * does not match, we should send back an action frame with
+ * some kind of refused or mismatch error code. */
+
+ os_memset(&buf, 0, sizeof(buf));
+ m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(m->da, addr, ETH_ALEN);
+ os_memcpy(m->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN);
+ m->u.action.category = WLAN_ACTION_RADIO_MEASUREMENT;
+ m->u.action.u.rm_action.action = WLAN_RRM_NEIGHBOR_REPORT_RESPONSE;
+ m->u.action.u.rm_action.dialog_token = dialog_token;
+
+ if (hapd->conf->neighbor_ap_list_file) {
+ FILE *f = fopen(hapd->conf->neighbor_ap_list_file, "r");
+ char line[128];
+
+ while (f && fgets(line, sizeof(line), f)) {
+ char *start = line;
+ char *tok, *save = NULL;
+ const char *bssid = NULL;
+ u32 cap = 0, bssinfo = 0;
+ u8 channel = 0, phy_type = 0;
+ struct rrm_neighbor_report_element *elem;
+
+ if (num_aps >= NR_MAX_APS) {
+ break;
+ }
+
+ while ((tok = strtok_r(start, "|", &save)) != NULL) {
+ start = NULL;
+ if (strncmp(tok, "bssid:", 6) == 0) {
+ bssid = tok + 6;
+ }
+ if (strncmp(tok, "cap:", 4) == 0) {
+ cap = strtol(tok + 4, NULL, 0);
+ }
+ if (strncmp(tok, "freq:", 5) == 0) {
+ u32 freq = strtol(tok + 5, NULL, 0);
+ ieee80211_freq_to_chan(freq, &channel);
+ }
+ if (strncmp(tok, "phy:", 4) == 0) {
+ phy_type = (u8)strtol(tok + 4, NULL, 0);
+ }
+ }
+
+ if (bssid == NULL || *bssid == '\0') {
+ continue;
+ }
+
+ elem = &elements[num_aps];
+
+ if (hwaddr_aton(bssid, elem->bssid) != 0) {
+ continue;
+ }
+ elem->eid = WLAN_EID_NEIGHBOR_REPORT;
+ elem->len = sizeof(*elem) - 2;
+ bssinfo = cap_to_bssinfo(cap);
+ WPA_PUT_LE32((u8 *)&elem->bssinfo, bssinfo);
+ elem->op_class = 0; // TODO: regulatory class.
+ elem->channel = channel;
+ elem->phy_type = phy_type;
+ num_aps++;
+ }
+ if (f) {
+ fclose(f);
+ } else {
+ hostapd_logger(hapd, m->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE, "unable to open %s",
+ hapd->conf->neighbor_ap_list_file);
+ }
+ }
+
+ len = (u8 *)&elements[num_aps] - buf;
+ hostapd_logger(hapd, m->da, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE, "sending Neighbor List with %d APs",
+ num_aps);
+ if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
+ wpa_printf(MSG_WARNING, "rm_send_neighbor_report: send failed");
+}
+
+
+/*
+ * Process a WLAN_ACTION_RADIO_MEASUREMENT frame.
+ */
+void hostapd_rm_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ int action_code;
+ struct sta_info *sta = ap_get_sta(hapd, mgmt->sa);
+ u8 dialog_token;
+
+ /* Check that the request comes from a valid station. */
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_NOTICE,
+ "rm action received is not from an associated "
+ "station");
+ /* TODO: respond with action frame refused status code */
+ return;
+ }
+
+ action_code = mgmt->u.action.u.rm_action.action;
+ dialog_token = mgmt->u.action.u.wmm_action.dialog_token;
+ switch (action_code) {
+ case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+ rm_send_neighbor_report(hapd, mgmt->sa, dialog_token);
+ return;
+ }
+
+ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "hostapd_rm_action - unknown action code %d",
+ action_code);
+}
diff --git a/src/ap/rm.h b/src/ap/rm.h
new file mode 100644
index 0000000..81fb89e
--- /dev/null
+++ b/src/ap/rm.h
@@ -0,0 +1,15 @@
+/*
+ * hostapd / RM (Radio Measurements)
+ * Copyright 2015, Google, Inc
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RM_H
+#define RM_H
+
+void hostapd_rm_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len);
+
+#endif /* RM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 20847d5..17ecad5 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -335,6 +335,48 @@
}
+#ifdef CONFIG_CLIENT_TAXONOMY
+void hostapd_write_sta_taxonomy(struct sta_info *sta)
+{
+ int i;
+ char tmpfile[1024 + 4], filename[1024];
+ FILE *f;
+
+ if (taxonomy_path == NULL) {
+ return;
+ }
+
+ if ((os_strlen(sta->probe_ie_taxonomy) == 0) ||
+ (os_strlen(sta->assoc_ie_taxonomy) == 0)) {
+ return;
+ }
+
+ snprintf(filename, sizeof(filename), "%s/" MACSTR,
+ taxonomy_path, MAC2STR(sta->addr));
+ snprintf(tmpfile, sizeof(tmpfile), "%s/" MACSTR ".tmp",
+ taxonomy_path, MAC2STR(sta->addr));
+ if ((f = fopen(tmpfile, "w")) == NULL) {
+ wpa_printf(MSG_ERROR, "open %s failed", tmpfile);
+ return;
+ }
+ fprintf(f, "wifi|probe:%s|assoc:%s", sta->probe_ie_taxonomy,
+ sta->assoc_ie_taxonomy);
+ fclose(f);
+ if (rename(tmpfile, filename)) {
+ wpa_printf(MSG_ERROR, "rename %s failed", tmpfile);
+ unlink(tmpfile);
+ }
+}
+static void hostapd_remove_sta_taxonomy(struct sta_info *sta)
+{
+ char filename[1024];
+
+ snprintf(filename, sizeof(filename), "%s/" MACSTR,
+ taxonomy_path, MAC2STR(sta->addr));
+ unlink(filename);
+}
+#endif /* CONFIG_CLIENT_TAXONOMY */
+
/**
* ap_handle_timer - Per STA timer handler
* @eloop_ctx: struct hostapd_data *
@@ -357,6 +399,9 @@
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "deauthenticated due to "
"local deauth request");
+#ifdef CONFIG_CLIENT_TAXONOMY
+ hostapd_remove_sta_taxonomy(sta);
+#endif /* CONFIG_CLIENT_TAXONOMY */
ap_free_sta(hapd, sta);
return;
}
@@ -393,14 +438,14 @@
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,
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
"Station " MACSTR " has been active %is ago",
MAC2STR(sta->addr), inactive_sec);
sta->timeout_next = STA_NULLFUNC;
next_time = hapd->conf->ap_max_inactivity + fuzz -
inactive_sec;
} else {
- wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
"Station " MACSTR " has been "
"inactive too long: %d sec, max allowed: %d",
MAC2STR(sta->addr), inactive_sec,
@@ -423,6 +468,10 @@
next_time = hapd->conf->ap_max_inactivity;
}
+#ifdef CONFIG_CLIENT_TAXONOMY
+ hostapd_write_sta_taxonomy(sta);
+#endif /* CONFIG_CLIENT_TAXONOMY */
+
skip_poll:
if (next_time) {
wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
@@ -698,8 +747,8 @@
void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
- wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
- hapd->conf->iface, MAC2STR(sta->addr));
+ wpa_printf(MSG_INFO, "%s: disassociate STA " MACSTR " (reason=0x%04x)",
+ hapd->conf->iface, MAC2STR(sta->addr), reason);
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);
@@ -737,8 +786,8 @@
void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
u16 reason)
{
- wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
- hapd->conf->iface, MAC2STR(sta->addr));
+ wpa_printf(MSG_INFO, "%s: deauthenticate STA " MACSTR " (reason=0x%04x)",
+ hapd->conf->iface, MAC2STR(sta->addr), reason);
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);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 52a9997..ce8d9fb 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -167,6 +167,12 @@
u16 last_seq_ctrl;
/* Last Authentication/(Re)Association Request/Action frame subtype */
u8 last_subtype;
+
+#ifdef CONFIG_CLIENT_TAXONOMY
+#define TAXONOMY_STRING_LEN 384
+ char probe_ie_taxonomy[TAXONOMY_STRING_LEN];
+ char assoc_ie_taxonomy[TAXONOMY_STRING_LEN];
+#endif /* CONFIG_CLIENT_TAXONOMY */
};
@@ -197,6 +203,9 @@
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);
+#ifdef CONFIG_CLIENT_TAXONOMY
+void hostapd_write_sta_taxonomy(struct sta_info *sta);
+#endif /* CONFIG_CLIENT_TAXONOMY */
void hostapd_free_stas(struct hostapd_data *hapd);
void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
diff --git a/src/ap/steering.c b/src/ap/steering.c
new file mode 100644
index 0000000..2916262
--- /dev/null
+++ b/src/ap/steering.c
@@ -0,0 +1,250 @@
+/*
+ * hostapd / Interface steering
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "steering.h"
+
+static int get_timestamp_filename(const u8 *mac,
+ logged_request_type type,
+ steering_path_type path_type, char *buf,
+ size_t len) {
+ char *path = (path_type == STEERING_PATH ?
+ steering_timestamp_path : request_logging_path);
+
+ if (path == NULL) {
+ return 0;
+ }
+
+ if (os_snprintf(buf, len, "%s/" COMPACT_MACSTR ".%d", path,
+ MAC2STR(mac), type) < 0) {
+ wpa_printf(MSG_ERROR, "os_snprintf couldn't format filename: %s",
+ strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+int write_timestamp_file(const u8 *mac,
+ const struct hostapd_data *hapd,
+ logged_request_type type,
+ const struct os_reltime *timestamp) {
+ FILE *f;
+ char filename[1024], tmp_filename[1024];
+ int success = 0;
+
+ if (!get_timestamp_filename(mac, type, LOGGING_PATH, filename,
+ sizeof(filename))) {
+ return 0;
+ }
+
+ /* Create a temporary filename to prevent multiple interfaces on the same band
+ * from touching each others' writes.
+ */
+ if (os_snprintf(tmp_filename, sizeof(tmp_filename), "%s%s", filename,
+ os_strrchr(hapd->iface->config_fname, '.')) < 0) {
+ wpa_printf(MSG_ERROR, "os_snprintf couldn't format temp filename: %s",
+ strerror(errno));
+ return 0;
+ }
+
+ if ((f = fopen(tmp_filename, "w")) == NULL) {
+ wpa_printf(MSG_ERROR, "fopen(%s) for write: %s", tmp_filename,
+ strerror(errno));
+ return 0;
+ }
+
+ if (timestamp) {
+ if (fprintf(f, "%d %d", timestamp->sec, timestamp->usec) < 0) {
+ wpa_printf(MSG_ERROR, "fprintf to %s: %s", tmp_filename, strerror(errno));
+ } else {
+ success = 1;
+ }
+ }
+
+ if (fclose(f) == EOF) {
+ wpa_printf(MSG_ERROR, "fclose(%s): %s", tmp_filename, strerror(errno));
+ return 0;
+ }
+
+ if (rename(tmp_filename, filename) != 0) {
+ wpa_printf(MSG_ERROR, "rename(%s, %s): %s", tmp_filename, filename,
+ strerror(errno));
+ return 0;
+ }
+
+ return success;
+}
+
+int maybe_write_timestamp_file(const u8 *mac,
+ const struct hostapd_data *hapd,
+ logged_request_type type) {
+ struct os_reltime now, prev_logged_timestamp, new_timestamp;
+ if (!request_logging_path) {
+ return 0;
+ }
+
+ os_get_reltime(&now);
+ if (garbage_collect_timestamp_files() == -1) {
+ wpa_printf(MSG_ERROR,
+ "Garbage collecting steering timestamp files failed: %s",
+ strerror(errno));
+ return 0;
+ }
+ if (!read_timestamp_file(mac, type, LOGGING_PATH, &prev_logged_timestamp) ||
+ os_reltime_expired(&now, &prev_logged_timestamp,
+ BANDSTEERING_EXPIRATION_SECONDS)) {
+ new_timestamp.sec = now.sec + BANDSTEERING_DELAY_SECONDS;
+ new_timestamp.usec = now.usec;
+ if (!write_timestamp_file(mac, hapd, type, &new_timestamp)) {
+ wpa_printf(MSG_ERROR, "Failed to write timestamp file.");
+ return 0;
+ } else {
+ wpa_printf(MSG_INFO, "Set timestamp for " MACSTR " (type=%d)",
+ MAC2STR(mac), type);
+ return 1;
+ }
+ }
+}
+
+int read_timestamp_file(const u8 *mac,
+ logged_request_type type,
+ steering_path_type path_type,
+ struct os_reltime *timestamp) {
+ FILE *f;
+ char filename[1024];
+ int success = 1;
+ struct stat st;
+ os_time_t sec = 0, usec = 0;
+
+ if (!get_timestamp_filename(mac, type, path_type, filename,
+ sizeof(filename))) {
+ return 0;
+ }
+
+ if (stat(filename, &st) == -1) {
+ return 0;
+ }
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "open(%s) for read: %s", filename, strerror(errno));
+ return 0;
+ }
+
+ if (timestamp) {
+ if (fscanf(f, "%d %d", ×tamp->sec, ×tamp->usec) != 2) {
+ wpa_printf(MSG_ERROR, "fscanf from %s: %s", filename, strerror(errno));
+ success = 0;
+ }
+ }
+
+ if (fclose(f) == EOF) {
+ wpa_printf(MSG_ERROR, "fclose(%s): %s", filename, strerror(errno));
+ return 0;
+ }
+
+ return success;
+}
+
+int delete_timestamp_file(const u8 *mac,
+ logged_request_type type,
+ steering_path_type path_type) {
+ char filename[1024];
+ struct stat st;
+
+ if (!get_timestamp_filename(mac, type, path_type, filename,
+ sizeof(filename))) {
+ return 0;
+ }
+
+ if (stat(filename, &st) == -1) {
+ return 1;
+ }
+
+ if (unlink(filename) == -1) {
+ wpa_printf(MSG_ERROR, "unlink(%s): %s", filename, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+int file_ctime_lt(const struct dirent **a, const struct dirent **b) {
+ struct stat astat, bstat;
+
+ /* If we can't stat both of the files, give up and say they're equivalent. */
+ if (stat((*a)->d_name, &astat) == -1 || stat((*b)->d_name, &bstat) == -1) {
+ return 0;
+ }
+
+ return astat.st_ctime - bstat.st_ctime;
+}
+
+/* Only garbage collect LOG_PROBE files. */
+int should_garbage_collect(const struct dirent *name) {
+ char *extension = os_strrchr(name->d_name, '.');
+ char buf[4];
+ os_snprintf(buf, sizeof(buf), ".%d", LOG_PROBE);
+
+ return os_strncmp(extension, buf, sizeof(buf)) == 0;
+}
+
+int garbage_collect_timestamp_files(const char *path) {
+ int num_timestamp_files = 0, num_timestamp_files_deleted = 0, i = 0;
+ struct dirent **namelist;
+ char original_cwd[1024];
+ char *filename;
+ int error = 0;
+
+ if (getcwd(original_cwd, sizeof(original_cwd)) == NULL) {
+ wpa_printf(MSG_ERROR, "getcwd(): %s", strerror(errno));
+ return -1;
+ }
+
+ if (chdir(request_logging_path) == -1) {
+ wpa_printf(MSG_ERROR, "chdir(%s): %s",
+ request_logging_path, strerror(errno));
+ return -1;
+ }
+
+ num_timestamp_files = scandir(request_logging_path, &namelist,
+ should_garbage_collect, file_ctime_lt);
+ for (i = 0; i < num_timestamp_files; ++i) {
+ if (MAX_STEERING_TIMESTAMP_FILES <
+ /* The -2 is because scandir includes "." and "..". */
+ (num_timestamp_files - 2) - num_timestamp_files_deleted) {
+ filename = namelist[i]->d_name;
+ if (filename[0] != '.' && !error) {
+ if (unlink(filename) == -1) {
+ wpa_printf(MSG_ERROR, "unlink(%s): %s", filename, strerror(errno));
+ error = 1;
+ } else {
+ ++num_timestamp_files_deleted;
+ }
+ }
+ }
+ os_free(namelist[i]);
+ }
+ os_free(namelist);
+
+ if (chdir(original_cwd) == -1) {
+ wpa_printf(MSG_ERROR, "chdir(%s): %s", original_cwd, strerror(errno));
+ return -1;
+ }
+
+ return error ? -1 : num_timestamp_files_deleted;
+}
diff --git a/src/ap/steering.h b/src/ap/steering.h
new file mode 100644
index 0000000..a17d79c
--- /dev/null
+++ b/src/ap/steering.h
@@ -0,0 +1,81 @@
+/*
+ * hostapd / Interface steering
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef STEERING_H
+#define STEERING_H
+
+#define MAX_STEERING_TIMESTAMP_FILES 100
+/* 10 seconds is long enough to scan all channels on both bands at least twice
+ * at 100ms/channel.
+ */
+#define BANDSTEERING_DELAY_SECONDS 10
+#define BANDSTEERING_EXPIRATION_SECONDS 120
+
+extern char *steering_timestamp_path;
+extern char *request_logging_path;
+
+struct hostapd_data;
+enum hostapd_hw_mode;
+struct ieee80211_mgmt;
+struct os_reltime;
+
+typedef enum {
+ LOG_PROBE,
+ LOG_ASSOC,
+ LOG_BANDSTEERING_FAILED,
+ LOG_ASSOC_SUCCESSFUL,
+ NUM_LOGGED_REQUEST_TYPES } logged_request_type;
+typedef enum {
+ LOGGING_PATH,
+ STEERING_PATH,
+ NUM_STEERING_PATH_TYPES } steering_path_type;
+
+/**
+ * Writes timestamp for the source address in mgmt to request_logging_path.
+ * Returns 1 if the write succeeded, 0 otherwise.
+ */
+int write_timestamp_file(const u8 *mac,
+ const struct hostapd_data *hapd,
+ logged_request_type type,
+ const struct os_reltime *timestamp);
+
+/**
+ * Calls write_timestamp_file unless there is an existing file younger than
+ * BANDSTEERING_EXPIRATION_SECONDS. Also garbage collects before writing.
+ * Returns 0 on write or garbage collection failure, 1 otherwise.
+ */
+int maybe_write_timestamp_file(const u8 *mac,
+ const struct hostapd_data *hapd,
+ logged_request_type type);
+
+/**
+ * Reads a timestamp from either request_logging_path or steering_timestamp_path
+ * (based on path) for the source address in mgmt, putting the result in
+ * timestamp. Returns 1 if the read succeeded, 0 otherwise.
+ */
+int read_timestamp_file(const u8 *mac,
+ logged_request_type type,
+ steering_path_type path_type,
+ struct os_reltime *timestamp);
+
+/**
+ * Deletes a timestamp file from either request_logging_path or
+ * steering_timestamp_path (based on path) for the source address in mgmt.
+ * Returns 1 if the delete succeeded or the file does not exist, 0 otherwise.
+ */
+int delete_timestamp_file(const u8 *mac,
+ logged_request_type type,
+ steering_path_type path_type);
+
+/**
+ * Delete all but the most recent MAX_TIMESTAMP_FILES files of type LOG_PROBE in
+ * request_logging_path. Returns the number of files deleted.
+ */
+int garbage_collect_timestamp_files();
+
+#endif /* STEERING_H */
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
new file mode 100644
index 0000000..9a8812d
--- /dev/null
+++ b/src/ap/taxonomy.c
@@ -0,0 +1,178 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+/*
+ * Parse a series of IEs, as in Probe or Association packets,
+ * and render them to a descriptive string. The tag number of
+ * standard options is written to the string, while the vendor
+ * ID and subtag are written for vendor options.
+ *
+ * Example strings:
+ * 0,1,50,45,221(00904c,51)
+ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "sta_info.h"
+
+/* Copy a string with no funny schtuff allowed; only alphanumerics. */
+static void no_mischief_strncpy(char *dst, const char *src, size_t n)
+{
+ size_t i;
+ for (i = 0; i < n; i++) {
+ unsigned char s = src[i];
+ int is_lower = (s >= 'a' && s <= 'z');
+ int is_upper = (s >= 'A' && s <= 'Z');
+ int is_digit = (s >= '0' && s <= '9');
+ if (is_lower || is_upper || is_digit) {
+ /* TODO: if any manufacturer uses Unicode within the
+ * WPS header, it will get mangled here. */
+ dst[i] = s;
+ } else {
+ /* note that even spaces will be transformed to underscores,
+ * so 'Nexus 7' will turn into 'Nexus_7'. This is deliberate,
+ * to make the string easier to parse. */
+ dst[i] = '_';
+ }
+ }
+}
+
+static int get_wps_name(char *name, size_t name_len,
+ const u8 *data, size_t data_len)
+{
+ /* Inside the WPS IE are a series of sub-IEs, using two byte IDs
+ * and two byte lengths. We're looking for the model name, if
+ * present. */
+ while (data_len >= 4) {
+ u16 id, elen;
+ id = (data[0] << 8) | data[1];
+ elen = (data[2] << 8) | data[3];
+ data += 4;
+ data_len -= 4;
+
+ if (elen > data_len) {
+ return 0;
+ }
+
+ if (id == 0x1023) {
+ /* Model name, like 'Nexus 7' */
+ size_t n = (elen < name_len) ? elen : name_len;
+ no_mischief_strncpy(name, data, n);
+ return n;
+ }
+
+ data += elen;
+ data_len -= elen;
+ }
+
+ return 0;
+}
+
+static void ie_to_string(char *fstr, size_t fstr_len,
+ const u8 *ie, size_t ie_len)
+{
+ size_t flen = fstr_len - 1;
+ char htcap[7 + 4 + 1]; // ",htcap:" + %04hx + trailing NUL
+ char vhtcap[8 + 8 + 1]; // ",vhtcap:" + %08x + trailing NUL
+ #define WPS_NAME_LEN 32
+ char wps[WPS_NAME_LEN + 5 + 1]; // room to prepend ",wps:" + trailing NUL
+ int num = 0;
+
+ memset(htcap, 0, sizeof(htcap));
+ memset(vhtcap, 0, sizeof(vhtcap));
+ memset(wps, 0, sizeof(wps));
+ fstr[0] = '\0';
+
+ while (ie_len >= 2) {
+ u8 id, elen;
+ char tagbuf[32];
+ char *sep = (num++ == 0) ? "" : ",";
+
+ id = *ie++;
+ elen = *ie++;
+ ie_len -= 2;
+
+ if (elen > ie_len) {
+ break;
+ }
+
+ if ((id == 221) && (elen >= 4)) {
+ /* Vendor specific */
+ int is_MSFT = (ie[0] == 0x00 && ie[1] == 0x50 && ie[2] == 0xf2);
+ if (is_MSFT && ie[3] == 0x04) {
+ /* WPS */
+ char model_name[WPS_NAME_LEN + 1];
+ const u8 *data = &ie[4];
+ size_t data_len = elen - 4;
+ memset(model_name, 0, sizeof(model_name));
+ if (get_wps_name(model_name, WPS_NAME_LEN, data, data_len)) {
+ snprintf(wps, sizeof(wps), ",wps:%s", model_name);
+ }
+ }
+
+ snprintf(tagbuf, sizeof(tagbuf), "%s%d(%02x%02x%02x,%d)",
+ sep, id, ie[0], ie[1], ie[2], ie[3]);
+ } else {
+ if ((id == 45) && (elen > 2)) {
+ /* HT Capabilities (802.11n) */
+ u16 cap;
+ memcpy(&cap, ie, sizeof(cap));
+ snprintf(htcap, sizeof(htcap), ",htcap:%04hx",
+ le_to_host16(cap));
+ }
+ if ((id == 191) && (elen > 4)) {
+ /* VHT Capabilities (802.11ac) */
+ u32 cap;
+ memcpy(&cap, ie, sizeof(cap));
+ snprintf(vhtcap, sizeof(vhtcap), ",vhtcap:%08x",
+ le_to_host32(cap));
+ }
+ snprintf(tagbuf, sizeof(tagbuf), "%s%d", sep, id);
+ }
+
+ strncat(fstr, tagbuf, flen);
+ flen = fstr_len - strlen(fstr) - 1;
+
+ ie += elen;
+ ie_len -= elen;
+ }
+
+ if (strlen(htcap)) {
+ strncat(fstr, htcap, flen);
+ flen = fstr_len - strlen(fstr) - 1;
+ }
+ if (strlen(vhtcap)) {
+ strncat(fstr, vhtcap, flen);
+ flen = fstr_len - strlen(fstr) - 1;
+ }
+ if (strlen(wps)) {
+ strncat(fstr, wps, flen);
+ flen = fstr_len - strlen(fstr) - 1;
+ }
+
+ fstr[fstr_len - 1] = '\0';
+}
+
+void hostapd_taxonomy_probe_req(struct sta_info *sta,
+ const u8 *ie, size_t ie_len)
+{
+ ie_to_string(sta->probe_ie_taxonomy,
+ sizeof(sta->probe_ie_taxonomy),
+ ie, ie_len);
+}
+
+void hostapd_taxonomy_assoc_req(struct sta_info *sta,
+ const u8 *ie, size_t ie_len)
+{
+ ie_to_string(sta->assoc_ie_taxonomy,
+ sizeof(sta->assoc_ie_taxonomy),
+ ie, ie_len);
+}
+
+/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */
diff --git a/src/ap/taxonomy.h b/src/ap/taxonomy.h
new file mode 100644
index 0000000..e8b37b0
--- /dev/null
+++ b/src/ap/taxonomy.h
@@ -0,0 +1,17 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TAXONOMY_H
+#define TAXONOMY_H
+
+void hostapd_taxonomy_probe_req(struct sta_info *sta,
+ const u8 *ie, size_t ie_len);
+void hostapd_taxonomy_assoc_req(struct sta_info *sta,
+ const u8 *ie, size_t ie_len);
+
+#endif /* TAXONOMY_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index b83b460..af24cf0 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -264,7 +264,7 @@
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_group *group;
- wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
+ wpa_auth_logger(wpa_auth, NULL, LOGGER_INFO, "rekeying GTK");
for (group = wpa_auth->group; group; group = group->next) {
group->GTKReKey = TRUE;
do {
@@ -285,7 +285,7 @@
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
- wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "rekeying PTK");
wpa_request_new_ptk(sm);
wpa_sm_step(sm);
}
@@ -661,7 +661,7 @@
return;
if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
- wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"strict rekeying - force GTK rekey since STA "
"is leaving");
eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL);
@@ -1095,7 +1095,7 @@
* Counter update and the station will be allowed to
* continue.
*/
- wpa_printf(MSG_DEBUG, "WPA: Reject 4-way handshake to "
+ wpa_printf(MSG_ERROR, "WPA: Reject 4-way handshake to "
"collect more entropy for random number "
"generation");
random_mark_pool_ready();
@@ -1139,6 +1139,8 @@
}
#ifdef CONFIG_IEEE80211R
if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "WPA ft_check_msg_2_of_4 failure");
wpa_sta_disconnect(wpa_auth, sm->addr);
return;
}
@@ -1780,6 +1782,7 @@
{
SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
sm->Disconnect = FALSE;
+ wpa_printf(MSG_INFO, "WPA_PTK: disconnect state");
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
}
@@ -2322,6 +2325,7 @@
int klen = wpa_cipher_key_len(sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
sm->PTK.tk, klen)) {
+ wpa_printf(MSG_ERROR, "wpa_auth_set_key error");
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
return;
}
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7cd0b6c..59a56e8 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -131,7 +131,7 @@
u16 reason)
{
struct hostapd_data *hapd = ctx;
- wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+ wpa_printf(MSG_INFO, "%s: WPA authenticator requests disconnect: "
"STA " MACSTR " reason %d",
__func__, MAC2STR(addr), reason);
ap_sta_disconnect(hapd, NULL, addr, reason);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 47b15de..feab921 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -615,6 +615,11 @@
u8 action; /* 15 */
u8 variable[];
} STRUCT_PACKED slf_prot_action;
+ struct {
+ u8 action; /* 5 */
+ u8 dialog_token;
+ u8 variable[0];
+ } STRUCT_PACKED rm_action;
} u;
} STRUCT_PACKED action;
} u;
@@ -1356,6 +1361,18 @@
u8 variable[0];
} STRUCT_PACKED;
+/* IEEE Std 802.11-2012, 8.5.7.7 - Neighbor Report Element format */
+struct rrm_neighbor_report_element {
+ u8 eid;
+ u8 len;
+ u8 bssid[6];
+ le32 bssinfo;
+ u8 op_class;
+ u8 channel;
+ u8 phy_type;
+ u8 variable[0];
+} STRUCT_PACKED;
+
#define SSID_MAX_LEN 32
#endif /* IEEE802_11_DEFS_H */
diff --git a/src/crypto/random.c b/src/crypto/random.c
index bc758aa..15c8b03 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -230,7 +230,7 @@
* so use non-blocking read to avoid blocking the application
* completely.
*/
- fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
strerror(errno));
@@ -411,7 +411,7 @@
if (random_fd >= 0)
return;
- random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ random_fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
if (random_fd < 0) {
wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
strerror(errno));
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index be0e7c5..640e099 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -962,7 +962,7 @@
drv->ignore_if_down_event = 0;
return;
}
- wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+ wpa_printf(MSG_WARNING, "nl80211: Interface down (%s/%s)",
namebuf, ifname);
if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
wpa_printf(MSG_DEBUG,
@@ -3314,7 +3314,7 @@
beacon_set = params->reenable ? 0 : bss->beacon_set;
- wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+ wpa_printf(MSG_INFO, "nl80211: Set beacon (beacon_set=%d)",
beacon_set);
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
index 45b26c4..f2cd47c 100644
--- a/src/drivers/rfkill.c
+++ b/src/drivers/rfkill.c
@@ -110,7 +110,7 @@
rfkill->cfg = cfg;
rfkill->fd = open("/dev/rfkill", O_RDONLY);
if (rfkill->fd < 0) {
- wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+ wpa_printf(MSG_DEBUG, "rfkill: Cannot open RFKILL control "
"device");
goto fail;
}
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 4a565eb..f929dcd 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -939,7 +939,7 @@
_tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_SELECT */
}
-
+ fflush(stdout);
#ifdef CONFIG_ELOOP_POLL
num_poll_fds = eloop_sock_table_set_fds(
&eloop.readers, &eloop.writers, &eloop.exceptions,