Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robimarko/55fd23f2350f483a75324a8ee4c897a5 to your computer and use it in GitHub Desktop.
Save robimarko/55fd23f2350f483a75324a8ee4c897a5 to your computer and use it in GitHub Desktop.
From 11e9ffc61037f15ce3161f80c660dfcf5f7dc2c3 Mon Sep 17 00:00:00 2001
From: Robert Marko <robimarko@gmail.com>
Date: Fri, 10 May 2019 20:38:20 +0200
Subject: [PATCH] Add low RSSI features for OpenWrt
Still WIP and depends on ubus.
---
hostapd/config_file.c | 20 ++++++++++++++++++++
src/ap/ap_config.c | 7 +++++++
src/ap/ap_config.h | 6 ++++++
src/ap/sta_info.c | 4 ++++
src/ap/sta_info.h | 4 ++++
src/drivers/driver_nl80211.c | 4 ++++
6 files changed, 45 insertions(+)
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3149,6 +3149,26 @@ static int hostapd_config_fill(struct ho
return 1;
}
conf->send_probe_response = val;
+#ifdef UBUS_SUPPORT
+ } else if (os_strcmp(buf, "signal_connect") == 0) {
+ bss->signal_auth_min = atoi(pos);
+ } else if (os_strcmp(buf, "signal_stay") == 0) {
+ bss->signal_stay_min = atoi(pos);
+ } else if (os_strcmp(buf, "signal_poll_time") == 0) {
+ bss->signal_poll_time = atoi(pos);
+ if (bss->signal_poll_time < 2) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid signal poll time", line);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "signal_strikes") == 0) {
+ bss->signal_strikes = atoi(pos);
+ } else if (os_strcmp(buf, "signal_drop_reason") == 0) {
+ bss->signal_drop_reason = atoi(pos);
+ if (bss->signal_drop_reason < 1 || bss->signal_drop_reason > 54) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid signal drop reason", line);
+ return 1;
+ }
+#endif /* UBUS_SUPPORT */
} else if (os_strcmp(buf, "supported_rates") == 0) {
if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -80,6 +80,13 @@ void hostapd_config_defaults_bss(struct
bss->eapol_version = EAPOL_VERSION;
bss->max_listen_interval = 65535;
+#ifdef UBUS_SUPPORT
+ bss->signal_auth_min = -128; /* this is lower than any real signal, so all stations will be accepted */
+ bss->signal_stay_min = -128;
+ bss->signal_strikes = 3;
+ bss->signal_poll_time = 10;
+ bss->signal_drop_reason = 3; /* "Local choice" */
+#endif /* UBUS_SUPPORT */
bss->pwd_group = 19; /* ECC: GF(p=256) */
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -322,6 +322,12 @@ struct hostapd_bss_config {
int wds_sta;
int isolate;
int start_disabled;
+ int signal_auth_min; /* Minimum signal a STA needs to authenticate */
+ int signal_stay_min; /* Minimum signal needed to stay connected. */
+ int signal_poll_time; /* Time in seconds between checks of connected STAs */
+ int signal_strikes; /* Number of consecutive times signal can be low
+ before dropping the STA. */
+ int signal_drop_reason; /* IEEE802.11 reason code transmitted when dropping a STA. */
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -706,6 +706,10 @@ struct sta_info * ap_sta_add(struct host
&sta->probe_ie_taxonomy);
#endif /* CONFIG_TAXONOMY */
+#ifdef UBUS_SUPPORT
+ sta->sig_drop_strikes = 0;
+#endif /* UBUS_SUPPORT */
+
return sta;
}
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -252,6 +252,10 @@ struct sta_info {
struct wpabuf *fils_g_sta;
#endif /* CONFIG_FILS */
+#ifdef UBUS_SUPPORT
+ int sig_drop_strikes; /* Number of times signal was below threshold. */
+#endif /* UBUS_SUPPORT */
+
#ifdef CONFIG_OWE
u8 *owe_pmk;
size_t owe_pmk_len;
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -6179,6 +6179,7 @@ static int get_sta_handler(struct nl_msg
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+ [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
};
struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
@@ -6242,6 +6243,9 @@ static int get_sta_handler(struct nl_msg
nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
if (stats[NL80211_STA_INFO_SIGNAL])
data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
+ if (stats[NL80211_STA_INFO_SIGNAL_AVG])
+ data->last_ack_rssi =
+ (int) (s8) nla_get_u8(stats[NL80211_STA_INFO_SIGNAL_AVG]);
if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
data->last_ack_rssi =
nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
--- a/src/ap/ubus.c
+++ b/src/ap/ubus.c
@@ -142,6 +142,59 @@ hostapd_bss_ban_client(struct hostapd_da
eloop_register_timeout(0, time * 1000, hostapd_bss_del_ban, ban, hapd);
}
+static void
+hostapd_bss_signal_check(void *eloop_data, void *user_ctx)
+/* This is called by an eloop timeout. All stations in the list are checked
+ * for signal level. This requires calling the driver, since hostapd doesn't
+ * see packets from a station once it is fully authorized.
+ * Stations with signal level below the threshold will be dropped.
+ * Cases where the last RSSI is significantly less than the average are usually
+ * a bad reading and should not lead to a drop.
+ */
+{
+ struct hostapd_data *hapd = user_ctx;
+ struct hostap_sta_driver_data data;
+ struct sta_info *sta, *sta_next;
+ u8 addr[ETH_ALEN]; // Buffer the address for logging purposes, in case it is destroyed while dropping
+ int strikes; // same with strike count on this station.
+ int num_sta = 0;
+ int num_drop = 0;
+ int signal_inst;
+ int signal_avg;
+
+
+ for (sta = hapd->sta_list; sta; sta = sta_next) {
+ sta_next = sta->next;
+ memcpy(addr, sta->addr, ETH_ALEN);
+ if (!hostapd_drv_read_sta_data(hapd, &data, addr)) {
+ signal_inst = data.signal;
+ signal_avg = data.last_ack_rssi;
+ num_sta++;
+ strikes = sta->sig_drop_strikes;
+ if (signal_inst > signal_avg)
+ signal_avg = signal_inst;
+ if (signal_inst > (signal_avg - 5)) { // ignore unusually low instantaneous signal.
+ if (signal_avg < hapd->conf->signal_stay_min) { // signal bad.
+ strikes = ++sta->sig_drop_strikes;
+ if (strikes >= hapd->conf->signal_strikes) { // Struck out--, drop.
+ ap_sta_deauthenticate(hapd, sta, hapd->conf->signal_drop_reason);
+ num_drop++;
+ }
+ }
+ else {
+ sta->sig_drop_strikes = 0; // signal OK, reset the strike counter.
+ strikes = 0;
+ }
+ }
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_DEBUG, "%i %i (%i)",
+ data.signal, data.last_ack_rssi, strikes);
+ }
+ }
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IAPP, HOSTAPD_LEVEL_INFO, "signal poll: %i STAs, %i dropped", num_sta, num_drop);
+
+ eloop_register_timeout(hapd->conf->signal_poll_time, 0, hostapd_bss_signal_check, eloop_data, hapd);
+}
+
static int
hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
@@ -503,6 +556,72 @@ hostapd_vendor_elements(struct ubus_cont
return UBUS_STATUS_OK;
}
+enum {
+ SIGNAL_CONNECT,
+ SIGNAL_STAY,
+ SIGNAL_STRIKES,
+ SIGNAL_POLL,
+ SIGNAL_DROP_REASON,
+ __SIGNAL_SETTINGS_MAX
+};
+
+static const struct blobmsg_policy sig_policy[__SIGNAL_SETTINGS_MAX] = {
+ [SIGNAL_CONNECT] = {"connect", BLOBMSG_TYPE_INT32},
+ [SIGNAL_STAY] = {"stay", BLOBMSG_TYPE_INT32},
+ [SIGNAL_STRIKES] = {"strikes", BLOBMSG_TYPE_INT32},
+ [SIGNAL_POLL] = {"poll_time", BLOBMSG_TYPE_INT32},
+ [SIGNAL_DROP_REASON] = {"reason", BLOBMSG_TYPE_INT32}
+};
+
+static int
+hostapd_bss_set_signal(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct blob_attr *tb[__SIGNAL_SETTINGS_MAX];
+ struct hostapd_data *hapd = get_hapd_from_object(obj);
+ int sig_stay;
+
+ blobmsg_parse(sig_policy, __SIGNAL_SETTINGS_MAX, tb, blob_data(msg), blob_len(msg));
+
+ if (!tb[SIGNAL_CONNECT])
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ hapd->conf->signal_auth_min = blobmsg_get_u32(tb[SIGNAL_CONNECT]);
+ if (tb[SIGNAL_STAY]) {
+ sig_stay = blobmsg_get_u32(tb[SIGNAL_STAY]);
+
+ }
+ else
+ sig_stay = hapd->conf->signal_auth_min - 5; // Default is 5 dB lower to stay.
+ hapd->conf->signal_stay_min = sig_stay;
+ if (tb[SIGNAL_STRIKES]) {
+ hapd->conf->signal_strikes = blobmsg_get_u32(tb[SIGNAL_STRIKES]);
+ if (hapd->conf->signal_strikes < 1)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+ else
+ hapd->conf->signal_strikes = 3;
+ if (tb[SIGNAL_POLL]) {
+ hapd->conf->signal_poll_time = blobmsg_get_u32(tb[SIGNAL_POLL]);
+ if (hapd->conf->signal_poll_time < 3)
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+ else
+ hapd->conf->signal_poll_time = 10;
+ if (tb[SIGNAL_DROP_REASON]) {
+ hapd->conf->signal_drop_reason = blobmsg_get_u32(tb[SIGNAL_DROP_REASON]);
+ if ((hapd->conf->signal_drop_reason < 1) || (hapd->conf->signal_drop_reason > 35)) // XXX -- look up real limit
+ return UBUS_STATUS_INVALID_ARGUMENT;
+ }
+ else
+ hapd->conf->signal_drop_reason = 3; // Local choice. 5 (AP too busy) is also a good one.
+
+ eloop_cancel_timeout(hostapd_bss_signal_check, ELOOP_ALL_CTX, ELOOP_ALL_CTX);
+ eloop_register_timeout(3, 0, hostapd_bss_signal_check, NULL, hapd); // Start up the poll timer.
+
+ return UBUS_STATUS_OK;
+}
+
static void
hostapd_rrm_print_nr(struct hostapd_neighbor_entry *nr)
{
@@ -960,6 +1079,7 @@ static const struct ubus_method bss_meth
UBUS_METHOD("switch_chan", hostapd_switch_chan, csa_policy),
#endif
UBUS_METHOD("set_vendor_elements", hostapd_vendor_elements, ve_policy),
+ UBUS_METHOD("set_required_signal", hostapd_bss_set_signal, sig_policy),
UBUS_METHOD("notify_response", hostapd_notify_response, notify_policy),
UBUS_METHOD("bss_mgmt_enable", hostapd_bss_mgmt_enable, bss_mgmt_enable_policy),
UBUS_METHOD_NOARG("rrm_nr_get_own", hostapd_rrm_nr_get_own),
@@ -1003,6 +1123,9 @@ void hostapd_ubus_add_bss(struct hostapd
obj->n_methods = bss_object_type.n_methods;
ret = ubus_add_object(ctx, obj);
hostapd_ubus_ref_inc();
+ /* This should run after the config file has been read, I hope. */
+ if (hapd->conf->signal_stay_min > -128)
+ eloop_register_timeout(3, 0, hostapd_bss_signal_check, NULL, hapd); // Start up the poll timer.
}
void hostapd_ubus_free_bss(struct hostapd_data *hapd)
@@ -1051,6 +1174,19 @@ int hostapd_ubus_handle_event(struct hos
else
addr = req->addr;
+ if (req->type < ARRAY_SIZE(types))
+ type = types[req->type];
+
+ if (req->frame_info && req->type != HOSTAPD_UBUS_PROBE_REQ) // don't clutter the log with probes.
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, HOSTAPD_LEVEL_INFO, "%s request, signal %i %s",
+ type, req->frame_info->ssi_signal,
+ (req->frame_info->ssi_signal >= hapd->conf->signal_auth_min) ? "(Accepted)" : "(DENIED)");
+ // reject weak signals.
+ if (req->frame_info && req->frame_info->ssi_signal < hapd->conf->signal_auth_min)
+ return -2;
+
+ // reject banned MACs.
+
ban = avl_find_element(&hapd->ubus.banned, addr, ban, avl);
if (ban)
return WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
@@ -1058,9 +1194,6 @@ int hostapd_ubus_handle_event(struct hos
if (!hapd->ubus.obj.has_subscribers)
return WLAN_STATUS_SUCCESS;
- if (req->type < ARRAY_SIZE(types))
- type = types[req->type];
-
blob_buf_init(&b, 0);
blobmsg_add_macaddr(&b, "address", addr);
if (req->mgmt_frame)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment