band-steering: add band-steering component
authorDavid Bauer <mail@david-bauer.net>
Tue, 15 Mar 2022 21:56:14 +0000 (22:56 +0100)
committerDavid Bauer <mail@david-bauer.net>
Fri, 18 Mar 2022 20:46:29 +0000 (21:46 +0100)
This adds a new band-steering component to usteer.

With band-steering enabled, usteer will attempt to move clients with a
consistently good SNR / signal from 2.4 GHz to the 5 GHz band.

In contrast to existing policies usteer tracks, band-steering is
non-invasive and will not lead to forceful connection termination.

Signed-off-by: David Bauer <mail@david-bauer.net>
CMakeLists.txt
band_steering.c [new file with mode: 0644]
local_node.c
main.c
node.h
openwrt/usteer/files/etc/config/usteer
openwrt/usteer/files/etc/init.d/usteer
sta.c
ubus.c
usteer.h

index 6344aab0731a36548f207d802705e715dca8c478..d44194b91e23cab586ecb6af92f6d71ffc364cd7 100644 (file)
@@ -24,7 +24,7 @@ IF(NOT HAVE_PCAP_H)
        MESSAGE(FATAL_ERROR "pcap/pcap.h is not found")
 ENDIF()
 
-SET(SOURCES main.c local_node.c node.c sta.c policy.c ubus.c remote.c parse.c netifd.c timeout.c event.c measurement.c)
+SET(SOURCES main.c local_node.c node.c sta.c policy.c ubus.c remote.c parse.c netifd.c timeout.c event.c measurement.c band_steering.c)
 
 IF(NL_CFLAGS)
        ADD_DEFINITIONS(${NL_CFLAGS})
diff --git a/band_steering.c b/band_steering.c
new file mode 100644 (file)
index 0000000..fb4a3f1
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *   Copyright (C) 2022 David Bauer <mail@david-bauer.net>
+ */
+
+#include "usteer.h"
+#include "node.h"
+
+void usteer_band_steering_sta_update(struct sta_info *si)
+{
+       if (si->signal < usteer_snr_to_signal(si->node, config.band_steering_min_snr))
+               si->band_steering.below_snr = true;
+}
+
+bool usteer_band_steering_is_target(struct usteer_local_node *ln, struct usteer_node *node)
+{
+       if (&ln->node == node)
+               return false;
+
+       if (strcmp(ln->node.ssid, node->ssid))
+               return false;
+
+       if (node->freq < 4000)
+               return false;
+
+       if (!usteer_policy_node_below_max_assoc(node))
+               return false;
+       
+       /* ToDo: Skip nodes with active load-kick */
+       
+       return true;
+ }
+
+
+static bool usteer_band_steering_has_target_iface(struct usteer_local_node *ln)
+{
+       struct usteer_node *node;
+
+       for_each_local_node(node) {
+               if (usteer_band_steering_is_target(ln, node))
+                       return true;
+       }
+
+       return false;
+}
+
+void usteer_band_steering_perform_steer(struct usteer_local_node *ln)
+{
+       unsigned int min_count = DIV_ROUND_UP(config.band_steering_interval, config.local_sta_update);
+       struct sta_info *si;
+
+       if (!config.band_steering_interval)
+               return;
+
+       /* Band-Steering is only available on 2.4 GHz interfaces */
+       if (ln->node.freq > 4000)
+               return;
+
+       /* Check if we have an interface we can steer to */
+       if (!usteer_band_steering_has_target_iface(ln))
+               return;
+
+       /* Only steer every interval */
+       if (ln->band_steering_interval < min_count) {
+               ln->band_steering_interval++;
+               return;
+       }
+
+       ln->band_steering_interval = 0;
+
+       list_for_each_entry(si, &ln->node.sta_info, node_list) {
+               if (si->connected != STA_CONNECTED)
+                       continue;
+
+               /* Skip clients with insufficient SNR-state */
+               if (si->band_steering.below_snr) {
+                       si->band_steering.below_snr = false;
+                       continue;
+               }
+
+               if (si->bss_transition)
+                       usteer_ubus_band_steering_request(si);
+
+               si->band_steering.below_snr = false;
+       }
+}
\ No newline at end of file
index 92c2dcbddd31f92aeb10038e1157c63dba6275f3..140af78e98609de4e1045d9cc2b317080cb9bac7 100644 (file)
@@ -642,6 +642,7 @@ usteer_local_node_update(struct uloop_timeout *timeout)
        usteer_local_node_state_reset(ln);
        uloop_timeout_set(&ln->req_timer, 1);
        usteer_local_node_kick(ln);
+       usteer_band_steering_perform_steer(ln);
        uloop_timeout_set(timeout, config.local_sta_update);
 }
 
diff --git a/main.c b/main.c
index 6371c75deb9796401659f076108eb7bd30045fa1..6c8033a86fd206d97e5d5ba1d65aab6c8ba941ed 100644 (file)
--- a/main.c
+++ b/main.c
@@ -99,6 +99,9 @@ void usteer_init_defaults(void)
 
        config.steer_reject_timeout = 60000;
 
+       config.band_steering_interval = 120000;
+       config.band_steering_min_snr = -60;
+
        config.roam_kick_delay = 10000;
        config.roam_process_timeout = 5 * 1000;
        config.roam_scan_tries = 3;
