[usteer] New aggressive roaming to support Intel Wifi Cards (and also, other devices)

Nils Rottgardt n.rottgardt at gmail.com
Sat Oct 19 09:06:11 PDT 2024


>From 2b0bb0b37e0f34be67be5d8e115a60354d55fce0 Mon Sep 17 00:00:00 2001
From: Nils Hendrik Rottgardt <n.rottgardt at gmail.com>
Date: Tue, 8 Oct 2024 23:30:19 +0200
Subject: [usteer] New aggressive roaming to support Intel Wifi Cards
(and also
 other devices)

Intel Wifi Cards does not understand the actual implementation because
of missing disassociation_timer and disassociation_imminent = true. So
they and other devices send a BSS-TM-RESP with status=1 (error).
This patch with add some new options to correct this behavior and also
fix the wrong bss_transition_request call.

- Added aggressive roaming (disaccociation_timer) and corrected calling
bss_transition_request with disassociation_imminent = false.
- Added 3 new options in config to control aggressive roaming for all or
specific MAC addresses.

- sta: corrected some linebreaks

Signed-off-by: Nils Hendrik Rottgardt <n.rottgardt at gmail.com>
---
 band_steering.c                        |  27 +-
 local_node.c                           | 267 ++++++++++++--------
 main.c                                 |  56 +++--
 openwrt/usteer/files/etc/config/usteer |   9 +
 openwrt/usteer/files/etc/init.d/usteer |   4 +-
 policy.c                               | 148 +++++++----
 sta.c                                  | 101 +++++---
 ubus.c                                 | 335 ++++++++++++++-----------
 usteer.h                               | 123 +++++----
 9 files changed, 654 insertions(+), 416 deletions(-)

diff --git a/band_steering.c b/band_steering.c
index 7fce1df..470f904 100644
--- a/band_steering.c
+++ b/band_steering.c
@@ -37,18 +37,18 @@ bool usteer_band_steering_is_target(struct
usteer_local_node *ln, struct usteer_
 
     if (!usteer_policy_node_below_max_assoc(node))
         return false;
-    
+
     /* ToDo: Skip nodes with active load-kick */
-    
-    return true;
- }
 
