From f4e120c9a3f460cd8478f072d4c7879e1bf98136 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Tue, 15 Mar 2022 22:56:14 +0100 Subject: [PATCH] band-steering: add band-steering component 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 --- CMakeLists.txt | 2 +- band_steering.c | 98 ++++++++++++++++++++++++++ local_node.c | 1 + main.c | 3 + node.h | 2 + openwrt/usteer/files/etc/config/usteer | 8 +++ openwrt/usteer/files/etc/init.d/usteer | 1 + sta.c | 4 +- ubus.c | 27 +++++++ usteer.h | 12 ++++ 10 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 band_steering.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6344aab..d44194b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 index 0000000..fb4a3f1 --- /dev/null +++ b/band_steering.c @@ -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 + */ + +#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 diff --git a/local_node.c b/local_node.c index 92c2dcb..140af78 100644 --- a/local_node.c +++ b/local_node.c @@ -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 6371c75..6c8033a 100644 --- 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 1e36f58..914746d 100644 --- 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; diff --git a/openwrt/usteer/files/etc/config/usteer b/openwrt/usteer/files/etc/config/usteer index 8243f30..53fcbbf 100644 --- a/openwrt/usteer/files/etc/config/usteer +++ b/openwrt/usteer/files/etc/config/usteer @@ -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 '' diff --git a/openwrt/usteer/files/etc/init.d/usteer b/openwrt/usteer/files/etc/init.d/usteer index 30c558e..2d014b0 100755 --- a/openwrt/usteer/files/etc/init.d/usteer +++ b/openwrt/usteer/files/etc/init.d/usteer @@ -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 53f203a..9c11bc3 100644 --- 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 a90e5ea..672c990 100644 --- 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); diff --git a/usteer.h b/usteer.h index dfc9544..9d9693f 100644 --- 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, -- 2.30.2