diff --git a/node.h b/node.h
index 1e36f58c226d0cb43431cd0a6bbdea6638ab1ad8..914746ddb6c3991b204c924e9019c5facd43f8c9 100644 (file)
--- a/node.h
+++ b/node.h
@@ -59,6 +59,8 @@ struct usteer_local_node {
 
        int beacon_interval;
 
+       uint16_t band_steering_interval;
+
        struct {
                bool present;
                struct uloop_timeout update;
index 8243f305857e8127048bdb000c506ea6253eaf88..53fcbbfa96737191302398286eb2644f5bfb0bc2 100644 (file)
@@ -117,6 +117,14 @@ config usteer
        # Reason code on client kick based on channel load (default: WLAN_REASON_DISASSOC_AP_BUSY)
        #option load_kick_reason_code 5
 
+       # Attempting to steer clients to a higher frequency-band every n ms.
+       # A value of 0 disabled band-steering.
+       #option band_steering_interval 120000
+
+       # Minimal SNR or absolute signal a device has to maintain over band_steering_interval to be
+       # steered to a higher frequency band
+       #option band_steering_min_snr -60
+
        # Script to run after bringing up a node
        #option node_up_script ''
 
index 30c558ebf9d61f25ea3ad4d388bd08f59766555f..2d014b066fca252b5d7984556072d373dea23ecf 100755 (executable)
@@ -85,6 +85,7 @@ uci_usteer() {
                roam_kick_delay roam_scan_tries roam_scan_timeout \
                roam_scan_snr roam_scan_interval \
                roam_trigger_snr roam_trigger_interval \
+               band_steering_interval band_steering_min_snr \
                load_kick_threshold load_kick_delay load_kick_min_clients \
                load_kick_reason_code
        do
diff --git a/sta.c b/sta.c
index 53f203af6bc8a6fabc69a7e27e5d84e2d1acc2b1..9c11bc3938e17fab312c8224fac5b4cac36784ba 100644 (file)
--- a/sta.c
+++ b/sta.c
@@ -160,8 +160,10 @@ usteer_sta_info_update(struct sta_info *si, int signal, bool avg)
        if (si->connected == STA_CONNECTED && si->signal != NO_SIGNAL && !avg)
                signal = NO_SIGNAL;
 
-       if (signal != NO_SIGNAL)
+       if (signal != NO_SIGNAL) {
                si->signal = signal;
+               usteer_band_steering_sta_update(si);
+       }
 
        si->seen = current_time;
 
diff --git a/ubus.c b/ubus.c
index a90e5ea7e1e0be81516c2fa573d4947e7e6e956b..672c9900c586e4882a0f43ff7ef9935549a6d5b1 100644 (file)
--- a/ubus.c
+++ b/ubus.c
@@ -181,6 +181,8 @@ struct cfg_item {
        _cfg(U32, load_kick_delay), \
        _cfg(U32, load_kick_min_clients), \
        _cfg(U32, load_kick_reason_code), \
+       _cfg(U32, band_steering_interval), \
+       _cfg(I32, band_steering_min_snr), \
        _cfg(ARRAY_CB, interfaces), \
        _cfg(STRING_CB, node_up_script), \
        _cfg(ARRAY_CB, event_log_types), \
@@ -654,6 +656,31 @@ int usteer_ubus_bss_transition_request(struct sta_info *si,
        return ubus_invoke(ubus_ctx, ln->obj_id, "bss_transition_request", b.head, NULL, 0, 100);
 }
 
+int usteer_ubus_band_steering_request(struct sta_info *si)
+{
+       struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node);
+       struct usteer_node *node;
+       void *c;
+
+       blob_buf_init(&b, 0);
+       blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr));
+       blobmsg_add_u32(&b, "dialog_token", 0);
+       blobmsg_add_u8(&b, "disassociation_imminent", false);
+       blobmsg_add_u8(&b, "abridged", false);
+       blobmsg_add_u32(&b, "validity_period", 100);
+
+       c = blobmsg_open_array(&b, "neighbors");
+       for_each_local_node(node) {
+               if (!usteer_band_steering_is_target(ln, node))
+                       continue;
+       
+               usteer_add_nr_entry(si->node, node);
+       }
+       blobmsg_close_array(&b, c);
+
+       return ubus_invoke(ubus_ctx, ln->obj_id, "bss_transition_request", b.head, NULL, 0, 100);
+}
+
 int usteer_ubus_notify_client_disassoc(struct sta_info *si)
 {
        struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node);
index dfc9544bf0fb0e46cf3f8d85a7868c9dafc55965..9d9693f14f06d04a1f7a97896b7e1888a653fa2a 100644 (file)
--- a/usteer.h
+++ b/usteer.h
@@ -188,6 +188,9 @@ struct usteer_config {
 
        uint32_t roam_kick_delay;
 
+       uint32_t band_steering_interval;
+       int32_t band_steering_min_snr; 
+
        uint32_t initial_connect_delay;
 
        bool load_kick_enabled;
@@ -253,6 +256,10 @@ struct sta_info {
                uint64_t timestamp;
        } bss_transition_response;
 
+       struct {
+               bool below_snr;
+       } band_steering;
+
        uint64_t kick_time;
 
        int kick_count;
@@ -318,10 +325,15 @@ int usteer_local_node_get_beacon_interval(struct usteer_local_node *ln);
 
 bool usteer_policy_node_below_max_assoc(struct usteer_node *node);
 
+void usteer_band_steering_perform_steer(struct usteer_local_node *ln);
+void usteer_band_steering_sta_update(struct sta_info *si);
+bool usteer_band_steering_is_target(struct usteer_local_node *ln, struct usteer_node *node);
+
 void usteer_ubus_init(struct ubus_context *ctx);
 void usteer_ubus_kick_client(struct sta_info *si);
 int usteer_ubus_trigger_client_scan(struct sta_info *si);
 int usteer_ubus_notify_client_disassoc(struct sta_info *si);
+int usteer_ubus_band_steering_request(struct sta_info *si);
 int usteer_ubus_bss_transition_request(struct sta_info *si,
                                       uint8_t dialog_token,
                                       bool disassoc_imminent,