+    return true;
+}
 
 static bool usteer_band_steering_has_target_iface(struct
usteer_local_node *ln)
 {
     struct usteer_node *node;
 
-    for_each_local_node(node) {
+    for_each_local_node(node)
+    {
         if (usteer_band_steering_is_target(ln, node))
             return true;
     }
@@ -73,26 +73,35 @@ void usteer_band_steering_perform_steer(struct
usteer_local_node *ln)
         return;
 
     /* Only steer every interval */
-    if (ln->band_steering_interval < min_count) {
+    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) {
+    list_for_each_entry(si, &ln->node.sta_info, node_list)
+    {
         /* Check if client is eligable to be steerd */
         if (!usteer_policy_can_perform_roam(si))
             continue;
 
         /* Skip clients with insufficient SNR-state */
-        if (si->band_steering.below_snr) {
+        if (si->band_steering.below_snr)
+        {
             si->band_steering.below_snr = false;
             continue;
         }
 
         if (si->bss_transition)
-            usteer_ubus_band_steering_request(si);
+        {
+            // usteer_ubus_band_steering_request(si, 0, false, 100,
true, 100);
+            if (si->sta->aggressive)
+                usteer_ubus_band_steering_request(si, 0, true,
config.aggressive_disassoc_timer, true, config.aggressive_disassoc_timer);
+            else
+                usteer_ubus_band_steering_request(si, 0, false, 0,
true, 100);
+        }
 
         si->band_steering.below_snr = false;
     }
diff --git a/local_node.c b/local_node.c
index e74d945..b415196 100644
--- a/local_node.c
+++ b/local_node.c
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #include <sys/types.h>
@@ -51,7 +51,8 @@ usteer_local_node_pending_bss_tm_free(struct
usteer_local_node *ln)
 {
     struct usteer_bss_tm_query *query, *tmp;
 
-    list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list) {
+    list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list)
+    {
         list_del(&query->list);
         free(query);
     }
@@ -62,7 +63,8 @@ usteer_free_node(struct ubus_context *ctx, struct
usteer_local_node *ln)
 {
     struct usteer_node_handler *h;
 
-    list_for_each_entry(h, &node_handlers, list) {
+    list_for_each_entry(h, &node_handlers, list)
+    {
         if (!h->free_node)
             continue;
         h->free_node(&ln->node);
@@ -80,11 +82,13 @@ usteer_free_node(struct ubus_context *ctx, struct
usteer_local_node *ln)
     free(ln);
 }
 
-struct usteer_local_node *usteer_local_node_by_bssid(uint8_t *bssid) {
+struct usteer_local_node *usteer_local_node_by_bssid(uint8_t *bssid)
+{
     struct usteer_local_node *ln;
     struct usteer_node *n;
 
-    for_each_local_node(n) {
+    for_each_local_node(n)
+    {
         ln = container_of(n, struct usteer_local_node, node);
         if (!memcmp(n->bssid, bssid, 6))
             return ln;
@@ -95,7 +99,7 @@ struct usteer_local_node
*usteer_local_node_by_bssid(uint8_t *bssid) {
 
 static void
 usteer_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s,
-            uint32_t id)
+                     uint32_t id)
 {
     struct usteer_local_node *ln = container_of(s, struct
usteer_local_node, ev);
 
@@ -105,16 +109,17 @@ usteer_handle_remove(struct ubus_context *ctx,
struct ubus_subscriber *s,
 static int
 usteer_handle_bss_tm_query(struct usteer_local_node *ln, struct
blob_attr *msg)
 {
-    enum {
+    enum
+    {
         BSS_TM_QUERY_ADDRESS,
         BSS_TM_QUERY_DIALOG_TOKEN,
         BSS_TM_QUERY_CANDIDATE_LIST,
         __BSS_TM_QUERY_MAX
     };
     struct blobmsg_policy policy[__BSS_TM_QUERY_MAX] = {
-        [BSS_TM_QUERY_ADDRESS] = { .name = "address", .type =
BLOBMSG_TYPE_STRING },
-        [BSS_TM_QUERY_DIALOG_TOKEN] = { .name = "dialog-token", .type =
BLOBMSG_TYPE_INT8 },
-        [BSS_TM_QUERY_CANDIDATE_LIST] = { .name = "candidate-list",
.type = BLOBMSG_TYPE_STRING },
+        [BSS_TM_QUERY_ADDRESS] = {.name = "address", .type =
BLOBMSG_TYPE_STRING},
+        [BSS_TM_QUERY_DIALOG_TOKEN] = {.name = "dialog-token", .type =
BLOBMSG_TYPE_INT8},
+        [BSS_TM_QUERY_CANDIDATE_LIST] = {.name = "candidate-list",
.type = BLOBMSG_TYPE_STRING},
     };
     struct blob_attr *tb[__BSS_TM_QUERY_MAX];
     struct usteer_bss_tm_query *query;
@@ -131,7 +136,7 @@ usteer_handle_bss_tm_query(struct usteer_local_node
*ln, struct blob_attr *msg)
 
     query->dialog_token = blobmsg_get_u8(tb[BSS_TM_QUERY_DIALOG_TOKEN]);
 
-    sta_addr = (uint8_t *)
ether_aton(blobmsg_get_string(tb[BSS_TM_QUERY_ADDRESS]));
+    sta_addr = (uint8_t
*)ether_aton(blobmsg_get_string(tb[BSS_TM_QUERY_ADDRESS]));
     if (!sta_addr)
         return 0;
 
@@ -146,14 +151,15 @@ usteer_handle_bss_tm_query(struct
usteer_local_node *ln, struct blob_attr *msg)
 static int
 usteer_handle_bss_tm_response(struct usteer_local_node *ln, struct
blob_attr *msg)
 {
-    enum {
+    enum
+    {
         BSS_TM_RESPONSE_ADDRESS,
         BSS_TM_RESPONSE_STATUS_CODE,
         __BSS_TM_RESPONSE_MAX
     };
     struct blobmsg_policy policy[__BSS_TM_RESPONSE_MAX] = {
-        [BSS_TM_RESPONSE_ADDRESS] = { .name = "address", .type =
BLOBMSG_TYPE_STRING },
-        [BSS_TM_RESPONSE_STATUS_CODE] = { .name = "status-code", .type
= BLOBMSG_TYPE_INT8 },
+        [BSS_TM_RESPONSE_ADDRESS] = {.name = "address", .type =
BLOBMSG_TYPE_STRING},
+        [BSS_TM_RESPONSE_STATUS_CODE] = {.name = "status-code", .type =
BLOBMSG_TYPE_INT8},
     };
     struct blob_attr *tb[__BSS_TM_RESPONSE_MAX];
     struct sta_info *si;
@@ -165,7 +171,7 @@ usteer_handle_bss_tm_response(struct
usteer_local_node *ln, struct blob_attr *ms
     if (!tb[BSS_TM_RESPONSE_ADDRESS] || !tb[BSS_TM_RESPONSE_STATUS_CODE])
         return 0;
 
-    sta_addr = (uint8_t *)
ether_aton(blobmsg_get_string(tb[BSS_TM_RESPONSE_ADDRESS]));
+    sta_addr = (uint8_t
*)ether_aton(blobmsg_get_string(tb[BSS_TM_RESPONSE_ADDRESS]));
     if (!sta_addr)
         return 0;
 
@@ -180,7 +186,8 @@ 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)
+    {
         /* Cancel imminent kick in case BSS transition was rejected */
         si->kick_time = 0;
     }
@@ -191,7 +198,8 @@ usteer_handle_bss_tm_response(struct
usteer_local_node *ln, struct blob_attr *ms
 static int
 usteer_local_node_handle_beacon_report(struct usteer_local_node *ln,
struct blob_attr *msg)
 {
-    enum {
+    enum
+    {
         BR_ADDRESS,
         BR_BSSID,
         BR_RCPI,
@@ -199,10 +207,10 @@ usteer_local_node_handle_beacon_report(struct
usteer_local_node *ln, struct blob
         __BR_MAX
     };
     struct blobmsg_policy policy[__BR_MAX] = {
-        [BR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
-        [BR_BSSID] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
-        [BR_RCPI] = { .name = "rcpi", .type = BLOBMSG_TYPE_INT16 },
-        [BR_RSNI] = { .name = "rsni", .type = BLOBMSG_TYPE_INT16 },
+        [BR_ADDRESS] = {.name = "address", .type = BLOBMSG_TYPE_STRING},
+        [BR_BSSID] = {.name = "bssid", .type = BLOBMSG_TYPE_STRING},
+        [BR_RCPI] = {.name = "rcpi", .type = BLOBMSG_TYPE_INT16},
+        [BR_RSNI] = {.name = "rsni", .type = BLOBMSG_TYPE_INT16},
     };
     struct blob_attr *tb[__BR_MAX];
     struct usteer_node *node;
@@ -213,7 +221,7 @@ usteer_local_node_handle_beacon_report(struct
usteer_local_node *ln, struct blob
     if (!tb[BR_ADDRESS] || !tb[BR_BSSID] || !tb[BR_RCPI] || !tb[BR_RSNI])
         return 0;
 
-    addr = (uint8_t *) ether_aton(blobmsg_get_string(tb[BR_ADDRESS]));
+    addr = (uint8_t *)ether_aton(blobmsg_get_string(tb[BR_ADDRESS]));
     if (!addr)
         return 0;
 
@@ -221,7 +229,7 @@ usteer_local_node_handle_beacon_report(struct
usteer_local_node *ln, struct blob
     if (!sta)
         return 0;
 
-    addr = (uint8_t *) ether_aton(blobmsg_get_string(tb[BR_BSSID]));
+    addr = (uint8_t *)ether_aton(blobmsg_get_string(tb[BR_BSSID]));
     if (!addr)
         return 0;
 
@@ -230,25 +238,26 @@ usteer_local_node_handle_beacon_report(struct
usteer_local_node *ln, struct blob
         return 0;
 
     usteer_measurement_report_add(sta, node,
-                      (uint8_t)blobmsg_get_u16(tb[BR_RCPI]),
-                      (uint8_t)blobmsg_get_u16(tb[BR_RSNI]),
-                      current_time);
+                                  (uint8_t)blobmsg_get_u16(tb[BR_RCPI]),
+                                  (uint8_t)blobmsg_get_u16(tb[BR_RSNI]),
+                                  current_time);
     return 0;
 }
 
 static int
 usteer_local_node_handle_link_measurement_report(struct
usteer_local_node *ln, struct blob_attr *msg)
 {
-    enum {
+    enum
+    {
         LMR_ADDRESS,
         LMR_RCPI,
         LMR_RSNI,
         __LMR_MAX
     };
     struct blobmsg_policy policy[__LMR_MAX] = {
-        [LMR_ADDRESS] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
-        [LMR_RCPI] = { .name = "rcpi", .type = BLOBMSG_TYPE_INT16 },
-        [LMR_RSNI] = { .name = "rsni", .type = BLOBMSG_TYPE_INT16 },
+        [LMR_ADDRESS] = {.name = "address", .type = BLOBMSG_TYPE_STRING},
+        [LMR_RCPI] = {.name = "rcpi", .type = BLOBMSG_TYPE_INT16},
+        [LMR_RSNI] = {.name = "rsni", .type = BLOBMSG_TYPE_INT16},
     };
     struct blob_attr *tb[__LMR_MAX];
     uint8_t *addr;
@@ -258,7 +267,7 @@
usteer_local_node_handle_link_measurement_report(struct
usteer_local_node *ln, s
     if (!tb[LMR_ADDRESS] || !tb[LMR_RCPI] || !tb[LMR_RSNI])
         return 0;
 
-    addr = (uint8_t *) ether_aton(blobmsg_get_string(tb[LMR_ADDRESS]));
+    addr = (uint8_t *)ether_aton(blobmsg_get_string(tb[LMR_ADDRESS]));
     if (!addr)
         return 0;
 
@@ -267,18 +276,19 @@
usteer_local_node_handle_link_measurement_report(struct
usteer_local_node *ln, s
         return 0;
 
     usteer_measurement_report_add(sta, &ln->node,
-                      (uint8_t)blobmsg_get_u16(tb[LMR_RCPI]),
-                      (uint8_t)blobmsg_get_u16(tb[LMR_RSNI]),
-                      current_time);
+                                  (uint8_t)blobmsg_get_u16(tb[LMR_RCPI]),
+                                  (uint8_t)blobmsg_get_u16(tb[LMR_RSNI]),
+                                  current_time);
     return 0;
 }
 
 static int
 usteer_handle_event(struct ubus_context *ctx, struct ubus_object *obj,
-           struct ubus_request_data *req, const char *method,
-           struct blob_attr *msg)
+                    struct ubus_request_data *req, const char *method,
+                    struct blob_attr *msg)
 {
-    enum {
+    enum
+    {
         EVENT_ADDR,
         EVENT_SIGNAL,
         EVENT_TARGET,
@@ -286,10 +296,10 @@ usteer_handle_event(struct ubus_context *ctx,
struct ubus_object *obj,
         __EVENT_MAX
     };
     struct blobmsg_policy policy[__EVENT_MAX] = {
-        [EVENT_ADDR] = { .name = "address", .type = BLOBMSG_TYPE_STRING },
-        [EVENT_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
-        [EVENT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
-        [EVENT_FREQ] = { .name = "freq", .type = BLOBMSG_TYPE_INT32 },
+        [EVENT_ADDR] = {.name = "address", .type = BLOBMSG_TYPE_STRING},
+        [EVENT_SIGNAL] = {.name = "signal", .type = BLOBMSG_TYPE_INT32},
+        [EVENT_TARGET] = {.name = "target", .type = BLOBMSG_TYPE_STRING},
+        [EVENT_FREQ] = {.name = "freq", .type = BLOBMSG_TYPE_INT32},
     };
     enum usteer_event_type ev_type = __EVENT_TYPE_MAX;
     struct blob_attr *tb[__EVENT_MAX];
@@ -306,17 +316,25 @@ usteer_handle_event(struct ubus_context *ctx,
struct ubus_object *obj,
 
     ln = container_of(obj, struct usteer_local_node, ev.obj);
 
-    if(!strcmp(method, "bss-transition-query")) {
+    if (!strcmp(method, "bss-transition-query"))
+    {
         return usteer_handle_bss_tm_query(ln, msg);
-    } else if(!strcmp(method, "bss-transition-response")) {
+    }
+    else if (!strcmp(method, "bss-transition-response"))
+    {
         return usteer_handle_bss_tm_response(ln, msg);
-    } else if(!strcmp(method, "beacon-report")) {
+    }
+    else if (!strcmp(method, "beacon-report"))
+    {
         return usteer_local_node_handle_beacon_report(ln, msg);
-    } else if(!strcmp(method, "link-measurement-report")) {
+    }
+    else if (!strcmp(method, "link-measurement-report"))
+    {
         return usteer_local_node_handle_link_measurement_report(ln, msg);
     }
 
-    for (i = 0; i < ARRAY_SIZE(event_types); i++) {
+    for (i = 0; i < ARRAY_SIZE(event_types); i++)
+    {
         if (strcmp(method, event_types[i]) != 0)
             continue;
 
@@ -331,20 +349,20 @@ usteer_handle_event(struct ubus_context *ctx,
struct ubus_object *obj,
         return UBUS_STATUS_INVALID_ARGUMENT;
 
     if (tb[EVENT_SIGNAL])
-        signal = (int32_t) blobmsg_get_u32(tb[EVENT_SIGNAL]);
+        signal = (int32_t)blobmsg_get_u32(tb[EVENT_SIGNAL]);
 
     if (tb[EVENT_FREQ])
         freq = blobmsg_get_u32(tb[EVENT_FREQ]);
 
     addr_str = blobmsg_data(tb[EVENT_ADDR]);
-    addr = (uint8_t *) ether_aton(addr_str);
+    addr = (uint8_t *)ether_aton(addr_str);
     if (!addr)
         return UBUS_STATUS_INVALID_ARGUMENT;
 
     ret = usteer_handle_sta_event(node, addr, ev_type, freq, signal);
 
     MSG(DEBUG, "received %s event from %s, signal=%d, freq=%d,
handled:%s\n",
-        method, addr_str, signal, freq, ret ? "true" : "false");
+        method, addr_str, signal, freq, ret ? "true" : "false");
 
     return ret ? 0 : 17 /* WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA */;
 }
@@ -352,27 +370,32 @@ usteer_handle_event(struct ubus_context *ctx,
struct ubus_object *obj,
 static void
 usteer_local_node_assoc_update(struct sta_info *si, struct blob_attr *data)
 {
-    enum {
+    enum
+    {
         MSG_ASSOC,
         __MSG_MAX,
     };
     static struct blobmsg_policy policy[__MSG_MAX] = {
-        [MSG_ASSOC] = { "assoc", BLOBMSG_TYPE_BOOL },
+        [MSG_ASSOC] = {"assoc", BLOBMSG_TYPE_BOOL},
     };
     struct blob_attr *tb[__MSG_MAX];
     struct usteer_remote_node *rn;
     struct sta_info *remote_si;
 
     blobmsg_parse(policy, __MSG_MAX, tb, blobmsg_data(data),
blobmsg_data_len(data));
-    if (tb[MSG_ASSOC] && blobmsg_get_u8(tb[MSG_ASSOC])) {
-        if (si->connected == STA_NOT_CONNECTED) {
+    if (tb[MSG_ASSOC] && blobmsg_get_u8(tb[MSG_ASSOC]))
+    {
+        if (si->connected == STA_NOT_CONNECTED)
+        {
             /* New connection. Check if STA roamed. */
-            for_each_remote_node(rn) {
+            for_each_remote_node(rn)
+            {
                 remote_si = usteer_sta_info_get(si->sta, &rn->node, NULL);
                 if (!remote_si)
                     continue;
 
-                if (current_time - remote_si->last_connected <
config.roam_process_timeout) {
+                if (current_time - remote_si->last_connected <
config.roam_process_timeout)
+                {
                     rn->node.roam_events.source++;
                     /* Don't abort looking for roam sources here.
                      * The client might have roamed via another node
@@ -424,11 +447,13 @@ usteer_local_node_update_sta_rrm_wnm(struct
sta_info *si, struct blob_attr *clie
     if (!wnm_blob)
         return;
 
-    blobmsg_for_each_attr(cur, wnm_blob, rem) {
+    blobmsg_for_each_attr(cur, wnm_blob, rem)
+    {
         if (blobmsg_type(cur) != BLOBMSG_TYPE_INT32)
             return;
-        
-        if (i == 2) {
+
+        if (i == 2)
+        {
             if (blobmsg_get_u32(cur) & (1 << 3))
                 si->bss_transition = true;
         }
@@ -450,13 +475,15 @@ usteer_local_node_set_assoc(struct
usteer_local_node *ln, struct blob_attr *cl)
 
     usteer_update_time();
 
-    list_for_each_entry(si, &node->sta_info, node_list) {
+    list_for_each_entry(si, &node->sta_info, node_list)
+    {
         if (si->connected)
             si->connected = STA_DISCONNECTED;
     }
 
-    blobmsg_for_each_attr(cur, cl, rem) {
-        uint8_t *addr = (uint8_t *) ether_aton(blobmsg_name(cur));
+    blobmsg_for_each_attr(cur, cl, rem)
+    {
+        uint8_t *addr = (uint8_t *)ether_aton(blobmsg_name(cur));
         bool create;
 
         if (!addr)
@@ -464,14 +491,16 @@ usteer_local_node_set_assoc(struct
usteer_local_node *ln, struct blob_attr *cl)
 
         sta = usteer_sta_get(addr, true);
         si = usteer_sta_info_get(sta, node, &create);
-        list_for_each_entry(h, &node_handlers, list) {
+        list_for_each_entry(h, &node_handlers, list)
+        {
             if (!h->update_sta)
                 continue;
 
             h->update_sta(node, si);
         }
         usteer_local_node_assoc_update(si, cur);
-        if (si->connected == STA_CONNECTED) {
+        if (si->connected == STA_CONNECTED)
+        {
             si->last_connected = current_time;
             n_assoc++;
         }
@@ -482,12 +511,13 @@ usteer_local_node_set_assoc(struct
usteer_local_node *ln, struct blob_attr *cl)
 
     node->n_assoc = n_assoc;
 
-    list_for_each_entry(si, &node->sta_info, node_list) {
+    list_for_each_entry(si, &node->sta_info, node_list)
+    {
         if (si->connected != STA_DISCONNECTED)
             continue;
 
         usteer_sta_disconnected(si);
-        MSG(VERBOSE, "station "MAC_ADDR_FMT" disconnected from node %s\n",
+        MSG(VERBOSE, "station " MAC_ADDR_FMT " disconnected from node
%s\n",
             MAC_ADDR_DATA(si->sta->addr), usteer_node_name(node));
     }
 }
@@ -495,14 +525,15 @@ usteer_local_node_set_assoc(struct
usteer_local_node *ln, struct blob_attr *cl)
 static void
 usteer_local_node_list_cb(struct ubus_request *req, int type, struct
blob_attr *msg)
 {
-    enum {
+    enum
+    {
         MSG_FREQ,
         MSG_CLIENTS,
         __MSG_MAX,
     };
     static struct blobmsg_policy policy[__MSG_MAX] = {
-        [MSG_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
-        [MSG_CLIENTS] = { "clients", BLOBMSG_TYPE_TABLE },
+        [MSG_FREQ] = {"freq", BLOBMSG_TYPE_INT32},
+        [MSG_CLIENTS] = {"clients", BLOBMSG_TYPE_TABLE},
     };
     struct blob_attr *tb[__MSG_MAX];
     struct usteer_local_node *ln;
@@ -522,7 +553,8 @@ usteer_local_node_list_cb(struct ubus_request *req,
int type, struct blob_attr *
 static void
 usteer_local_node_status_cb(struct ubus_request *req, int type, struct
blob_attr *msg)
 {
-    enum {
+    enum
+    {
         MSG_FREQ,
         MSG_CHANNEL,
         MSG_OP_CLASS,
@@ -530,10 +562,10 @@ usteer_local_node_status_cb(struct ubus_request
*req, int type, struct blob_attr
         __MSG_MAX,
     };
     static struct blobmsg_policy policy[__MSG_MAX] = {
-        [MSG_FREQ] = { "freq", BLOBMSG_TYPE_INT32 },
-        [MSG_CHANNEL] = { "channel", BLOBMSG_TYPE_INT32 },
-        [MSG_OP_CLASS] = { "op_class", BLOBMSG_TYPE_INT32 },
-        [MSG_BEACON_INTERVAL] = { "beacon_interval", BLOBMSG_TYPE_INT32 },
+        [MSG_FREQ] = {"freq", BLOBMSG_TYPE_INT32},
+        [MSG_CHANNEL] = {"channel", BLOBMSG_TYPE_INT32},
+        [MSG_OP_CLASS] = {"op_class", BLOBMSG_TYPE_INT32},
+        [MSG_BEACON_INTERVAL] = {"beacon_interval", BLOBMSG_TYPE_INT32},
     };
     struct blob_attr *tb[__MSG_MAX];
     struct usteer_local_node *ln;
@@ -548,7 +580,7 @@ usteer_local_node_status_cb(struct ubus_request
*req, int type, struct blob_attr
     if (tb[MSG_CHANNEL])
         node->channel = blobmsg_get_u32(tb[MSG_CHANNEL]);
     if (tb[MSG_OP_CLASS])
-        node->op_class = blobmsg_get_u32(tb[MSG_OP_CLASS]);    
+        node->op_class = blobmsg_get_u32(tb[MSG_OP_CLASS]);
 
     /* Local-Node */
     if (tb[MSG_BEACON_INTERVAL])
@@ -559,8 +591,7 @@ static void
 usteer_local_node_rrm_nr_cb(struct ubus_request *req, int type, struct
blob_attr *msg)
 {
     static const struct blobmsg_policy policy = {
-        "value", BLOBMSG_TYPE_ARRAY
-    };
+        "value", BLOBMSG_TYPE_ARRAY};
     struct usteer_local_node *ln;
     struct blob_attr *tb;
 
@@ -596,8 +627,8 @@ usteer_add_rrm_data(struct usteer_local_node *ln,
struct usteer_node *node)
         return false;
 
     blobmsg_add_field(&b, BLOBMSG_TYPE_ARRAY, "",
-              blobmsg_data(node->rrm_nr),
-              blobmsg_data_len(node->rrm_nr));
+                      blobmsg_data(node->rrm_nr),
+                      blobmsg_data_len(node->rrm_nr));
 
     return true;
 }
@@ -610,16 +641,19 @@ usteer_local_node_prepare_rrm_set(struct
usteer_local_node *ln)
     void *c;
 
     c = blobmsg_open_array(&b, "list");
-    for_each_local_node(node) {
+    for_each_local_node(node)
+    {
         if (i >= config.max_neighbor_reports)
             break;
         if (usteer_add_rrm_data(ln, node))
             i++;
     }
 
-    while (i < config.max_neighbor_reports) {
+    while (i < config.max_neighbor_reports)
+    {
         node = usteer_node_get_next_neighbor(&ln->node,
last_remote_neighbor);
-        if (!node) {
+        if (!node)
+        {
             /* No more nodes available */
             break;
         }
@@ -628,7 +662,7 @@ usteer_local_node_prepare_rrm_set(struct
usteer_local_node *ln)
         if (usteer_add_rrm_data(ln, node))
             i++;
     }
-        
+
     blobmsg_close_array(&b, c);
 }
 
@@ -640,13 +674,15 @@ usteer_local_node_state_next(struct uloop_timeout
*timeout)
     ln = container_of(timeout, struct usteer_local_node, req_timer);
 
     ln->req_state++;
-    if (ln->req_state >= __REQ_MAX) {
+    if (ln->req_state >= __REQ_MAX)
+    {
         ln->req_state = REQ_IDLE;
         return;
     }
 
     blob_buf_init(&b, 0);
-    switch (ln->req_state) {
+    switch (ln->req_state)
+    {
     case REQ_CLIENTS:
         ubus_invoke_async(ubus_ctx, ln->obj_id, "get_clients", b.head,
&ln->req);
         ln->req.data_cb = usteer_local_node_list_cb;
@@ -680,17 +716,19 @@ usteer_local_node_request_link_measurement(struct
usteer_local_node *ln)
 
     node = &ln->node;
 
-    if (ln->link_measurement_tries < min_count) {
+    if (ln->link_measurement_tries < min_count)
+    {
         ln->link_measurement_tries++;
         return;
     }
-    
+
     ln->link_measurement_tries = 0;
 
     if (!config.link_measurement_interval)
         return;
 
-    list_for_each_entry(si, &node->sta_info, node_list) {
+    list_for_each_entry(si, &node->sta_info, node_list)
+    {
         if (si->connected != STA_CONNECTED)
             continue;
 
@@ -708,7 +746,8 @@ usteer_local_node_update(struct uloop_timeout *timeout)
     ln = container_of(timeout, struct usteer_local_node, update);
     node = &ln->node;
 
-    list_for_each_entry(h, &node_handlers, list) {
+    list_for_each_entry(h, &node_handlers, list)
+    {
         if (!h->update_node)
             continue;
 
@@ -739,7 +778,8 @@ usteer_local_node_process_bss_tm_queries(struct
uloop_timeout *timeout)
 
     validity_period = 10000 /
usteer_local_node_get_beacon_interval(ln); /* ~ 10 seconds */
 
-    list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list) {
+    list_for_each_entry_safe(query, tmp, &ln->bss_tm_queries, list)
+    {
         sta = usteer_sta_get(query->sta_addr, false);
         if (!sta)
             continue;
@@ -748,7 +788,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,
config.aggressive_all, validity_period, true, validity_period, NULL);
     }
 
     /* Free pending queries we can not handle */
@@ -808,7 +848,8 @@ usteer_check_node_enabled(struct usteer_local_node *ln)
     struct blob_attr *cur;
     int rem;
 
-    blobmsg_for_each_attr(cur, config.ssid_list, rem) {
+    blobmsg_for_each_attr(cur, config.ssid_list, rem)
+    {
         if (strcmp(blobmsg_get_string(cur), ln->node.ssid) != 0)
             continue;
 
@@ -821,7 +862,8 @@ usteer_check_node_enabled(struct usteer_local_node *ln)
 
     ln->node.disabled = ssid_disabled;
 
-    if (ssid_disabled) {
+    if (ssid_disabled)
+    {
         MSG(INFO, "Disconnecting from local node %s\n",
usteer_node_name(&ln->node));
         usteer_local_node_state_reset(ln);
         usteer_sta_node_cleanup(&ln->node);
@@ -866,7 +908,8 @@ usteer_register_node(struct ubus_context *ctx, const
char *name, uint32_t id)
     blobmsg_add_u8(&b, "bss_transition", 1);
     ubus_invoke(ctx, id, "bss_mgmt_enable", b.head, NULL, NULL, 1000);
 
-    list_for_each_entry(h, &node_handlers, list) {
+    list_for_each_entry(h, &node_handlers, list)
+    {
         if (!h->init_node)
             continue;
 
@@ -879,11 +922,11 @@ usteer_register_node(struct ubus_context *ctx,
const char *name, uint32_t id)
 
 static void
 usteer_event_handler(struct ubus_context *ctx, struct
ubus_event_handler *ev,
-            const char *type, struct blob_attr *msg)
+                     const char *type, struct blob_attr *msg)
 {
     static const struct blobmsg_policy policy[2] = {
-        { .name = "id", .type = BLOBMSG_TYPE_INT32 },
-        { .name = "path", .type = BLOBMSG_TYPE_STRING },
+        {.name = "id", .type = BLOBMSG_TYPE_INT32},
+        {.name = "path", .type = BLOBMSG_TYPE_STRING},
     };
     struct blob_attr *tb[2];
     const char *path;
@@ -901,8 +944,7 @@ static void
 usteer_register_events(struct ubus_context *ctx)
 {
     static struct ubus_event_handler handler = {
-        .cb = usteer_event_handler
-    };
+        .cb = usteer_event_handler};
 
     ubus_register_event_handler(ctx, &handler, "ubus.object.add");
 }
@@ -913,8 +955,7 @@ node_list_cb(struct ubus_context *ctx, struct
ubus_object_data *obj, void *priv)
     usteer_register_node(ctx, obj->path, obj->id);
 }
 
-int
-usteer_local_node_get_beacon_interval(struct usteer_local_node *ln)
+int usteer_local_node_get_beacon_interval(struct usteer_local_node *ln)
 {
     /* Check if beacon-interval is not available (pre-21.02+) */
     if (ln->beacon_interval < 1)
@@ -937,7 +978,8 @@ void config_set_node_up_script(struct blob_attr *data)
 
     free(node_up_script);
 
-    if (!strlen(val)) {
+    if (!strlen(val))
+    {
         node_up_script = NULL;
         return;
     }
@@ -977,8 +1019,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_aggressive_mac_list(struct blob_attr *data)
+{
+    free(config.aggressive_mac_list);
+
+    if (data && blobmsg_len(data))
+        config.aggressive_mac_list = blob_memdup(data);
+    else
+        config.aggressive_mac_list = NULL;
+}
+
+void config_get_aggressive_mac_list(struct blob_buf *buf)
+{
+    if (config.aggressive_mac_list)
+        blobmsg_add_blob(buf, config.aggressive_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..96386ca 100644
--- a/main.c
+++ b/main.c
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #include <unistd.h>
@@ -35,7 +35,7 @@ static int dump_time;
 
 LIST_HEAD(node_handlers);
 
-const char * const event_types[__EVENT_TYPE_MAX] = {
+const char *const event_types[__EVENT_TYPE_MAX] = {
     [EVENT_TYPE_PROBE] = "probe",
     [EVENT_TYPE_AUTH] = "auth",
     [EVENT_TYPE_ASSOC] = "assoc",
@@ -65,7 +65,6 @@ void debug_msg(int level, const char *func, int line,
const char *format, ...)
     else
         vfprintf(stderr, format, ap);
     va_end(ap);
-
 }
 
 void debug_msg_cont(int level, const char *format, ...)
@@ -96,6 +95,8 @@ void usteer_init_defaults(void)
     config.remote_update_interval = 1000;
     config.initial_connect_delay = 0;
     config.remote_node_timeout = 10;
+    config.aggressive_all = false;
+    config.aggressive_disassoc_timer = 100;
 
     config.steer_reject_timeout = 60000;
 
@@ -127,26 +128,26 @@ void usteer_init_defaults(void)
 void usteer_update_time(void)
 {
     struct timespec ts;
-
     clock_gettime(CLOCK_MONOTONIC, &ts);
-    current_time = (uint64_t) ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+    current_time = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
 }
 
 static int usage(const char *prog)
 {
     fprintf(stderr, "Usage: %s [options]\n"
-        "Options:\n"
-        " -v:           Increase debug level (repeat for more messages):\n"
-        "               1: info messages\n"
-        "               2: debug messages\n"
-        "               3: verbose debug messages\n"
-        "               4: include network messages\n"
-        "               5: include extra testing messages\n"
-        " -i <name>:    Connect to other instances on interface <name>\n"
-        " -s:        Output log messages via syslog instead of stderr\n"
-        " -D <n>:    Do not daemonize, wait for <n> seconds and print\n"
-        "        remote hosts and nodes\n"
-        "\n", prog);
+                    "Options:\n"
+                    " -v:           Increase debug level (repeat for
more messages):\n"
+                    "               1: info messages\n"
+                    "               2: debug messages\n"
+                    "               3: verbose debug messages\n"
+                    "               4: include network messages\n"
+                    "               5: include extra testing messages\n"
+                    " -i <name>:    Connect to other instances on
interface <name>\n"
+                    " -s:        Output log messages via syslog instead
of stderr\n"
+                    " -D <n>:    Do not daemonize, wait for <n> seconds
and print\n"
+                    "        remote hosts and nodes\n"
+                    "\n",
+            prog);
     return 1;
 }
 
@@ -187,8 +188,10 @@ int main(int argc, char **argv)
 
     usteer_init_defaults();
 
-    while ((ch = getopt(argc, argv, "D:i:sv")) != -1) {
-        switch(ch) {
+    while ((ch = getopt(argc, argv, "D:i:sv")) != -1)
+    {
+        switch (ch)
+        {
         case 'v':
             config.debug_level++;
             break;
@@ -213,19 +216,24 @@ int main(int argc, char **argv)
     uloop_init();
 
     ubus_ctx = ubus_connect(NULL);
-    if (!ubus_ctx) {
+    if (!ubus_ctx)
+    {
         fprintf(stderr, "Failed to connect to ubus\n");
         return -1;
     }
 
     ubus_add_uloop(ubus_ctx);
-    if (dump_time) {
+    if (dump_time)
+    {
         dump_timer.cb = usteer_dump_timeout;
         uloop_timeout_set(&dump_timer, dump_time * 1000);
-    } else {
+    }
+    else
+    {
         usteer_ubus_init(ubus_ctx);
         usteer_local_nodes_init(ubus_ctx);
     }
+
     uloop_run();
 
     uloop_done();
diff --git a/openwrt/usteer/files/etc/config/usteer
b/openwrt/usteer/files/etc/config/usteer
index f53c338..2fe47f3 100644
--- a/openwrt/usteer/files/etc/config/usteer
+++ b/openwrt/usteer/files/etc/config/usteer
@@ -71,6 +71,15 @@ config usteer
     # Timeout (ms) for which a client will not be steered after
rejecting a BSS-transition-request
     #option steer_reject_timeout 60000
 
+    # Use aggressvice roaming to push clients to another AP
+    #option aggressive_all 0
+
+    # List of MACs to enable aggressive roaming on. If not set all macs
will handled aggressive
+    #list aggressive_mac_list ''
+
+    # Disassociation imminent tuner - in aggresive mode the time a
client has to roam away before disconnected hardly
+    #option aggressive_disassoc_timer 100
+
     # 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..fdc8211 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" aggressive_all
     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" aggressive_mac_list
     uci_option_to_json_string_array "$cfg" event_log_types
 
     for opt in \
@@ -84,7 +86,7 @@ uci_usteer() {
         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 \
+        roam_trigger_snr roam_trigger_interval aggressive_disassoc_timer\
         band_steering_interval band_steering_min_snr
link_measurement_interval \
         load_kick_threshold load_kick_delay load_kick_min_clients \
         load_kick_reason_code
diff --git a/policy.c b/policy.c
index 8c5d244..7085114 100644
--- a/policy.c
+++ b/policy.c
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #include "usteer.h"
@@ -45,8 +45,7 @@ below_assoc_threshold(struct usteer_node *node_cur,
struct usteer_node *node_new
 static bool
 better_signal_strength(int signal_cur, int signal_new)
 {
-    const bool is_better = signal_new - signal_cur
-                > (int) config.signal_diff_threshold;
+    const bool is_better = signal_new - signal_cur >
(int)config.signal_diff_threshold;
 
     if (!config.signal_diff_threshold)
         return false;
@@ -58,7 +57,7 @@ static bool
 below_load_threshold(struct usteer_node *node)
 {
     return node->n_assoc >= config.load_kick_min_clients &&
-           node->load > config.load_kick_threshold;
+           node->load > config.load_kick_threshold;
 }
 
 static bool
@@ -67,8 +66,7 @@ has_better_load(struct usteer_node *node_cur, struct
usteer_node *node_new)
     return !below_load_threshold(node_cur) &&
below_load_threshold(node_new);
 }
 
-bool
-usteer_policy_node_below_max_assoc(struct usteer_node *node)
+bool usteer_policy_node_below_max_assoc(struct usteer_node *node)
 {
     return !node->max_assoc || node->n_assoc < node->max_assoc;
 }
@@ -81,7 +79,7 @@ over_min_signal(struct usteer_node *node, int signal)
 
     if (config.roam_trigger_snr && signal < usteer_snr_to_signal(node,
config.roam_trigger_snr))
         return false;
-    
+
     return true;
 }
 
@@ -101,7 +99,7 @@ is_better_candidate(struct sta_info *si_cur, struct
sta_info *si_new)
         return 0;
 
     if (below_assoc_threshold(current_node, new_node) &&
-        !below_assoc_threshold(new_node, current_node))
+        !below_assoc_threshold(new_node, current_node))
         reasons |= (1 << UEV_SELECT_REASON_NUM_ASSOC);
 
     if (better_signal_strength(current_signal, new_signal))
@@ -121,7 +119,8 @@ find_better_candidate(struct sta_info *si_ref,
struct uevent *ev, uint32_t requi
     struct sta *sta = si_ref->sta;
     uint32_t reasons;
 
-    list_for_each_entry(si, &sta->nodes, list) {
+    list_for_each_entry(si, &sta->nodes, list)
+    {
         if (si == si_ref)
             continue;
 
@@ -141,7 +140,8 @@ find_better_candidate(struct sta_info *si_ref,
struct uevent *ev, uint32_t requi
         if (!(reasons & required_criteria))
             continue;
 
-        if (ev) {
+        if (ev)
+        {
             ev->si_other = si;
             ev->select_reasons = reasons;
         }
@@ -153,8 +153,7 @@ find_better_candidate(struct sta_info *si_ref,
struct uevent *ev, uint32_t requi
     return candidate;
 }
 
-int
-usteer_snr_to_signal(struct usteer_node *node, int snr)
+int usteer_snr_to_signal(struct usteer_node *node, int snr)
 {
     int noise = -95;
 
@@ -167,8 +166,7 @@ usteer_snr_to_signal(struct usteer_node *node, int snr)
     return noise + snr;
 }
 
-bool
-usteer_check_request(struct sta_info *si, enum usteer_event_type type)
+bool usteer_check_request(struct sta_info *si, enum usteer_event_type type)
 {
     struct uevent ev = {
         .si_cur = si,
@@ -182,25 +180,30 @@ usteer_check_request(struct sta_info *si, enum
usteer_event_type type)
     if (type == EVENT_TYPE_AUTH)
         goto out;
 
-    if (type == EVENT_TYPE_ASSOC) {
+    if (type == EVENT_TYPE_ASSOC)
+    {
         /* Check if assoc request has lower signal than min_signal.
          * If this is the case, block assoc even when assoc steering is
enabled.
          *
          * Otherwise, the client potentially ends up in a assoc - kick
loop.
          */
-        if (config.min_snr && si->signal <
usteer_snr_to_signal(si->node, config.min_snr)) {
+        if (config.min_snr && si->signal <
usteer_snr_to_signal(si->node, config.min_snr))
+        {
             ev.reason = UEV_REASON_LOW_SIGNAL;
             ev.threshold.cur = si->signal;
             ev.threshold.ref = usteer_snr_to_signal(si->node,
config.min_snr);
             ret = false;
             goto out;
-        } else if (!config.assoc_steering) {
+        }
+        else if (!config.assoc_steering)
+        {
             goto out;
         }
     }
 
     min_signal = usteer_snr_to_signal(si->node, config.min_connect_snr);
-    if (si->signal < min_signal) {
+    if (si->signal < min_signal)
+    {
         ev.reason = UEV_REASON_LOW_SIGNAL;
         ev.threshold.cur = si->signal;
         ev.threshold.ref = min_signal;
@@ -208,7 +211,8 @@ usteer_check_request(struct sta_info *si, enum
usteer_event_type type)
         goto out;
     }
 
-    if (current_time - si->created < config.initial_connect_delay) {
+    if (current_time - si->created < config.initial_connect_delay)
+    {
         ev.reason = UEV_REASON_CONNECT_DELAY;
         ev.threshold.cur = current_time - si->created;
         ev.threshold.ref = config.initial_connect_delay;
@@ -224,7 +228,8 @@ usteer_check_request(struct sta_info *si, enum
usteer_event_type type)
     ret = false;
 
 out:
-    switch (type) {
+    switch (type)
+    {
     case EVENT_TYPE_PROBE:
         ev.type = ret ? UEV_PROBE_REQ_ACCEPT : UEV_PROBE_REQ_DENY;
         break;
@@ -238,7 +243,8 @@ out:
         break;
     }
 
-    if (!ret && si->stats[type].blocked_cur >= config.max_retry_band) {
+    if (!ret && si->stats[type].blocked_cur >= config.max_retry_band)
+    {
         ev.reason = UEV_REASON_RETRY_EXCEEDED;
         ev.threshold.cur = si->stats[type].blocked_cur;
         ev.threshold.ref = config.max_retry_band;
@@ -262,19 +268,23 @@ is_more_kickable(struct sta_info *si_cur, struct
sta_info *si_new)
 
 static void
 usteer_roam_set_state(struct sta_info *si, enum roam_trigger_state state,
-              struct uevent *ev)
+                      struct uevent *ev)
 {
     /* NOP in case we remain idle */
-    if (si->roam_state == state && si->roam_state == ROAM_TRIGGER_IDLE) {
+    if (si->roam_state == state && si->roam_state == ROAM_TRIGGER_IDLE)
+    {
         si->roam_tries = 0;
         return;
     }
 
     si->roam_event = current_time;
 
-    if (si->roam_state == state) {
+    if (si->roam_state == state)
+    {
         si->roam_tries++;
-    } else {
+    }
+    else
+    {
         si->roam_tries = 0;
     }
 
@@ -287,7 +297,8 @@ usteer_roam_sm_start_scan(struct sta_info *si,
struct uevent *ev)
 {
     /* Start scanning in case we are not timeout-constrained or timeout
has expired */
     if (!config.roam_scan_timeout ||
-        current_time > si->roam_scan_timeout_start +
config.roam_scan_timeout) {
+        current_time > si->roam_scan_timeout_start +
config.roam_scan_timeout)
+    {
         usteer_roam_set_state(si, ROAM_TRIGGER_SCAN, ev);
         return;
     }
@@ -326,9 +337,11 @@ usteer_roam_trigger_sm(struct usteer_local_node
*ln, struct sta_info *si)
         .si_cur = si,
     };
 
-    switch (si->roam_state) {
+    switch (si->roam_state)
+    {
     case ROAM_TRIGGER_SCAN:
-        if (!si->roam_tries) {
+        if (!si->roam_tries)
+        {
             si->roam_scan_start = current_time;
         }
 
@@ -341,11 +354,15 @@ usteer_roam_trigger_sm(struct usteer_local_node
*ln, struct sta_info *si)
             break;
 
         /* 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) {
+        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 {
+            }
+            else
+            {
                 /* Kick in scan timeout */
                 si->roam_scan_timeout_start = current_time;
                 usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev);
@@ -365,13 +382,24 @@ usteer_roam_trigger_sm(struct usteer_local_node
*ln, struct sta_info *si)
     case ROAM_TRIGGER_SCAN_DONE:
         candidate = usteer_roam_sm_found_better_node(si, &ev,
ROAM_TRIGGER_SCAN_DONE);
         /* Kick back in case no better node is found */
-        if (!candidate) {
+        if (!candidate)
+        {
             usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev);
             break;
         }
 
-        usteer_ubus_bss_transition_request(si, 1, false, false, 100,
candidate->node);
-        si->kick_time = current_time + config.roam_kick_delay;
+        if (si->sta->aggressive)
+        {
+            // TODO: Disaccociation Timer noch konfigurierbar machen
+            usteer_ubus_bss_transition_request(si, 1, true,
config.aggressive_disassoc_timer, true,
config.aggressive_disassoc_timer, candidate->node);
+            si->roam_disassoc_time = current_time + (100 * 100);
+        }
+        else
+        {
+            usteer_ubus_bss_transition_request(si, 1, false, 0, true,
100, candidate->node);
+            si->kick_time = current_time + config.roam_kick_delay;
+        }
+
         usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, &ev);
         break;
     }
@@ -400,7 +428,11 @@ 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;
-    
+
+    /* Skip on aggressive roaming in progress - wait 10s after
disassociation event*/
+    if (current_time - si->roam_disassoc_time < 10000)
+        return false;
+
     return true;
 }
 
@@ -433,8 +465,10 @@ usteer_local_node_roam_check(struct
usteer_local_node *ln, struct uevent *ev)
     usteer_update_time();
     min_signal = usteer_snr_to_signal(&ln->node, min_signal);
 
-    list_for_each_entry(si, &ln->node.sta_info, node_list) {
-        if (!usteer_local_node_roam_sm_active(si, min_signal)) {
+    list_for_each_entry(si, &ln->node.sta_info, node_list)
+    {
+        if (!usteer_local_node_roam_sm_active(si, min_signal))
+        {
             usteer_roam_set_state(si, ROAM_TRIGGER_IDLE, ev);
             continue;
         }
@@ -464,14 +498,18 @@ usteer_local_node_snr_kick(struct
usteer_local_node *ln)
     min_signal = usteer_snr_to_signal(&ln->node, config.min_snr);
     ev.threshold.ref = min_signal;
 
-    list_for_each_entry(si, &ln->node.sta_info, node_list) {
+    list_for_each_entry(si, &ln->node.sta_info, node_list)
+    {
         if (si->connected != STA_CONNECTED)
             continue;
 
-        if (si->signal >= min_signal) {
+        if (si->signal >= min_signal)
+        {
             si->below_min_snr = 0;
             continue;
-        } else {
+        }
+        else
+        {
             si->below_min_snr++;
         }
 
@@ -501,10 +539,11 @@ usteer_local_node_load_kick(struct
usteer_local_node *ln)
     unsigned int min_count = DIV_ROUND_UP(config.load_kick_delay,
config.local_sta_update);
 
     if (!config.load_kick_enabled || !config.load_kick_threshold ||
-        !config.load_kick_delay)
+        !config.load_kick_delay)
         return;
 
-    if (node->load < config.load_kick_threshold) {
+    if (node->load < config.load_kick_threshold)
+    {
         if (!ln->load_thr_count)
             return;
 
@@ -515,7 +554,8 @@ usteer_local_node_load_kick(struct usteer_local_node
*ln)
         goto out;
     }
 
-    if (++ln->load_thr_count <= min_count) {
+    if (++ln->load_thr_count <= min_count)
+    {
         if (ln->load_thr_count > 1)
             return;
 
@@ -526,14 +566,16 @@ usteer_local_node_load_kick(struct
usteer_local_node *ln)
     }
 
     ln->load_thr_count = 0;
-    if (node->n_assoc < config.load_kick_min_clients) {
+    if (node->n_assoc < config.load_kick_min_clients)
+    {
         ev.type = UEV_LOAD_KICK_MIN_CLIENTS;
         ev.threshold.cur = node->n_assoc;
         ev.threshold.ref = config.load_kick_min_clients;
         goto out;
     }
 
-    list_for_each_entry(si, &ln->node.sta_info, node_list) {
+    list_for_each_entry(si, &ln->node.sta_info, node_list)
+    {
         struct sta_info *tmp;
 
         if (si->connected != STA_CONNECTED)
@@ -546,13 +588,15 @@ usteer_local_node_load_kick(struct
usteer_local_node *ln)
         if (!tmp)
             continue;
 
-        if (is_more_kickable(kick2, si)) {
+        if (is_more_kickable(kick2, si))
+        {
             kick2 = si;
             candidate = tmp;
         }
     }
 
-    if (!kick1) {
+    if (!kick1)
+    {
         ev.type = UEV_LOAD_KICK_NO_CLIENT;
         goto out;
     }
@@ -578,7 +622,8 @@ usteer_local_node_perform_kick(struct
usteer_local_node *ln)
 {
     struct sta_info *si;
 
-    list_for_each_entry(si, &ln->node.sta_info, node_list) {
+    list_for_each_entry(si, &ln->node.sta_info, node_list)
+    {
         if (!si->kick_time || si->kick_time > current_time)
             continue;
 
@@ -586,8 +631,7 @@ usteer_local_node_perform_kick(struct
usteer_local_node *ln)
     }
 }
 
-void
-usteer_local_node_kick(struct usteer_local_node *ln)
+void usteer_local_node_kick(struct usteer_local_node *ln)
 {
     struct uevent ev = {
         .node_local = &ln->node,
diff --git a/sta.c b/sta.c
index ed7e40e..42c0576 100644
--- a/sta.c
+++ b/sta.c
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #include "usteer.h"
@@ -28,24 +28,22 @@ avl_macaddr_cmp(const void *k1, const void *k2, void
*ptr)
 AVL_TREE(stations, avl_macaddr_cmp, false, NULL);
 static struct usteer_timeout_queue tq;
 
-static void
-usteer_sta_del(struct sta *sta)
+static void usteer_sta_del(struct sta *sta)
 {
     MSG(DEBUG, "Delete station " MAC_ADDR_FMT "\n",
-        MAC_ADDR_DATA(sta->addr));
+        MAC_ADDR_DATA(sta->addr));
 
     avl_delete(&stations, &sta->avl);
     usteer_measurement_report_sta_cleanup(sta);
     free(sta);
 }
 
-static void
-usteer_sta_info_del(struct sta_info *si)
+static void usteer_sta_info_del(struct sta_info *si)
 {
     struct sta *sta = si->sta;
 
     MSG(DEBUG, "Delete station " MAC_ADDR_FMT " entry for node %s\n",
-        MAC_ADDR_DATA(sta->addr), usteer_node_name(si->node));
+        MAC_ADDR_DATA(sta->addr), usteer_node_name(si->node));
 
     usteer_timeout_cancel(&tq, &si->timeout);
     list_del(&si->list);
@@ -56,8 +54,7 @@ usteer_sta_info_del(struct sta_info *si)
         usteer_sta_del(sta);
 }
 
-void
-usteer_sta_node_cleanup(struct usteer_node *node)
+void usteer_sta_node_cleanup(struct usteer_node *node)
 {
     struct sta_info *si, *tmp;
 
@@ -68,20 +65,42 @@ usteer_sta_node_cleanup(struct usteer_node *node)
         usteer_sta_info_del(si);
 }
 
-static void
-usteer_sta_info_timeout(struct usteer_timeout_queue *q, struct
usteer_timeout *t)
+static void usteer_sta_info_timeout(struct usteer_timeout_queue *q,
struct usteer_timeout *t)
 {
     struct sta_info *si = container_of(t, struct sta_info, timeout);
 
     usteer_sta_info_del(si);
 }
 
-struct sta_info *
-usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool
*create)
+static void usteer_sta_update_aggressive(struct sta *sta)
+{
+    struct blob_attr *cur;
+    int rem;
+    char sta_mac[18];
+    sprintf(sta_mac, MAC_ADDR_FMT, MAC_ADDR_DATA(sta->addr));
+
+    if (config.aggressive_all)
+        sta->aggressive = true;
+    else
+    {
+        sta->aggressive = false;
+        blobmsg_for_each_attr(cur, config.aggressive_mac_list, rem)
+        {
+            if (strcmp(blobmsg_get_string(cur), sta_mac) != 0)
+                continue;
+
+            sta->aggressive = true;
+            break;
+        }
+    }
+}
+
+struct sta_info *usteer_sta_info_get(struct sta *sta, struct
usteer_node *node, bool *create)
 {
     struct sta_info *si;
 
-    list_for_each_entry(si, &sta->nodes, list) {
+    list_for_each_entry(si, &sta->nodes, list)
+    {
         if (si->node != node)
             continue;
 
@@ -95,7 +114,7 @@ usteer_sta_info_get(struct sta *sta, struct
usteer_node *node, bool *create)
         return NULL;
 
     MSG(DEBUG, "Create station " MAC_ADDR_FMT " entry for node %s\n",
-        MAC_ADDR_DATA(sta->addr), usteer_node_name(node));
+        MAC_ADDR_DATA(sta->addr), usteer_node_name(node));
 
     si = calloc(1, sizeof(*si));
     si->node = node;
@@ -105,15 +124,15 @@ usteer_sta_info_get(struct sta *sta, struct
usteer_node *node, bool *create)
     si->created = current_time;
     *create = true;
 
+    usteer_sta_update_aggressive(sta);
+
     /* Node is by default not connected. */
     usteer_sta_disconnected(si);
 
     return si;
 }
 
-
-void
-usteer_sta_info_update_timeout(struct sta_info *si, int timeout)
+void usteer_sta_info_update_timeout(struct sta_info *si, int timeout)
 {
     if (si->connected == STA_CONNECTED)
         usteer_timeout_cancel(&tq, &si->timeout);
@@ -123,8 +142,7 @@ usteer_sta_info_update_timeout(struct sta_info *si,
int timeout)
         usteer_sta_info_del(si);
 }
 
-struct sta *
-usteer_sta_get(const uint8_t *addr, bool create)
+struct sta *usteer_sta_get(const uint8_t *addr, bool create)
 {
     struct sta *sta;
 
@@ -154,14 +172,14 @@ void usteer_sta_disconnected(struct sta_info *si)
     usteer_sta_info_update_timeout(si, config.local_sta_timeout);
 }
 
-void
-usteer_sta_info_update(struct sta_info *si, int signal, bool avg)
+void usteer_sta_info_update(struct sta_info *si, int signal, bool avg)
 {
     /* ignore probe request signal when connected */
     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);
     }
@@ -176,9 +194,8 @@ usteer_sta_info_update(struct sta_info *si, int
signal, bool avg)
     usteer_sta_info_update_timeout(si, config.local_sta_timeout);
 }
 
-bool
-usteer_handle_sta_event(struct usteer_node *node, const uint8_t *addr,
-               enum usteer_event_type type, int freq, int signal)
+bool usteer_handle_sta_event(struct usteer_node *node, const uint8_t *addr,
+                             enum usteer_event_type type, int freq, int
signal)
 {
     struct sta *sta;
     struct sta_info *si;
@@ -199,11 +216,14 @@ usteer_handle_sta_event(struct usteer_node *node,
const uint8_t *addr,
         si->stats[type].blocked_cur = 0;
 
     ret = usteer_check_request(si, type);
-    if (!ret) {
+    if (!ret)
+    {
         si->stats[type].blocked_cur++;
         si->stats[type].blocked_total++;
         si->stats[type].blocked_last_time = current_time;
-    } else {
+    }
+    else
+    {
         si->stats[type].blocked_cur = 0;
     }
 
@@ -213,23 +233,22 @@ usteer_handle_sta_event(struct usteer_node *node,
const uint8_t *addr,
     return ret;
 }
 
-bool
-usteer_sta_supports_beacon_measurement_mode(struct sta_info *si, enum
usteer_beacon_measurement_mode mode)
+bool usteer_sta_supports_beacon_measurement_mode(struct sta_info *si,
enum usteer_beacon_measurement_mode mode)
 {
-    switch (mode) {
-        case BEACON_MEASUREMENT_PASSIVE:
-            return si->rrm & (1 << 4);
-        case BEACON_MEASUREMENT_ACTIVE:
-            return si->rrm & (1 << 5);
-        case BEACON_MEASUREMENT_TABLE:
-            return si->rrm & (1 << 6);
+    switch (mode)
+    {
+    case BEACON_MEASUREMENT_PASSIVE:
+        return si->rrm & (1 << 4);
+    case BEACON_MEASUREMENT_ACTIVE:
+        return si->rrm & (1 << 5);
+    case BEACON_MEASUREMENT_TABLE:
+        return si->rrm & (1 << 6);
     }
 
     return false;
 }
 
-bool
-usteer_sta_supports_link_measurement(struct sta_info *si)
+bool usteer_sta_supports_link_measurement(struct sta_info *si)
 {
     return si->rrm & (1 << 0);
 }
diff --git a/ubus.c b/ubus.c
index 40daf74..066a3c3 100644
--- a/ubus.c
+++ b/ubus.c
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #include <sys/types.h>
@@ -41,17 +41,19 @@ blobmsg_open_table_mac(struct blob_buf *buf, uint8_t
*addr)
 
 static int
 usteer_ubus_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
-               struct ubus_request_data *req, const char *method,
-               struct blob_attr *msg)
+                        struct ubus_request_data *req, const char *method,
+                        struct blob_attr *msg)
 {
     struct sta_info *si;
     struct sta *sta;
     void *_s, *_cur_n;
 
     blob_buf_init(&b, 0);
-    avl_for_each_element(&stations, sta, avl) {
+    avl_for_each_element(&stations, sta, avl)
+    {
         _s = blobmsg_open_table_mac(&b, sta->addr);
-        list_for_each_entry(si, &sta->nodes, list) {
+        list_for_each_entry(si, &sta->nodes, list)
+        {
             _cur_n = blobmsg_open_table(&b, usteer_node_name(si->node));
             blobmsg_add_u8(&b, "connected", si->connected);
             blobmsg_add_u32(&b, "signal", si->signal);
@@ -64,7 +66,10 @@ usteer_ubus_get_clients(struct ubus_context *ctx,
struct ubus_object *obj,
 }
 
 static struct blobmsg_policy client_arg[] = {
-    { .name = "address", .type = BLOBMSG_TYPE_STRING, },
+    {
+        .name = "address",
+        .type = BLOBMSG_TYPE_STRING,
+    },
 };
 
 static void
@@ -81,8 +86,8 @@ usteer_ubus_add_stats(struct sta_info_stats *stats,
const char *name)
 
 static int
 usteer_ubus_get_client_info(struct ubus_context *ctx, struct
ubus_object *obj,
-               struct ubus_request_data *req, const char *method,
-               struct blob_attr *msg)
+                            struct ubus_request_data *req, const char
*method,
+                            struct blob_attr *msg)
 {
     struct sta_info *si;
     struct sta *sta;
@@ -95,7 +100,7 @@ usteer_ubus_get_client_info(struct ubus_context *ctx,
struct ubus_object *obj,
     if (!mac_str)
         return UBUS_STATUS_INVALID_ARGUMENT;
 
-    mac = (uint8_t *) ether_aton(blobmsg_data(mac_str));
+    mac = (uint8_t *)ether_aton(blobmsg_data(mac_str));
     if (!mac)
         return UBUS_STATUS_INVALID_ARGUMENT;
 
@@ -107,7 +112,8 @@ usteer_ubus_get_client_info(struct ubus_context
*ctx, struct ubus_object *obj,
     blobmsg_add_u8(&b, "2ghz", sta->seen_2ghz);
     blobmsg_add_u8(&b, "5ghz", sta->seen_5ghz);
     _n = blobmsg_open_table(&b, "nodes");
-    list_for_each_entry(si, &sta->nodes, list) {
+    list_for_each_entry(si, &sta->nodes, list)
+    {
         _cur_n = blobmsg_open_table(&b, usteer_node_name(si->node));
         blobmsg_add_u8(&b, "connected", si->connected);
         blobmsg_add_u32(&b, "signal", si->signal);
@@ -124,7 +130,8 @@ usteer_ubus_get_client_info(struct ubus_context
*ctx, struct ubus_object *obj,
     return 0;
 }
 
-enum cfg_type {
+enum cfg_type
+{
     CFG_BOOL,
     CFG_I32,
     CFG_U32,
@@ -132,64 +139,71 @@ enum cfg_type {
     CFG_STRING_CB,
 };
 
-struct cfg_item {
+struct cfg_item
+{
     enum cfg_type type;
-    union {
+    union
+    {
         bool *BOOL;
         uint32_t *U32;
         int32_t *I32;
-        struct {
+        struct
+        {
             void (*set)(struct blob_attr *data);
             void (*get)(struct blob_buf *buf);
         } CB;
     } ptr;
 };
 
-#define __config_items \
-    _cfg(BOOL, syslog), \
-    _cfg(U32, debug_level), \
-    _cfg(BOOL, ipv6), \
-    _cfg(BOOL, local_mode), \
-    _cfg(U32, sta_block_timeout), \
-    _cfg(U32, local_sta_timeout), \
-    _cfg(U32, local_sta_update), \
-    _cfg(U32, max_neighbor_reports), \
-    _cfg(U32, max_retry_band), \
-    _cfg(U32, seen_policy_timeout), \
-    _cfg(U32, measurement_report_timeout), \
-    _cfg(U32, load_balancing_threshold), \
-    _cfg(U32, band_steering_threshold), \
-    _cfg(U32, remote_update_interval), \
-    _cfg(U32, remote_node_timeout), \
-    _cfg(BOOL, assoc_steering), \
-    _cfg(I32, min_connect_snr), \
-    _cfg(I32, min_snr), \
-    _cfg(U32, min_snr_kick_delay), \
-    _cfg(U32, steer_reject_timeout), \
-    _cfg(U32, roam_process_timeout), \
-    _cfg(I32, roam_scan_snr), \
-    _cfg(U32, roam_scan_tries), \
-    _cfg(U32, roam_scan_timeout), \
-    _cfg(U32, roam_scan_interval), \
-    _cfg(I32, roam_trigger_snr), \
-    _cfg(U32, roam_trigger_interval), \
-    _cfg(U32, roam_kick_delay), \
-    _cfg(U32, signal_diff_threshold), \
-    _cfg(U32, initial_connect_delay), \
-    _cfg(BOOL, load_kick_enabled), \
-    _cfg(U32, load_kick_threshold), \
-    _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(U32, link_measurement_interval), \
-    _cfg(ARRAY_CB, interfaces), \
-    _cfg(STRING_CB, node_up_script), \
-    _cfg(ARRAY_CB, event_log_types), \
-    _cfg(ARRAY_CB, ssid_list)
-
-enum cfg_items {
+#define __config_items                         \
+    _cfg(BOOL, syslog),                        \
+        _cfg(U32, debug_level),                \
+        _cfg(BOOL, ipv6),                      \
+        _cfg(BOOL, local_mode),                \
+        _cfg(U32, sta_block_timeout),          \
+        _cfg(U32, local_sta_timeout),          \
+        _cfg(U32, local_sta_update),           \
+        _cfg(U32, max_neighbor_reports),       \
+        _cfg(U32, max_retry_band),             \
+        _cfg(U32, seen_policy_timeout),        \
+        _cfg(U32, measurement_report_timeout), \
+        _cfg(U32, load_balancing_threshold),   \
+        _cfg(U32, band_steering_threshold),    \
+        _cfg(U32, remote_update_interval),     \
+        _cfg(U32, remote_node_timeout),        \
+        _cfg(BOOL, assoc_steering),            \
+        _cfg(BOOL, aggressive_all),            \
+        _cfg(ARRAY_CB, aggressive_mac_list),   \
+        _cfg(U32, aggressive_disassoc_timer),  \
+        _cfg(I32, min_connect_snr),            \
+        _cfg(I32, min_snr),                    \
+        _cfg(U32, min_snr_kick_delay),         \
+        _cfg(U32, steer_reject_timeout),       \
+        _cfg(U32, roam_process_timeout),       \
+        _cfg(I32, roam_scan_snr),              \
+        _cfg(U32, roam_scan_tries),            \
+        _cfg(U32, roam_scan_timeout),          \
+        _cfg(U32, roam_scan_interval),         \
+        _cfg(I32, roam_trigger_snr),           \
+        _cfg(U32, roam_trigger_interval),      \
+        _cfg(U32, roam_kick_delay),            \
+        _cfg(U32, signal_diff_threshold),      \
+        _cfg(U32, initial_connect_delay),      \
+        _cfg(BOOL, load_kick_enabled),         \
+        _cfg(U32, load_kick_threshold),        \
+        _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(U32, link_measurement_interval),  \
+        _cfg(ARRAY_CB, interfaces),            \
+        _cfg(STRING_CB, node_up_script),       \
+        _cfg(ARRAY_CB, event_log_types),       \
+        _cfg(ARRAY_CB, ssid_list)
+
+enum cfg_items
+{
 #define _cfg(_type, _name) CFG_##_name
     __config_items,
 #undef _cfg
@@ -197,7 +211,7 @@ enum cfg_items {
 };
 
 static const struct blobmsg_policy config_policy[__CFG_MAX] = {
-#define _cfg_policy(_type, _name) [CFG_##_name] = { .name = #_name,
.type = BLOBMSG_TYPE_ ## _type }
+#define _cfg_policy(_type, _name) [CFG_##_name] = {.name = #_name,
.type = BLOBMSG_TYPE_##_type}
 #define _cfg_policy_BOOL(_name) _cfg_policy(BOOL, _name)
 #define _cfg_policy_U32(_name) _cfg_policy(INT32, _name)
 #define _cfg_policy_I32(_name) _cfg_policy(INT32, _name)
@@ -212,31 +226,33 @@ static const struct cfg_item
config_data[__CFG_MAX] = {
 #define _cfg_data_BOOL(_name) .ptr.BOOL = &config._name
 #define _cfg_data_U32(_name) .ptr.U32 = &config._name
 #define _cfg_data_I32(_name) .ptr.I32 = &config._name
-#define _cfg_data_ARRAY_CB(_name) .ptr.CB = { .set =
config_set_##_name, .get = config_get_##_name }
-#define _cfg_data_STRING_CB(_name) .ptr.CB = { .set =
config_set_##_name, .get = config_get_##_name }
-#define _cfg(_type, _name) [CFG_##_name] = { .type = CFG_##_type,
_cfg_data_##_type(_name) }
+#define _cfg_data_ARRAY_CB(_name) .ptr.CB = {.set = config_set_##_name,
.get = config_get_##_name}
+#define _cfg_data_STRING_CB(_name) .ptr.CB = {.set =
config_set_##_name, .get = config_get_##_name}
+#define _cfg(_type, _name) [CFG_##_name] = {.type = CFG_##_type,
_cfg_data_##_type(_name)}
     __config_items,
 #undef _cfg
 };
 
 static int
 usteer_ubus_get_config(struct ubus_context *ctx, struct ubus_object *obj,
-              struct ubus_request_data *req, const char *method,
-              struct blob_attr *msg)
+                       struct ubus_request_data *req, const char *method,
+                       struct blob_attr *msg)
 {
     int i;
 
     blob_buf_init(&b, 0);
-    for (i = 0; i < __CFG_MAX; i++) {
-        switch(config_data[i].type) {
+    for (i = 0; i < __CFG_MAX; i++)
+    {
+        switch (config_data[i].type)
+        {
         case CFG_BOOL:
             blobmsg_add_u8(&b, config_policy[i].name,
-                    *config_data[i].ptr.BOOL);
+                           *config_data[i].ptr.BOOL);
             break;
         case CFG_I32:
         case CFG_U32:
             blobmsg_add_u32(&b, config_policy[i].name,
-                    *config_data[i].ptr.U32);
+                            *config_data[i].ptr.U32);
             break;
         case CFG_ARRAY_CB:
         case CFG_STRING_CB:
@@ -250,8 +266,8 @@ usteer_ubus_get_config(struct ubus_context *ctx,
struct ubus_object *obj,
 
 static int
 usteer_ubus_set_config(struct ubus_context *ctx, struct ubus_object *obj,
-              struct ubus_request_data *req, const char *method,
-              struct blob_attr *msg)
+                       struct ubus_request_data *req, const char *method,
+                       struct blob_attr *msg)
 {
     struct blob_attr *tb[__CFG_MAX];
     int i;
@@ -260,8 +276,10 @@ usteer_ubus_set_config(struct ubus_context *ctx,
struct ubus_object *obj,
         usteer_init_defaults();
 
     blobmsg_parse(config_policy, __CFG_MAX, tb, blob_data(msg),
blob_len(msg));
-    for (i = 0; i < __CFG_MAX; i++) {
-        switch(config_data[i].type) {
+    for (i = 0; i < __CFG_MAX; i++)
+    {
+        switch (config_data[i].type)
+        {
         case CFG_BOOL:
             if (!tb[i])
                 continue;
@@ -307,12 +325,12 @@ void usteer_dump_node(struct blob_buf *buf, struct
usteer_node *node)
 
     if (node->rrm_nr)
         blobmsg_add_field(buf, BLOBMSG_TYPE_ARRAY, "rrm_nr",
-                  blobmsg_data(node->rrm_nr),
-                  blobmsg_data_len(node->rrm_nr));
+                          blobmsg_data(node->rrm_nr),
+                          blobmsg_data_len(node->rrm_nr));
     if (node->node_info)
         blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "node_info",
-                  blob_data(node->node_info),
-                  blob_len(node->node_info));
+                          blob_data(node->node_info),
+                          blob_len(node->node_info));
 
     blobmsg_close_table(buf, c);
 }
@@ -325,15 +343,15 @@ void usteer_dump_host(struct blob_buf *buf, struct
usteer_remote_host *host)
     blobmsg_add_u32(buf, "id", (uint32_t)(uintptr_t)host->avl.key);
     if (host->host_info)
         blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "host_info",
-                  blobmsg_data(host->host_info),
-                  blobmsg_len(host->host_info));
+                          blobmsg_data(host->host_info),
+                          blobmsg_len(host->host_info));
     blobmsg_close_table(buf, c);
 }
 
 static int
 usteer_ubus_local_info(struct ubus_context *ctx, struct ubus_object *obj,
-              struct ubus_request_data *req, const char *method,
-              struct blob_attr *msg)
+                       struct ubus_request_data *req, const char *method,
+                       struct blob_attr *msg)
 {
     struct usteer_node *node;
 
@@ -349,8 +367,8 @@ usteer_ubus_local_info(struct ubus_context *ctx,
struct ubus_object *obj,
 
 static int
 usteer_ubus_remote_hosts(struct ubus_context *ctx, struct ubus_object *obj,
-             struct ubus_request_data *req, const char *method,
-             struct blob_attr *msg)
+                         struct ubus_request_data *req, const char *method,
+                         struct blob_attr *msg)
 {
     struct usteer_remote_host *host;
 
@@ -366,8 +384,8 @@ usteer_ubus_remote_hosts(struct ubus_context *ctx,
struct ubus_object *obj,
 
 static int
 usteer_ubus_remote_info(struct ubus_context *ctx, struct ubus_object *obj,
-               struct ubus_request_data *req, const char *method,
-               struct blob_attr *msg)
+                        struct ubus_request_data *req, const char *method,
+                        struct blob_attr *msg)
 {
     struct usteer_remote_node *rn;
 
@@ -383,21 +401,22 @@ usteer_ubus_remote_info(struct ubus_context *ctx,
struct ubus_object *obj,
 
 static const char *usteer_get_roam_sm_name(enum roam_trigger_state state)
 {
-    switch (state) {
-        case ROAM_TRIGGER_IDLE:
-            return "ROAM_TRIGGER_IDLE";
-        case ROAM_TRIGGER_SCAN:
-            return "ROAM_TRIGGER_SCAN";
-        case ROAM_TRIGGER_SCAN_DONE:
-            return "ROAM_TRIGGER_SCAN_DONE";
+    switch (state)
+    {
+    case ROAM_TRIGGER_IDLE:
+        return "ROAM_TRIGGER_IDLE";
+    case ROAM_TRIGGER_SCAN:
+        return "ROAM_TRIGGER_SCAN";
+    case ROAM_TRIGGER_SCAN_DONE:
+        return "ROAM_TRIGGER_SCAN_DONE";
     }
     return "N/A";
 }
 
 static int
 usteer_ubus_get_connected_clients(struct ubus_context *ctx, struct
ubus_object *obj,
-                  struct ubus_request_data *req, const char *method,
-                  struct blob_attr *msg)
+                                  struct ubus_request_data *req, const
char *method,
+                                  struct blob_attr *msg)
 {
     struct usteer_measurement_report *mr;
     struct usteer_node *node;
@@ -406,10 +425,12 @@ usteer_ubus_get_connected_clients(struct
ubus_context *ctx, struct ubus_object *
 
     blob_buf_init(&b, 0);
 
-    for_each_local_node(node) {
+    for_each_local_node(node)
+    {
         n = blobmsg_open_table(&b, usteer_node_name(node));
 
-        list_for_each_entry(si, &node->sta_info, node_list) {
+        list_for_each_entry(si, &node->sta_info, node_list)
+        {
             if (si->connected != STA_CONNECTED)
                 continue;
 
@@ -423,7 +444,7 @@ usteer_ubus_get_connected_clients(struct
ubus_context *ctx, struct ubus_object *
             blobmsg_close_table(&b, t);
 
             t = blobmsg_open_table(&b, "roam-state-machine");
-            blobmsg_add_string(&b,
"state",usteer_get_roam_sm_name(si->roam_state));
+            blobmsg_add_string(&b, "state",
usteer_get_roam_sm_name(si->roam_state));
             blobmsg_add_u32(&b, "tries", si->roam_tries);
             blobmsg_add_u64(&b, "event", si->roam_event ? current_time
- si->roam_event : 0);
             blobmsg_add_u32(&b, "kick-count", si->kick_count);
@@ -458,7 +479,8 @@ usteer_ubus_get_connected_clients(struct
ubus_context *ctx, struct ubus_object *
 
             /* Measurements */
             a = blobmsg_open_array(&b, "measurements");
-            list_for_each_entry(mr, &si->sta->measurements, sta_list) {
+            list_for_each_entry(mr, &si->sta->measurements, sta_list)
+            {
                 t = blobmsg_open_table(&b, "");
                 blobmsg_add_string(&b, "node", usteer_node_name(mr->node));
                 blobmsg_add_u32(&b, "rcpi", mr->rcpi);
@@ -480,30 +502,32 @@ usteer_ubus_get_connected_clients(struct
ubus_context *ctx, struct ubus_object *
     return 0;
 }
 
-enum {
+enum
+{
     NODE_DATA_NODE,
     NODE_DATA_VALUES,
     __NODE_DATA_MAX,
 };
 
 static const struct blobmsg_policy set_node_data_policy[] = {
-    [NODE_DATA_NODE] = { "node", BLOBMSG_TYPE_STRING },
-    [NODE_DATA_VALUES] = { "data", BLOBMSG_TYPE_TABLE },
+    [NODE_DATA_NODE] = {"node", BLOBMSG_TYPE_STRING},
+    [NODE_DATA_VALUES] = {"data", BLOBMSG_TYPE_TABLE},
 };
 
 static const struct blobmsg_policy del_node_data_policy[] = {
-    [NODE_DATA_NODE] = { "node", BLOBMSG_TYPE_STRING },
-    [NODE_DATA_VALUES] = { "names", BLOBMSG_TYPE_ARRAY },
+    [NODE_DATA_NODE] = {"node", BLOBMSG_TYPE_STRING},
+    [NODE_DATA_VALUES] = {"names", BLOBMSG_TYPE_ARRAY},
 };
 
 static void
 usteer_update_kvlist_data(struct kvlist *kv, struct blob_attr *data,
-              bool delete)
+                          bool delete)
 {
     struct blob_attr *cur;
     int rem;
 
-    blobmsg_for_each_attr(cur, data, rem) {
+    blobmsg_for_each_attr(cur, data, rem)
+    {
         if (delete)
             kvlist_delete(kv, blobmsg_get_string(cur));
         else
@@ -520,7 +544,7 @@ usteer_update_kvlist_blob(struct blob_attr **dest,
struct kvlist *kv)
     blob_buf_init(&b, 0);
     kvlist_for_each(kv, name, val)
         blobmsg_add_field(&b, blobmsg_type(val), name,
-                  blobmsg_data(val), blobmsg_len(val));
+                          blobmsg_data(val), blobmsg_len(val));
 
     val = b.head;
     if (!blobmsg_len(val))
@@ -531,8 +555,8 @@ usteer_update_kvlist_blob(struct blob_attr **dest,
struct kvlist *kv)
 
 static int
 usteer_ubus_update_node_data(struct ubus_context *ctx, struct
ubus_object *obj,
-                 struct ubus_request_data *req, const char *method,
-                 struct blob_attr *msg)
+                             struct ubus_request_data *req, const char
*method,
+                             struct blob_attr *msg)
 {
     const struct blobmsg_policy *policy;
     struct blob_attr *tb[__NODE_DATA_MAX];
@@ -550,10 +574,11 @@ usteer_ubus_update_node_data(struct ubus_context
*ctx, struct ubus_object *obj,
 
     name = blobmsg_get_string(tb[NODE_DATA_NODE]);
     val = tb[NODE_DATA_VALUES];
-    if (delete && blobmsg_check_array(val, BLOBMSG_TYPE_STRING) < 0)
+    if (delete &&blobmsg_check_array(val, BLOBMSG_TYPE_STRING) < 0)
         return UBUS_STATUS_INVALID_ARGUMENT;
 
-    if (strcmp(name, "*") != 0) {
+    if (strcmp(name, "*") != 0)
+    {
         ln = avl_find_element(&local_nodes, name, ln, node.avl);
         if (!ln)
             return UBUS_STATUS_NOT_FOUND;
@@ -598,9 +623,9 @@ static bool
 usteer_add_nr_entry(struct usteer_node *ln, struct usteer_node *node)
 {
     struct blobmsg_policy policy[3] = {
-        { .type = BLOBMSG_TYPE_STRING },
-        { .type = BLOBMSG_TYPE_STRING },
-        { .type = BLOBMSG_TYPE_STRING },
+        {.type = BLOBMSG_TYPE_STRING},
+        {.type = BLOBMSG_TYPE_STRING},
+        {.type = BLOBMSG_TYPE_STRING},
     };
     struct blob_attr *tb[3];
 
@@ -614,15 +639,15 @@ usteer_add_nr_entry(struct usteer_node *ln, struct
usteer_node *node)
         return false;
 
     blobmsg_parse_array(policy, ARRAY_SIZE(tb), tb,
-                blobmsg_data(node->rrm_nr),
-                blobmsg_data_len(node->rrm_nr));
+                        blobmsg_data(node->rrm_nr),
+                        blobmsg_data_len(node->rrm_nr));
     if (!tb[2])
         return false;
 
     blobmsg_add_field(&b, BLOBMSG_TYPE_STRING, "",
-              blobmsg_data(tb[2]),
-              blobmsg_data_len(tb[2]));
-    
+                      blobmsg_data(tb[2]),
+                      blobmsg_data_len(tb[2]));
+
     return true;
 }
 
@@ -644,7 +669,8 @@ usteer_ubus_disassoc_add_neighbors(struct sta_info *si)
     void *c;
 
     c = blobmsg_open_array(&b, "neighbors");
-    for_each_local_node(node) {
+    for_each_local_node(node)
+    {
         if (i >= config.max_neighbor_reports)
             break;
         if (si->node == node)
@@ -653,9 +679,11 @@ usteer_ubus_disassoc_add_neighbors(struct sta_info *si)
             i++;
     }
 
-    while (i < config.max_neighbor_reports) {
+    while (i < config.max_neighbor_reports)
+    {
         node = usteer_node_get_next_neighbor(si->node,
last_remote_neighbor);
-        if (!node) {
+        if (!node)
+        {
             /* No more nodes available */
             break;
         }
@@ -668,11 +696,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,
+                                       uint8_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 +709,32 @@ 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) {
+    if (!target_node)
+    {
+        // Add all known neighbors if no specific target set
+        MSG(DEBUG, "ROAMING requested for sta=" MAC_ADDR_FMT " without
target\n", MAC_ADDR_DATA(si->sta->addr));
         usteer_ubus_disassoc_add_neighbors(si);
-    } else {
+    }
+    else
+    {
+        MSG(DEBUG, "ROAMING requested for sta=" MAC_ADDR_FMT " to %s
with disassociation timer %i\n", MAC_ADDR_DATA(si->sta->addr),
usteer_node_name(target_node), disassoc_timer);
         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,
+                                      uint8_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,21 +742,31 @@ 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) {
+    for_each_local_node(node)
+    {
         if (!usteer_band_steering_is_target(ln, node))
             continue;
-    
+        // TODO: Funktioniert nicht, Targets werden nicht korrekt
ausgewiesen.
         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);
+    if (sizeof(si->node) > 0)
+    {
+        MSG(DEBUG, "BAND STEERING requested for sta=" MAC_ADDR_FMT "
with disassociation timer %i\n", MAC_ADDR_DATA(si->sta->addr),
disassoc_timer);
+        return ubus_invoke(ubus_ctx, ln->obj_id,
"bss_transition_request", b.head, NULL, 0, 100);
+    }
+    else
+        MSG(DEBUG, "BAND STEERING no targets found for sta="
MAC_ADDR_FMT "\n", MAC_ADDR_DATA(si->sta->addr));
 }
 
 int usteer_ubus_trigger_link_measurement(struct sta_info *si)
@@ -733,7 +787,8 @@ int usteer_ubus_trigger_client_scan(struct sta_info *si)
 {
     struct usteer_local_node *ln = container_of(si->node, struct
usteer_local_node, node);
 
-    if (!usteer_sta_supports_beacon_measurement_mode(si,
BEACON_MEASUREMENT_ACTIVE)) {
+    if (!usteer_sta_supports_beacon_measurement_mode(si,
BEACON_MEASUREMENT_ACTIVE))
+    {
         MSG(DEBUG, "STA does not support beacon measurement sta="
MAC_ADDR_FMT "\n", MAC_ADDR_DATA(si->sta->addr));
         return 0;
     }
diff --git a/usteer.h b/usteer.h
index f692fb8..26eeff0 100644
--- a/usteer.h
+++ b/usteer.h
@@ -12,9 +12,9 @@
  *   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) 2020 embedd.ch
- *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
- *   Copyright (C) 2020 John Crispin <john at phrozen.org>
+ *   Copyright (C) 2020 embedd.ch
+ *   Copyright (C) 2020 Felix Fietkau <nbd at nbd.name>
+ *   Copyright (C) 2020 John Crispin <john at phrozen.org>
  */
 
 #ifndef __APMGR_H
@@ -31,36 +31,40 @@
 
 #define NO_SIGNAL 0xff
 
-#define __STR(x)        #x
-#define _STR(x)            __STR(x)
+#define __STR(x) #x
+#define _STR(x) __STR(x)
 
-#define APMGR_V6_MCAST_GROUP    "ff02::4150"
+#define APMGR_V6_MCAST_GROUP "ff02::4150"
 
-#define APMGR_PORT        16720 /* AP */
-#define APMGR_PORT_STR        _STR(APMGR_PORT)
-#define APMGR_BUFLEN        (64 * 1024)
+#define APMGR_PORT 16720 /* AP */
+#define APMGR_PORT_STR _STR(APMGR_PORT)
+#define APMGR_BUFLEN (64 * 1024)
 
-#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
 
-enum usteer_event_type {
+enum usteer_event_type
+{
     EVENT_TYPE_PROBE,
     EVENT_TYPE_ASSOC,
     EVENT_TYPE_AUTH,
     __EVENT_TYPE_MAX,
 };
 
-enum usteer_node_type {
+enum usteer_node_type
+{
     NODE_TYPE_LOCAL,
     NODE_TYPE_REMOTE,
 };
 
-enum usteer_sta_connection_state {
+enum usteer_sta_connection_state
+{
     STA_NOT_CONNECTED = 0,
     STA_CONNECTED = 1,
     STA_DISCONNECTED = 2,
 };
 
-enum usteer_beacon_measurement_mode {
+enum usteer_beacon_measurement_mode
+{
     BEACON_MEASUREMENT_PASSIVE = 0,
     BEACON_MEASUREMENT_ACTIVE = 1,
     BEACON_MEASUREMENT_TABLE = 2,
@@ -70,7 +74,8 @@ struct sta_info;
 struct usteer_local_node;
 struct usteer_remote_host;
 
-struct usteer_node {
+struct usteer_node
+{
     struct avl_node avl;
     struct list_head sta_info;
     struct list_head measurements;
@@ -91,7 +96,8 @@ struct usteer_node {
     int max_assoc;
     int load;
 
-    struct {
+    struct
+    {
         int source;
         int target;
     } roam_events;
@@ -99,14 +105,16 @@ struct usteer_node {
     uint64_t created;
 };
 
-struct usteer_scan_request {
+struct usteer_scan_request
+{
     int n_freq;
     int *freq;
 
     bool passive;
 };
 
-struct usteer_scan_result {
+struct usteer_scan_result
+{
     uint8_t bssid[6];
     char ssid[33];
 
@@ -114,7 +122,8 @@ struct usteer_scan_result {
     int signal;
 };
 
-struct usteer_survey_data {
+struct usteer_survey_data
+{
     uint16_t freq;
     int8_t noise;
 
@@ -122,14 +131,16 @@ struct usteer_survey_data {
     uint64_t time_busy;
 };
 
-struct usteer_freq_data {
+struct usteer_freq_data
+{
     uint16_t freq;
 
     uint8_t txpower;
     bool dfs;
 };
 
-struct usteer_node_handler {
+struct usteer_node_handler
+{
     struct list_head list;
 
     void (*init_node)(struct usteer_node *);
@@ -137,14 +148,15 @@ struct usteer_node_handler {
     void (*update_node)(struct usteer_node *);
     void (*update_sta)(struct usteer_node *, struct sta_info *);
     void (*get_survey)(struct usteer_node *, void *,
-               void (*cb)(void *priv, struct usteer_survey_data *d));
+                       void (*cb)(void *priv, struct usteer_survey_data
*d));
     void (*get_freqlist)(struct usteer_node *, void *,
-                 void (*cb)(void *priv, struct usteer_freq_data *f));
+                         void (*cb)(void *priv, struct usteer_freq_data
*f));
     int (*scan)(struct usteer_node *, struct usteer_scan_request *,
-            void *, void (*cb)(void *priv, struct usteer_scan_result *r));
+                void *, void (*cb)(void *priv, struct
usteer_scan_result *r));
 };
 
-struct usteer_config {
+struct usteer_config
+{
     bool syslog;
     uint32_t debug_level;
 
@@ -170,6 +182,10 @@ struct usteer_config {
     uint32_t remote_update_interval;
     uint32_t remote_node_timeout;
 
+    bool aggressive_all;
+    struct blob_attr *aggressive_mac_list;
+    uint32_t aggressive_disassoc_timer;
+
     int32_t min_snr;
     uint32_t min_snr_kick_delay;
     int32_t min_connect_snr;
@@ -190,7 +206,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;
 
@@ -208,7 +224,8 @@ struct usteer_config {
     struct blob_attr *ssid_list;
 };
 
-struct usteer_bss_tm_query {
+struct usteer_bss_tm_query
+{
     struct list_head list;
 
     /* Can't use sta_info here, as the STA might already be deleted */
@@ -216,20 +233,23 @@ struct usteer_bss_tm_query {
     uint8_t dialog_token;
 };
 
-struct sta_info_stats {
+struct sta_info_stats
+{
     uint32_t requests;
     uint32_t blocked_cur;
     uint32_t blocked_total;
     uint32_t blocked_last_time;
 };
 
-enum roam_trigger_state {
+enum roam_trigger_state
+{
     ROAM_TRIGGER_IDLE,
     ROAM_TRIGGER_SCAN,
     ROAM_TRIGGER_SCAN_DONE,
 };
 
-struct sta_info {
+struct sta_info
+{
     struct list_head list;
     struct list_head node_list;
 
@@ -255,15 +275,18 @@ struct sta_info {
     uint8_t roam_tries;
     uint64_t roam_event;
     uint64_t roam_kick;
+    uint64_t roam_disassoc_time;
     uint64_t roam_scan_start;
     uint64_t roam_scan_timeout_start;
 
-    struct {
+    struct
+    {
         uint8_t status_code;
         uint64_t timestamp;
     } bss_transition_response;
 
-    struct {
+    struct
+    {
         bool below_snr;
     } band_steering;
 
@@ -277,7 +300,8 @@ struct sta_info {
     uint8_t connected : 2;
 };
 
-struct sta {
+struct sta
+{
     struct avl_node avl;
     struct list_head nodes;
     struct list_head measurements;
@@ -285,10 +309,13 @@ struct sta {
     uint8_t seen_2ghz : 1;
     uint8_t seen_5ghz : 1;
 
+    bool aggressive;
+
     uint8_t addr[6];
 };
 
-struct usteer_measurement_report {
+struct usteer_measurement_report
+{
     struct usteer_timeout timeout;
 
     struct list_head list;
@@ -311,13 +338,13 @@ extern struct list_head node_handlers;
 extern struct avl_tree stations;
 extern struct ubus_object usteer_obj;
 extern uint64_t current_time;
-extern const char * const event_types[__EVENT_TYPE_MAX];
+extern const char *const event_types[__EVENT_TYPE_MAX];
 extern struct blob_attr *host_info_blob;
 
 void usteer_update_time(void);
 void usteer_init_defaults(void);
 bool usteer_handle_sta_event(struct usteer_node *node, const uint8_t *addr,
-                enum usteer_event_type type, int freq, int signal);
+                             enum usteer_event_type type, int freq, int
signal);
 
 int usteer_snr_to_signal(struct usteer_node *node, int snr);
 
@@ -336,13 +363,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,
+                                      uint8_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,
+                                       uint8_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 +409,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_aggressive_mac_list(struct blob_attr *data);
+void config_get_aggressive_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);
@@ -390,7 +426,7 @@ void usteer_dump_host(struct blob_buf *buf, struct
usteer_remote_host *host);
 
 int usteer_measurement_get_rssi(struct usteer_measurement_report *report);
 
-struct usteer_measurement_report * usteer_measurement_report_get(struct
sta *sta, struct usteer_node *node, bool create);
+struct usteer_measurement_report *usteer_measurement_report_get(struct
sta *sta, struct usteer_node *node, bool create);
 void usteer_measurement_report_node_cleanup(struct usteer_node *node);
 void usteer_measurement_report_sta_cleanup(struct sta *sta);
 void usteer_measurement_report_del(struct usteer_measurement_report *mr);
@@ -398,6 +434,5 @@ void usteer_measurement_report_del(struct
usteer_measurement_report *mr);
 struct usteer_measurement_report *
 usteer_measurement_report_add(struct sta *sta, struct usteer_node
*node, uint8_t rcpi, uint8_t rsni, uint64_t timestamp);
 
-
 int usteer_ubus_trigger_link_measurement(struct sta_info *si);
 #endif
-- 
2.39.5





More information about the openwrt-devel mailing list