From ff1e72dbd76bd31518a9e4014f3414ff70662935 Mon Sep 17 00:00:00 2001 From: Nils Hendrik Rottgardt Date: Sun, 20 Oct 2024 13:28:35 +0200 Subject: [PATCH 1/4] Optimized steering with different levels of aggressiveness Add some new options and also changed disassociation_timer/_imminent call to have a better roaming experience. This is necessary for some Intel WiFi cards ignoring actual transition request frames due to expecting a combination of disassociation_timer and disassociation_imminent = true. According to posts on the OpenWrt forum this behavior isn't limited to just Intel WiFi cards. Affected stations respond with BSS-TM-RESP status=1 (error) and refuse to roam. - Add new aggr. levels combined with station specific configuration - Set disassociation imminent and timer as default option to have a better roaming by default - Use abridged flag to put priority on neighbors provided - Take care of correct disassociation_timer corresponding to kick_time - Let Disassociation Timer run a countdown before dissassociating the station if roam_kick_delay is longer than validity_period like defined in IEEE. Fix validity_period to 100 beacon intervals. - Add new logging messages (verbose and debug) to visualize the station behaviour Signed-off-by: Nils Hendrik Rottgardt --- band_steering.c | 22 ++++++++++- local_node.c | 24 +++++++++-- main.c | 1 + openwrt/usteer/files/etc/config/usteer | 12 ++++++ openwrt/usteer/files/etc/init.d/usteer | 2 + policy.c | 55 ++++++++++++++++++++++---- sta.c | 26 ++++++++++++ ubus.c | 47 ++++++++++++++++------ usteer.h | 31 +++++++++++---- 9 files changed, 188 insertions(+), 32 deletions(-) diff --git a/band_steering.c b/band_steering.c index 7fce1df..a3dfd62 100644 --- a/band_steering.c +++ b/band_steering.c @@ -60,6 +60,8 @@ 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; + uint32_t disassoc_timer; + uint32_t validity_period; if (!config.band_steering_interval) return; @@ -91,8 +93,24 @@ void usteer_band_steering_perform_steer(struct usteer_local_node *ln) continue; } - if (si->bss_transition) - usteer_ubus_band_steering_request(si); + /* Skip if in validity period */ + if (current_time < si->roam_transition_request_validity_end) + continue; + + if (si->bss_transition) { + si->roam_transition_request_validity_end = current_time + 10000; + validity_period = 10000 / usteer_local_node_get_beacon_interval(ln); /* ~ 10 seconds */ + if (si->sta->aggressiveness >= 2) { + if (!si->kick_time) + si->kick_time = current_time + config.roam_kick_delay; + if (si->sta->aggressiveness >= 3) + disassoc_timer = (si->kick_time - current_time) / usteer_local_node_get_beacon_interval(ln); + else + disassoc_timer = 0; + usteer_ubus_band_steering_request(si, 0, true, disassoc_timer, true, validity_period); + } else + usteer_ubus_band_steering_request(si, 0, false, 0, true, validity_period); + } si->band_steering.below_snr = false; } diff --git a/local_node.c b/local_node.c index e74d945..df73332 100644 --- a/local_node.c +++ b/local_node.c @@ -180,9 +180,10 @@ usteer_handle_bss_tm_response(struct usteer_local_node *ln, struct blob_attr *ms si->bss_transition_response.status_code = blobmsg_get_u8(tb[BSS_TM_RESPONSE_STATUS_CODE]); si->bss_transition_response.timestamp = current_time; - if (si->bss_transition_response.status_code) { + if (si->bss_transition_response.status_code && si->kick_time && si->sta->aggressiveness) { /* Cancel imminent kick in case BSS transition was rejected */ si->kick_time = 0; + MSG(VERBOSE, "Kick canceled because transition rejected by station " MAC_ADDR_FMT "\n", MAC_ADDR_DATA(si->sta->addr)); } return 0; @@ -748,7 +749,7 @@ usteer_local_node_process_bss_tm_queries(struct uloop_timeout *timeout) if (!si) continue; - usteer_ubus_bss_transition_request(si, query->dialog_token, false, false, validity_period, NULL); + usteer_ubus_bss_transition_request(si, query->dialog_token, false, 0, true, validity_period, NULL); } /* Free pending queries we can not handle */ @@ -977,8 +978,23 @@ void config_get_ssid_list(struct blob_buf *buf) blobmsg_add_blob(buf, config.ssid_list); } -void -usteer_local_nodes_init(struct ubus_context *ctx) +void config_set_aggressiveness_mac_list(struct blob_attr *data) +{ + free(config.aggressiveness_mac_list); + + if (data && blobmsg_len(data)) + config.aggressiveness_mac_list = blob_memdup(data); + else + config.aggressiveness_mac_list = NULL; +} + +void config_get_aggressiveness_mac_list(struct blob_buf *buf) +{ + if (config.aggressiveness_mac_list) + blobmsg_add_blob(buf, config.aggressiveness_mac_list); +} + +void usteer_local_nodes_init(struct ubus_context *ctx) { usteer_register_events(ctx); ubus_lookup(ctx, "hostapd.*", node_list_cb, NULL); diff --git a/main.c b/main.c index 99aa6ad..dd2a6d2 100644 --- a/main.c +++ b/main.c @@ -96,6 +96,7 @@ void usteer_init_defaults(void) config.remote_update_interval = 1000; config.initial_connect_delay = 0; config.remote_node_timeout = 10; + config.aggressiveness = 3; config.steer_reject_timeout = 60000; diff --git a/openwrt/usteer/files/etc/config/usteer b/openwrt/usteer/files/etc/config/usteer index f53c338..5a7af8b 100644 --- a/openwrt/usteer/files/etc/config/usteer +++ b/openwrt/usteer/files/etc/config/usteer @@ -1,4 +1,6 @@ config usteer + # OpenWrt guide: https://openwrt.org/docs/guide-user/network/wifi/usteer + # The network interface for inter-AP communication option 'network' 'lan' @@ -71,6 +73,16 @@ config usteer # Timeout (ms) for which a client will not be steered after rejecting a BSS-transition-request #option steer_reject_timeout 60000 + # Aggressiveness of BSS-transition-request to push a station to another node (AP or band) + # 0 = no active transition / 1 = passive BSS-transition-request + # 2 = BSS-transition-request with disassociation imminent + # 3 = BSS-transition-request with disassociation imminent and timer + # 4 = BSS-transition-request with disassociation imminent, timer and forced disassociation + #option aggressiveness 3 + + # List of MACs (lower case) to set aggressiveness per station (ff:ff:ff:ff:ff,2) + #list aggressiveness_mac_list '' + # Timeout (in ms) after which a association following a disassociation is not seen # as a roam #option roam_process_timeout 5000 diff --git a/openwrt/usteer/files/etc/init.d/usteer b/openwrt/usteer/files/etc/init.d/usteer index 07fd99e..eefe809 100755 --- a/openwrt/usteer/files/etc/init.d/usteer +++ b/openwrt/usteer/files/etc/init.d/usteer @@ -69,8 +69,10 @@ uci_usteer() { uci_option_to_json_bool "$cfg" local_mode uci_option_to_json_bool "$cfg" load_kick_enabled uci_option_to_json_bool "$cfg" assoc_steering + uci_option_to_json_bool "$cfg" aggressiveness uci_option_to_json_string "$cfg" node_up_script uci_option_to_json_string_array "$cfg" ssid_list + uci_option_to_json_string_array "$cfg" aggressiveness_mac_list uci_option_to_json_string_array "$cfg" event_log_types for opt in \ diff --git a/policy.c b/policy.c index 8c5d244..8923d97 100644 --- a/policy.c +++ b/policy.c @@ -322,6 +322,8 @@ static bool usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si) { struct sta_info *candidate; + uint32_t disassoc_timer; + uint32_t validity_period; struct uevent ev = { .si_cur = si, }; @@ -343,10 +345,9 @@ usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si) /* Check if no node was found within roam_scan_tries tries */ if (config.roam_scan_tries && si->roam_tries >= config.roam_scan_tries) { if (!config.roam_scan_timeout) { - /* Prepare to kick client */ usteer_roam_set_state(si, ROAM_TRIGGER_SCAN_DONE, &ev); } else { - /* Kick in scan timeout */ + /* Set timeout until roam_scans are paused */ si->roam_scan_timeout_start = current_time; usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev); } @@ -363,6 +364,13 @@ usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si) break; case ROAM_TRIGGER_SCAN_DONE: + /* Roaming time over, switch back to ROAM_TRIGGER_IDLE */ + if (si->roam_transition_start && current_time - si->roam_transition_start > config.roam_kick_delay) { + si->roam_transition_start = 0; + usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev); + break; + } + candidate = usteer_roam_sm_found_better_node(si, &ev, ROAM_TRIGGER_SCAN_DONE); /* Kick back in case no better node is found */ if (!candidate) { @@ -370,9 +378,30 @@ usteer_roam_trigger_sm(struct usteer_local_node *ln, struct sta_info *si) break; } - usteer_ubus_bss_transition_request(si, 1, false, false, 100, candidate->node); - si->kick_time = current_time + config.roam_kick_delay; - usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev); + if (!candidate->node->rrm_nr) + MSG(VERBOSE, "Candidates node rrm nr not returned from hostapd. Neighbor list empty!"); + + if (!si->roam_transition_start) + si->roam_transition_start = current_time; + si->roam_transition_request_validity_end = current_time + 10000; + validity_period = 10000 / usteer_local_node_get_beacon_interval(ln); /* ~ 10 seconds */ + if (si->sta->aggressiveness >= 2) { + if (!si->kick_time) + si->kick_time = current_time + config.roam_kick_delay; + if (si->sta->aggressiveness >= 3) + disassoc_timer = (si->kick_time - current_time) / usteer_local_node_get_beacon_interval(ln); + else + disassoc_timer = 0; + usteer_ubus_bss_transition_request(si, 1, true, disassoc_timer, true, validity_period, candidate->node); + /* Countdown end */ + if (disassoc_timer < validity_period) { + si->roam_transition_start = 0; + usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev); + } + } else { + usteer_ubus_bss_transition_request(si, 1, false, 0, true, validity_period, candidate->node); + usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev); + } break; } @@ -385,14 +414,26 @@ bool usteer_policy_can_perform_roam(struct sta_info *si) if (si->connected != STA_CONNECTED) return false; + /* Only trigger for STA with active roaming */ + if (!si->sta->aggressiveness) + return false; + /* Skip on pending kick */ - if (si->kick_time) + if (si->kick_time && si->kick_time <= current_time) + return false; + + /* Skip if in validity period */ + if (current_time < si->roam_transition_request_validity_end) return false; /* Skip on rejected transition */ if (si->bss_transition_response.status_code && current_time - si->bss_transition_response.timestamp < config.steer_reject_timeout) return false; + /* Skip if transition accepted */ + if (!si->bss_transition_response.status_code && current_time - si->bss_transition_response.timestamp < config.roam_kick_delay) + return false; + /* Skip on previous kick attempt */ if (current_time - si->roam_kick < config.roam_trigger_interval) return false; @@ -400,7 +441,7 @@ bool usteer_policy_can_perform_roam(struct sta_info *si) /* Skip if connection is established shorter than the trigger-interval */ if (current_time - si->connected_since < config.roam_trigger_interval) return false; - + return true; } diff --git a/sta.c b/sta.c index ed7e40e..059e987 100644 --- a/sta.c +++ b/sta.c @@ -76,6 +76,30 @@ usteer_sta_info_timeout(struct usteer_timeout_queue *q, struct usteer_timeout *t usteer_sta_info_del(si); } +static void +usteer_sta_update_aggressiveness(struct sta *sta) +{ + struct blob_attr *cur; + int rem; + char sta_mac[18]; + 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) + continue; + sta->aggressiveness = config_entry[18] - '0'; + break; + } +} + struct sta_info * usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create) { @@ -105,6 +129,8 @@ 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); diff --git a/ubus.c b/ubus.c index 40daf74..197d1b9 100644 --- a/ubus.c +++ b/ubus.c @@ -162,6 +162,9 @@ struct cfg_item { _cfg(U32, remote_update_interval), \ _cfg(U32, remote_node_timeout), \ _cfg(BOOL, assoc_steering), \ + _cfg(U32, aggressiveness), \ + _cfg(ARRAY_CB, aggressiveness_mac_list), \ + _cfg(U32, aggressive_disassoc_timer), \ _cfg(I32, min_connect_snr), \ _cfg(I32, min_snr), \ _cfg(U32, min_snr_kick_delay), \ @@ -668,11 +671,12 @@ usteer_ubus_disassoc_add_neighbors(struct sta_info *si) } int usteer_ubus_bss_transition_request(struct sta_info *si, - uint8_t dialog_token, - bool disassoc_imminent, - bool abridged, - uint8_t validity_period, - struct usteer_node *target_node) + uint8_t dialog_token, + bool disassoc_imminent, + uint32_t disassoc_timer, + bool abridged, + uint8_t validity_period, + struct usteer_node *target_node) { struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node); @@ -680,17 +684,29 @@ int usteer_ubus_bss_transition_request(struct sta_info *si, blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr)); blobmsg_add_u32(&b, "dialog_token", dialog_token); blobmsg_add_u8(&b, "disassociation_imminent", disassoc_imminent); + if (disassoc_imminent) { + blobmsg_add_u32(&b, "disassociation_timer", disassoc_timer); + } blobmsg_add_u8(&b, "abridged", abridged); blobmsg_add_u32(&b, "validity_period", validity_period); + if (!target_node) { + /* Add all known neighbors if no specific target set */ + MSG(VERBOSE, "roaming station " MAC_ADDR_FMT " without target\n", MAC_ADDR_DATA(si->sta->addr)); usteer_ubus_disassoc_add_neighbors(si); } else { + MSG(VERBOSE, "roaming station " MAC_ADDR_FMT " to " MAC_ADDR_FMT " (%s) disassociation timer %u, signal %d\n", MAC_ADDR_DATA(si->sta->addr), MAC_ADDR_DATA(target_node->bssid), usteer_node_name(target_node), disassoc_timer, si->signal); usteer_ubus_disassoc_add_neighbor(si, target_node); } 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) +int usteer_ubus_band_steering_request(struct sta_info *si, + uint8_t dialog_token, + bool disassoc_imminent, + uint32_t disassoc_timer, + bool abridged, + uint8_t validity_period) { struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node); struct usteer_node *node; @@ -698,10 +714,13 @@ int usteer_ubus_band_steering_request(struct sta_info *si) 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); + blobmsg_add_u32(&b, "dialog_token", dialog_token); + blobmsg_add_u8(&b, "disassociation_imminent", disassoc_imminent); + if (disassoc_imminent) { + blobmsg_add_u32(&b, "disassociation_timer", disassoc_timer); + } + blobmsg_add_u8(&b, "abridged", abridged); + blobmsg_add_u32(&b, "validity_period", validity_period); c = blobmsg_open_array(&b, "neighbors"); for_each_local_node(node) { @@ -709,10 +728,14 @@ int usteer_ubus_band_steering_request(struct sta_info *si) continue; usteer_add_nr_entry(si->node, node); + MSG(DEBUG, "band steering station " MAC_ADDR_FMT " adding neighbor " MAC_ADDR_FMT " (%s)\n", MAC_ADDR_DATA(si->sta->addr), MAC_ADDR_DATA(node->bssid), usteer_node_name(node)); } blobmsg_close_array(&b, c); - - return ubus_invoke(ubus_ctx, ln->obj_id, "bss_transition_request", b.head, NULL, 0, 100); + if (sizeof(si->node) > 0) { + MSG(VERBOSE, "band steering station " MAC_ADDR_FMT " disassociation timer %u, signal %d\n", MAC_ADDR_DATA(si->sta->addr), disassoc_timer, si->signal); + return ubus_invoke(ubus_ctx, ln->obj_id, "bss_transition_request", b.head, NULL, 0, 100); + } else + MSG(VERBOSE, "band steering no targets found for station " MAC_ADDR_FMT "\n", MAC_ADDR_DATA(si->sta->addr)); } int usteer_ubus_trigger_link_measurement(struct sta_info *si) diff --git a/usteer.h b/usteer.h index f692fb8..86cb99f 100644 --- a/usteer.h +++ b/usteer.h @@ -170,6 +170,10 @@ struct usteer_config { uint32_t remote_update_interval; uint32_t remote_node_timeout; + uint32_t aggressiveness; + struct blob_attr *aggressiveness_mac_list; + uint32_t aggressive_disassoc_timer; + int32_t min_snr; uint32_t min_snr_kick_delay; int32_t min_connect_snr; @@ -190,7 +194,7 @@ struct usteer_config { uint32_t roam_kick_delay; uint32_t band_steering_interval; - int32_t band_steering_min_snr; + int32_t band_steering_min_snr; uint32_t link_measurement_interval; @@ -254,6 +258,8 @@ struct sta_info { enum roam_trigger_state roam_state; uint8_t roam_tries; uint64_t roam_event; + uint64_t roam_transition_request_validity_end; + uint64_t roam_transition_start; uint64_t roam_kick; uint64_t roam_scan_start; uint64_t roam_scan_timeout_start; @@ -285,6 +291,8 @@ struct sta { uint8_t seen_2ghz : 1; uint8_t seen_5ghz : 1; + uint32_t aggressiveness; + uint8_t addr[6]; }; @@ -336,13 +344,19 @@ bool usteer_band_steering_is_target(struct usteer_local_node *ln, struct usteer_ 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_band_steering_request(struct sta_info *si); +int usteer_ubus_band_steering_request(struct sta_info *si, + uint8_t dialog_token, + bool disassoc_imminent, + uint32_t disassoc_timer, + bool abridged, + uint8_t validity_period); int usteer_ubus_bss_transition_request(struct sta_info *si, - uint8_t dialog_token, - bool disassoc_imminent, - bool abridged, - uint8_t validity_period, - struct usteer_node *target_node); + uint8_t dialog_token, + bool disassoc_imminent, + uint32_t disassoc_timer, + bool abridged, + uint8_t validity_period, + struct usteer_node *target_node); struct sta *usteer_sta_get(const uint8_t *addr, bool create); struct sta_info *usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create); @@ -376,6 +390,9 @@ void config_get_node_up_script(struct blob_buf *buf); void config_set_ssid_list(struct blob_attr *data); void config_get_ssid_list(struct blob_buf *buf); +void config_set_aggressiveness_mac_list(struct blob_attr *data); +void config_get_aggressiveness_mac_list(struct blob_buf *buf); + int usteer_interface_init(void); void usteer_interface_add(const char *name); void usteer_sta_node_cleanup(struct usteer_node *node); From efb5aa90d9b31f8721f0e432c8fe32a80cbc6b28 Mon Sep 17 00:00:00 2001 From: Nils Hendrik Rottgardt Date: Sun, 3 Nov 2024 12:37:07 +0100 Subject: [PATCH 2/4] 802.11 Mgmt: DEAUTH reason more accurate Previous reason codes sent to stations were inaccurate. Respond with correct DEAUTH reason codes in order to give stations better control over their roaming decisions. - policy: correct DEAUTH reason is send to the station - ubus: add reason code to method usteer_ubus_kick_client - usteer: clearly define reason codes as enums Signed-off-by: Nils Hendrik Rottgardt --- policy.c | 6 +++--- ubus.c | 4 ++-- usteer.h | 9 ++++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/policy.c b/policy.c index 8923d97..0ccb73f 100644 --- a/policy.c +++ b/policy.c @@ -524,7 +524,7 @@ usteer_local_node_snr_kick(struct usteer_local_node *ln) ev.count = si->kick_count; usteer_event(&ev); - usteer_ubus_kick_client(si); + usteer_ubus_kick_client(si, KICK_REASON_UNSPECIFIED); return; } } @@ -608,7 +608,7 @@ usteer_local_node_load_kick(struct usteer_local_node *ln) ev.si_other = candidate; ev.count = kick1->kick_count; - usteer_ubus_kick_client(kick1); + usteer_ubus_kick_client(kick1, config.load_kick_reason_code); out: usteer_event(&ev); @@ -623,7 +623,7 @@ usteer_local_node_perform_kick(struct usteer_local_node *ln) if (!si->kick_time || si->kick_time > current_time) continue; - usteer_ubus_kick_client(si); + usteer_ubus_kick_client(si, KICK_REASON_BSS_TRANSITION); } } diff --git a/ubus.c b/ubus.c index 197d1b9..2d30ee1 100644 --- a/ubus.c +++ b/ubus.c @@ -773,13 +773,13 @@ int usteer_ubus_trigger_client_scan(struct sta_info *si) return ubus_invoke(ubus_ctx, ln->obj_id, "rrm_beacon_req", b.head, NULL, 0, 100); } -void usteer_ubus_kick_client(struct sta_info *si) +void usteer_ubus_kick_client(struct sta_info *si, uint32_t kick_reason_code) { struct usteer_local_node *ln = container_of(si->node, struct usteer_local_node, node); blob_buf_init(&b, 0); blobmsg_printf(&b, "addr", MAC_ADDR_FMT, MAC_ADDR_DATA(si->sta->addr)); - blobmsg_add_u32(&b, "reason", config.load_kick_reason_code); + blobmsg_add_u32(&b, "reason", kick_reason_code); blobmsg_add_u8(&b, "deauth", 1); ubus_invoke(ubus_ctx, ln->obj_id, "del_client", b.head, NULL, 0, 100); usteer_sta_disconnected(si); diff --git a/usteer.h b/usteer.h index 86cb99f..a4346d7 100644 --- a/usteer.h +++ b/usteer.h @@ -66,6 +66,13 @@ enum usteer_beacon_measurement_mode { BEACON_MEASUREMENT_TABLE = 2, }; +enum usteer_kick_reason_code +{ + KICK_REASON_UNSPECIFIED = 1, + KICK_REASON_LOAD = 5, + KICK_REASON_BSS_TRANSITION = 12, +}; + struct sta_info; struct usteer_local_node; struct usteer_remote_host; @@ -342,7 +349,7 @@ 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); +void usteer_ubus_kick_client(struct sta_info *si, uint32_t kick_reason_code); int usteer_ubus_trigger_client_scan(struct sta_info *si); int usteer_ubus_band_steering_request(struct sta_info *si, uint8_t dialog_token, From 22e31ac063ec64c07e89f81a4d284e69b7343f39 Mon Sep 17 00:00:00 2001 From: Nils Hendrik Rottgardt Date: Sun, 10 Nov 2024 23:47:33 +0100 Subject: [PATCH 3/4] Add dynamic signal threshold to band-steering Avoids ping-pong between bands by adding a dynamic signal threshold. If 5Ghz band is below roaming threshold (e.g. -70) and 2,4Ghz over band-steering threshold (e.g. -50) this adds a dynamic threshold depends on the signal level when station has connected. It adapts to worser signal slowly. This allows to reduce the check interval to lower values like 30s as new standard. - band_steering: added dynamic signal threshold - main: added config option band_steering_signal threshold which specifies the difference until a signal is defined as "better" and band steering is triggered - main: reduced interval to 30000ms to get in use of dynamic threshold more often. It is not neccessary to "wait" anymore to steer a station to a higher band. Signed-off-by: Nils Hendrik Rottgardt --- band_steering.c | 19 ++++++++++++++++++- main.c | 3 ++- openwrt/usteer/files/etc/config/usteer | 4 ++++ openwrt/usteer/files/etc/init.d/usteer | 6 +++--- usteer.h | 2 ++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/band_steering.c b/band_steering.c index a3dfd62..7014b03 100644 --- a/band_steering.c +++ b/band_steering.c @@ -20,7 +20,24 @@ void usteer_band_steering_sta_update(struct sta_info *si) { - if (si->signal < usteer_snr_to_signal(si->node, config.band_steering_min_snr)) + if (si->connected == STA_NOT_CONNECTED) { + if (si->band_steering.signal_threshold != NO_SIGNAL) { + si->band_steering.signal_threshold = NO_SIGNAL; + } + return; + } + if (si->connected != STA_NOT_CONNECTED && si->band_steering.signal_threshold == NO_SIGNAL) { + si->band_steering.signal_threshold = si->signal; + MSG(DEBUG, "band steering station " MAC_ADDR_FMT " (%s) set threshold %d\n", MAC_ADDR_DATA(si->sta->addr), usteer_node_name(si->node), si->band_steering.signal_threshold); + return; + } + + /* Adapt signal threshold to actual signal quality */ + if (si->signal < si->band_steering.signal_threshold) { + si->band_steering.signal_threshold--; + MSG(DEBUG, "band steering station " MAC_ADDR_FMT " (%s) reduce threshold %d, signal: %d\n", MAC_ADDR_DATA(si->sta->addr), usteer_node_name(si->node), si->band_steering.signal_threshold, si->signal); + } + if (si->signal < usteer_snr_to_signal(si->node, config.band_steering_min_snr) || si->signal < si->band_steering.signal_threshold + config.band_steering_signal_threshold) si->band_steering.below_snr = true; } diff --git a/main.c b/main.c index dd2a6d2..d84c815 100644 --- a/main.c +++ b/main.c @@ -100,8 +100,9 @@ void usteer_init_defaults(void) config.steer_reject_timeout = 60000; - config.band_steering_interval = 120000; + config.band_steering_interval = 30000; config.band_steering_min_snr = -60; + config.band_steering_signal_threshold = 5; config.link_measurement_interval = 30000; diff --git a/openwrt/usteer/files/etc/config/usteer b/openwrt/usteer/files/etc/config/usteer index 5a7af8b..858a20b 100644 --- a/openwrt/usteer/files/etc/config/usteer +++ b/openwrt/usteer/files/etc/config/usteer @@ -140,6 +140,10 @@ config usteer # steered to a higher frequency band #option band_steering_min_snr -60 + # Difference that the signal must be better compared to signal was on connection to node. + # Avoids conflicts between roaming and band-steering policies. + # option band_steering_signal_threshold 5 + # Interval (ms) the device is sent a link-measurement request to help assess # the bi-directional link quality. Setting the interval to 0 disables link-measurements. #option link_measurement_interval 30000 diff --git a/openwrt/usteer/files/etc/init.d/usteer b/openwrt/usteer/files/etc/init.d/usteer index eefe809..a7d3fdf 100755 --- a/openwrt/usteer/files/etc/init.d/usteer +++ b/openwrt/usteer/files/etc/init.d/usteer @@ -85,9 +85,9 @@ uci_usteer() { min_connect_snr min_snr min_snr_kick_delay signal_diff_threshold \ initial_connect_delay steer_reject_timeout roam_process_timeout\ 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 link_measurement_interval \ + roam_scan_snr roam_scan_interval roam_trigger_snr roam_trigger_interval \ + link_measurement_interval \ + band_steering_interval band_steering_min_snr band_steering_signal_threshold \ load_kick_threshold load_kick_delay load_kick_min_clients \ load_kick_reason_code do diff --git a/usteer.h b/usteer.h index a4346d7..0f27077 100644 --- a/usteer.h +++ b/usteer.h @@ -202,6 +202,7 @@ struct usteer_config { uint32_t band_steering_interval; int32_t band_steering_min_snr; + uint32_t band_steering_signal_threshold; uint32_t link_measurement_interval; @@ -278,6 +279,7 @@ struct sta_info { struct { bool below_snr; + int signal_threshold; } band_steering; uint64_t kick_time; From edd1f09ced2e7544fbbbc11252ae30262788ea6c Mon Sep 17 00:00:00 2001 From: Nils Hendrik Rottgardt Date: Sun, 3 Nov 2024 20:42:20 +0100 Subject: [PATCH 4/4] 802.11 MBO - added reassoc_delay and reason code Multi Band Operations (MBO) provide stations with additional information about roaming requests. Reassociation delay avoids ping-pong between APs. By default a delay of 30s is requested from the station. MBO code 5 defines signal quality as a reason for roaming. This could be enhanced in the future by responding with other dynamically defined reasons to improve further how transitions are handled by stations. - ubus: add reason code to bss_transition request (actually static only) - ubus: add reassociation delay to let the client know how long it should stay at the new AP dereived from station block timeout. - main: add config option for reassociation delay with 30s as default Signed-off-by: Nils Hendrik Rottgardt --- main.c | 1 + openwrt/usteer/files/etc/config/usteer | 3 +++ ubus.c | 4 ++++ usteer.h | 1 + 4 files changed, 9 insertions(+) diff --git a/main.c b/main.c index d84c815..bd6ae4b 100644 --- a/main.c +++ b/main.c @@ -97,6 +97,7 @@ void usteer_init_defaults(void) config.initial_connect_delay = 0; config.remote_node_timeout = 10; config.aggressiveness = 3; + config.reassociation_delay = 30; config.steer_reject_timeout = 60000; diff --git a/openwrt/usteer/files/etc/config/usteer b/openwrt/usteer/files/etc/config/usteer index 858a20b..ecb43d9 100644 --- a/openwrt/usteer/files/etc/config/usteer +++ b/openwrt/usteer/files/etc/config/usteer @@ -80,6 +80,9 @@ 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 '' diff --git a/ubus.c b/ubus.c index 2d30ee1..0ed9f1d 100644 --- a/ubus.c +++ b/ubus.c @@ -689,6 +689,8 @@ int usteer_ubus_bss_transition_request(struct sta_info *si, } blobmsg_add_u8(&b, "abridged", abridged); blobmsg_add_u32(&b, "validity_period", validity_period); + blobmsg_add_u32(&b, "mbo_reason", 5); + blobmsg_add_u32(&b, "reassoc_delay", config.reassociation_delay); if (!target_node) { /* Add all known neighbors if no specific target set */ @@ -721,6 +723,8 @@ int usteer_ubus_band_steering_request(struct sta_info *si, } blobmsg_add_u8(&b, "abridged", abridged); blobmsg_add_u32(&b, "validity_period", validity_period); + blobmsg_add_u32(&b, "mbo_reason", 5); + blobmsg_add_u32(&b, "reassoc_delay", config.reassociation_delay); c = blobmsg_open_array(&b, "neighbors"); for_each_local_node(node) { diff --git a/usteer.h b/usteer.h index 0f27077..407d02d 100644 --- a/usteer.h +++ b/usteer.h @@ -180,6 +180,7 @@ struct usteer_config { uint32_t aggressiveness; struct blob_attr *aggressiveness_mac_list; uint32_t aggressive_disassoc_timer; + uint32_t reassociation_delay; int32_t min_snr; uint32_t min_snr_kick_delay;