Skip to content
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ usteer is available from the OpenWrt packages feed and can be installed on devic
opkg update; opkg install usteer
```

You can find the complete documentation for setting everything up and getting it running here:[OpenWRT documentation](https://openwrt.org/docs/guide-user/network/wifi/usteer).

### Config recommendation
The default settings are the best in most environments (many users overdo optimizations to it) to support roaming and band steering. At best nothing has to be configured but take care that usteer communication is not done on a public network.


## Submitting patches

usteer patches are welcome on the openwrt-devel mailing list.
Expand Down
9 changes: 9 additions & 0 deletions band_steering.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ void usteer_band_steering_sta_update(struct sta_info *si)
}
return;
}
if (si->node->freq >= 4000)
return;
if (!si->sta->seen_5ghz)
return;

if (config.band_steering_signal_threshold)
{
if (si->connected != STA_NOT_CONNECTED && si->band_steering.signal_threshold == NO_SIGNAL)
Expand Down Expand Up @@ -106,6 +111,10 @@ void usteer_band_steering_perform_steer(struct usteer_local_node *ln)
ln->band_steering_interval = 0;

list_for_each_entry(si, &ln->node.sta_info, node_list) {
/* Check if client supports 5Ghz */
if (!si->sta->seen_5ghz)
continue;

/* Check if client is eligable to be steerd */
if (!usteer_policy_can_perform_roam(si))
continue;
Expand Down
46 changes: 39 additions & 7 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,41 +80,75 @@ void debug_msg_cont(int level, const char *format, ...)
va_end(ap);
}


/* DEBUG: Hex dump helper function for Kirkwood diagnostics */
void debug_hexdump(const char *prefix, const void *data, int len)
{
const unsigned char *buf = (const unsigned char *)data;
int i, j;
char hex_str[64], ascii_str[32];

MSG(NETWORK, "%s (len=%d):\n", prefix, len);

for (i = 0; i < len; i += 16) {
hex_str[0] = '\0';
ascii_str[0] = '\0';

for (j = 0; j < 16 && i + j < len; j++) {
unsigned char c = buf[i + j];
sprintf(hex_str + strlen(hex_str), "%02x ", c);
sprintf(ascii_str + strlen(ascii_str), "%c", (c >= 32 && c < 127) ? c : '.');
}

MSG(NETWORK, " %04x: %-48s | %s\n", i, hex_str, ascii_str);
}
}

void usteer_init_defaults(void)
{
memset(&config, 0, sizeof(config));

config.syslog = true;
config.debug_level = MSG_FATAL;

config.ipv6 = false;
config.local_mode = 0;

config.sta_block_timeout = 30 * 1000;
config.local_sta_timeout = 120 * 1000;
config.measurement_report_timeout = 120 * 1000;
config.local_sta_update = 1 * 1000;
config.max_retry_band = 5;
config.max_neighbor_reports = 8;
config.seen_policy_timeout = 30 * 1000;
config.signal_diff_threshold = 8;
config.band_steering_threshold = 5;
config.load_balancing_threshold = 0;
config.local_mode = 0;
config.remote_update_interval = 1000;
config.initial_connect_delay = 0;
config.remote_node_timeout = 10;
config.aggressiveness = 3;
config.reassociation_delay = 30;

config.steer_reject_timeout = 60000;
config.steer_reject_timeout = 60 * 1000;

config.band_steering_interval = 30000;
config.band_steering_interval = 30 * 1000;
config.band_steering_min_snr = -60;
config.band_steering_signal_threshold = 5;

config.link_measurement_interval = 30000;
config.link_measurement_interval = 30 * 1000;

config.probe_steering = 0;

config.roam_kick_delay = 10000;
config.roam_kick_delay = 10 * 1000;
config.roam_process_timeout = 5 * 1000;
config.roam_scan_tries = 3;
config.roam_scan_timeout = 0;
config.roam_scan_interval = 10 * 1000;
config.roam_trigger_interval = 60 * 1000;
config.roam_scan_snr = -65;
config.roam_trigger_interval = 30 * 1000;
config.roam_trigger_snr = 0;

config.min_snr_kick_delay = 5 * 1000;

Expand All @@ -123,8 +157,6 @@ void usteer_init_defaults(void)
config.load_kick_delay = 10 * 1000;
config.load_kick_min_clients = 10;
config.load_kick_reason_code = 5; /* WLAN_REASON_DISASSOC_AP_BUSY */

config.debug_level = MSG_FATAL;
}

void usteer_update_time(void)
Expand Down
30 changes: 18 additions & 12 deletions openwrt/usteer/files/etc/config/usteer
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ config usteer
option 'network' 'lan'

# Log messages to syslog (0/1)
option 'syslog' '1'
#option 'syslog' '1'

# Disable network communication (0/1)
option local_mode '0'
#option local_mode '0'

# Use IPv6 for remote exchange
option 'ipv6' '0'
#option 'ipv6' '0'

# Minimum level of logged messages
# 0 = fatal
Expand All @@ -20,7 +20,7 @@ config usteer
# 3 = some debug messages
# 4 = network packet information
# 5 = all debug messages
option 'debug_level' '2'
#option 'debug_level' '2'

# Maximum number of neighbor reports set for a node
#option max_neighbor_reports 8
Expand All @@ -44,6 +44,7 @@ config usteer
#option seen_policy_timeout 30000

# Minimum number of stations delta between APs before load balancing policy is active
# No effect unless load_balancing_threshold is above 0
#option load_balancing_threshold 0

# Minimum number of stations delta between bands before band steering policy is active
Expand All @@ -62,9 +63,11 @@ config usteer
#option probe_steering 0

# Minimum signal-to-noise ratio or signal level (dBm) to allow connections
# Related: should be equal to or higher than `min_snr` and `band_steering_min_snr`
#option min_connect_snr 0

# Minimum signal-to-noise ratio or signal level (dBm) to remain connected
# Related: should be equal to or lower than `min_connect_snr` and lower than `band_steering_min_snr`
#option min_snr 0

# Timeout after which a station with snr < min_snr will be kicked
Expand All @@ -80,19 +83,20 @@ config usteer
# 4 = BSS-transition-request with disassociation imminent, timer and forced disassociation
#option aggressiveness 3

# Timeout (s in "1024ms") a station is requested to avoid reassociation after bss transition
#option reassociation_delay 30

# List of MACs (lower case) to set aggressiveness per station (ff:ff:ff:ff:ff,2)
#list aggressiveness_mac_list ''

# Timeout (s in "1024ms") a station is requested to avoid reassociation after bss transition
#option reassociation_delay 30

# Timeout (in ms) after which a association following a disassociation is not seen
# as a roam
#option roam_process_timeout 5000

# Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger
# Maximum signal-to-noise ratio or signal level (dBm) before attempting to trigger
# client scans for roaming
#option roam_scan_snr 0
# Related: should be higher than `roam_trigger_snr`
#option roam_scan_snr -65

# Maximum number of client roaming scan trigger attempts
#option roam_scan_tries 3
Expand All @@ -104,18 +108,19 @@ config usteer
# Minimum time (ms) between client roaming scan trigger attempts
#option roam_scan_interval 10000

# Minimum signal-to-noise ratio or signal level (dBm) before attempting to trigger
# Maximum signal-to-noise ratio or signal level (dBm) before attempting to trigger
# forced client roaming
# Related: should be lower than `roam_scan_snr`
#option roam_trigger_snr 0

# Minimum time (ms) between client roaming trigger attempts
#option roam_trigger_interval 60000
#option roam_trigger_interval 30000

# Timeout (ms) for client roam requests. usteer will kick the client after this times out.
#option roam_kick_delay 10000

# Minimum signal strength difference until AP steering policy is active
#option signal_diff_threshold 0
#option signal_diff_threshold 8

# Initial delay (ms) before responding to probe requests (to allow other APs to see packets as well)
#option initial_connect_delay 0
Expand All @@ -141,6 +146,7 @@ config usteer

# Minimal SNR or absolute signal a device has to maintain over band_steering_interval to be
# steered to a higher frequency band
# Related: should be above `min_connect_snr` and `min_snr`
#option band_steering_min_snr -60

# SNR difference that the signal must be better compared to signal was on connection to node.
Expand Down
7 changes: 3 additions & 4 deletions policy.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ better_signal_strength(int signal_cur, int signal_new)
}

static bool
below_load_threshold(struct usteer_node *node)
above_load_threshold(struct usteer_node *node)
{
return node->n_assoc >= config.load_kick_min_clients &&
node->load > config.load_kick_threshold;
Expand All @@ -64,7 +64,7 @@ below_load_threshold(struct usteer_node *node)
static bool
has_better_load(struct usteer_node *node_cur, struct usteer_node *node_new)
{
return !below_load_threshold(node_cur) && below_load_threshold(node_new);
return above_load_threshold(node_cur) && !above_load_threshold(node_new);
}

bool
Expand Down Expand Up @@ -107,8 +107,7 @@ is_better_candidate(struct sta_info *si_cur, struct sta_info *si_new)
if (better_signal_strength(current_signal, new_signal))
reasons |= (1 << UEV_SELECT_REASON_SIGNAL);

if (has_better_load(current_node, new_node) &&
!has_better_load(new_node, current_node))
if (has_better_load(current_node, new_node))
reasons |= (1 << UEV_SELECT_REASON_LOAD);

return reasons;
Expand Down
25 changes: 21 additions & 4 deletions remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct interface {
int ifindex;
};


static void
interfaces_update_cb(struct vlist_tree *tree,
struct vlist_node *node_new,
Expand Down Expand Up @@ -318,6 +319,9 @@ interface_recv_msg(struct interface *iface, char *addr_str, void *buf, int len)
if (config.local_mode)
return;

/* DEBUG 1: Hexdump first 64 bytes of raw packet for pattern analysis */
debug_hexdump("RAW PACKET START", data, (blob_pad_len(data) < 64) ? blob_pad_len(data) : 64);

if (blob_pad_len(data) != len) {
MSG(DEBUG, "Invalid message length (header: %d, real: %d)\n", blob_pad_len(data), len);
return;
Expand Down Expand Up @@ -357,8 +361,12 @@ interface_find_by_ifindex(int index)
static void
interface_recv_v4(struct uloop_fd *u, unsigned int events)
{
static char buf[APMGR_BUFLEN];
static char cmsg_buf[( CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1];
/* Keep receive buffers aligned for blob parsing and cmsghdr access on
* stricter ARM targets such as Kirkwood.
*/
static unsigned char buf[APMGR_BUFLEN] __attribute__((aligned(sizeof(unsigned long))));
static unsigned char cmsg_buf[(CMSG_SPACE(sizeof(struct in_pktinfo)) + sizeof(int)) + 1]
__attribute__((aligned(sizeof(unsigned long))));
static struct sockaddr_in sin;
char addr_str[INET_ADDRSTRLEN];
static struct iovec iov = {
Expand Down Expand Up @@ -420,8 +428,12 @@ interface_recv_v4(struct uloop_fd *u, unsigned int events)


static void interface_recv_v6(struct uloop_fd *u, unsigned int events){
static char buf[APMGR_BUFLEN];
static char cmsg_buf[( CMSG_SPACE(sizeof(struct in6_pktinfo)) + sizeof(int)) + 1];
/* Keep receive buffers aligned for blob parsing and cmsghdr access on
* stricter ARM targets such as Kirkwood.
*/
static unsigned char buf[APMGR_BUFLEN] __attribute__((aligned(sizeof(unsigned long))));
static unsigned char cmsg_buf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + sizeof(int)) + 1]
__attribute__((aligned(sizeof(unsigned long))));
static struct sockaddr_in6 sin;
static struct iovec iov = {
.iov_base = buf,
Expand Down Expand Up @@ -749,6 +761,9 @@ static void usteer_reload_timer(struct uloop_timeout *t) {
close(remote_fd.fd);
}

if (config.local_mode)
return;

if (config.ipv6) {
remote_fd.fd = usteer_create_v6_socket();
remote_fd.cb = interface_recv_v6;
Expand All @@ -765,6 +780,8 @@ static void usteer_reload_timer(struct uloop_timeout *t) {

int usteer_interface_init(void)
{
if (config.local_mode)
return -1;
if (usteer_init_local_id())
return -1;

Expand Down
25 changes: 18 additions & 7 deletions sta.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* Copyright (C) 2020 John Crispin <john@phrozen.org>
*/

#include <ctype.h>
#include <netinet/ether.h>
#include "usteer.h"

static int
Expand Down Expand Up @@ -81,25 +83,36 @@ usteer_sta_update_aggressiveness(struct sta *sta)
{
struct blob_attr *cur;
int rem;
char sta_mac[18];
uint8_t *config_addr;
char config_entry[20];
char config_mac[18];

sprintf(sta_mac, MAC_ADDR_FMT, MAC_ADDR_DATA(sta->addr));

sta->aggressiveness = config.aggressiveness;
blobmsg_for_each_attr(cur, config.aggressiveness_mac_list, rem) {
strcpy(config_entry, blobmsg_get_string(cur));
if (strlen(config_entry) != 19)
continue;
strncpy(config_mac, config_entry, 18);
config_mac[17] = '\0';
if (strcmp(config_mac, sta_mac) != 0)
config_addr = (uint8_t *) ether_aton(config_mac);
if (memcmp(config_addr, sta->addr, 6) != 0)
continue;
sta->aggressiveness = config_entry[18] - '0';
MSG(DEBUG, "Set aggressiveness for station " MAC_ADDR_FMT " from config to %d\n",
MAC_ADDR_DATA(sta->addr), sta->aggressiveness);
break;
}
}

void
usteer_sta_load_each_aggressiveness()
{
struct sta *sta;
avl_for_each_element(&stations, sta, avl) {
usteer_sta_update_aggressiveness(sta);
}
}

struct sta_info *
usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create)
{
Expand Down Expand Up @@ -129,8 +142,6 @@ usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create)
si->created = current_time;
*create = true;

usteer_sta_update_aggressiveness(sta);

/* Node is by default not connected. */
usteer_sta_disconnected(si);

Expand Down Expand Up @@ -168,7 +179,7 @@ usteer_sta_get(const uint8_t *addr, bool create)
avl_insert(&stations, &sta->avl);
INIT_LIST_HEAD(&sta->nodes);
INIT_LIST_HEAD(&sta->measurements);

usteer_sta_update_aggressiveness(sta);
return sta;
}

Expand Down
Loading