Created
May 11, 2019 17:28
-
-
Save robimarko/55fd23f2350f483a75324a8ee4c897a5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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