diff --git a/band_steering.c b/band_steering.c index 7fce1df..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; } @@ -60,6 +77,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 +110,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..bd6ae4b 100644 --- a/main.c +++ b/main.c @@ -96,11 +96,14 @@ void usteer_init_defaults(void) config.remote_update_interval = 1000; config.initial_connect_delay = 0; config.remote_node_timeout = 10; + config.aggressiveness = 3; + config.reassociation_delay = 30; config.steer_reject_timeout = 60000; - config.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 f53c338..ecb43d9 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,19 @@ 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 + + # Timeout (s in "1024ms") a station is requested to avoid reassociation after bss transition + #option reassociation_delay 30 + + # List of MACs (lower case) to set aggressiveness per station (ff:ff:ff:ff:ff,2) + #list aggressiveness_mac_list '' + # Timeout (in ms) after which a association following a disassociation is not seen # as a roam #option roam_process_timeout 5000 @@ -128,6 +143,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 07fd99e..a7d3fdf 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 \ @@ -83,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/policy.c b/policy.c index 8c5d244..0ccb73f 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; } @@ -483,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; } } @@ -567,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); @@ -582,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/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..0ed9f1d 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,31 @@ 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); + 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 */ + 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 +716,15 @@ 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); + 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) { @@ -709,10 +732,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) @@ -750,13 +777,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 f692fb8..407d02d 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; @@ -170,6 +177,11 @@ 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; + uint32_t reassociation_delay; + int32_t min_snr; uint32_t min_snr_kick_delay; int32_t min_connect_snr; @@ -190,7 +202,8 @@ 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 band_steering_signal_threshold; uint32_t link_measurement_interval; @@ -254,6 +267,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; @@ -265,6 +280,7 @@ struct sta_info { struct { bool below_snr; + int signal_threshold; } band_steering; uint64_t kick_time; @@ -285,6 +301,8 @@ struct sta { uint8_t seen_2ghz : 1; uint8_t seen_5ghz : 1; + uint32_t aggressiveness; + uint8_t addr[6]; }; @@ -334,15 +352,21 @@ 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); +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 +400,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);