kicking: improve kicking algorithm

- Tweak disassociate / kick NR code to reduce build overhead
- Add set_hostapd_nrdynamic == 2 to build of AP local station NR from what current clients hear elsewhere
- Tighten up hex string length usage for strncpy(), etc

[cleanup commit message]
Signed-off-by: Nick Hainke <vincent@systemli.org>
This commit is contained in:
Ian Clowes 2022-01-31 11:40:55 +00:00 committed by Nick Hainke
parent 60ea5b4daf
commit 829fa0faed
7 changed files with 217 additions and 125 deletions

View file

@ -74,6 +74,7 @@ struct probe_metric_s {
int min_number_to_kick; // kick_clients()
int chan_util_avg_period;
int set_hostapd_nr;
int disassoc_nr_length; // FIXME: Is there a standard for how many? Use 6 as default to suit iOS.
int kicking;
int kicking_threshold;
int duration;
@ -192,7 +193,7 @@ typedef struct hostapd_notify_entry_s {
} hostapd_notify_entry;
// ---------------- Defines ----------------
// FIXME: Is this enough of the NR to allow 802.11 operations to work? Should we go for max possible length of 256 * 2 + 1
#define NEIGHBOR_REPORT_LEN 200
/* Neighbor report string elements
* [Elemen ID|1][LENGTH|1][BSSID|6][BSSID INFORMATION|4][Operating Class|1][Channel Number|1][PHY Type|1][Operational Subelements]
@ -259,7 +260,7 @@ typedef struct ap_s {
time_t time; // remove_old...entries // (2)
uint32_t station_count; // compare_station_count() <- better_ap_available() // (1)
uint8_t ssid[SSID_MAX_LEN + 1]; // compare_sid() < -better_ap_available() // (1)
char neighbor_report[NEIGHBOR_REPORT_LEN]; // (1) // TODO: Check whether we should store and AP one or generate on fly for client
char neighbor_report[NEIGHBOR_REPORT_LEN + 1]; // (1) // This is the self-NR of the AP
uint32_t op_class; // ubus_send_beacon_report() // (1)
uint32_t channel; // ubus_send_beacon_report() // (1)
//uint32_t collision_domain; // TODO: ap_get_collision_count() never evaluated?
@ -345,7 +346,7 @@ void send_beacon_requests(ap *a, int id);
/* Utils */
struct kicking_nr {
char nr[NEIGHBOR_REPORT_LEN];
ap *nr_ap;
int score;
struct kicking_nr *next;
};

View file

@ -60,6 +60,8 @@ void del_client_all_interfaces(const struct dawn_mac client_addr, uint32_t reaso
*/
void update_hostapd_sockets(struct uloop_timeout *t);
void ubus_set_nr_from_clients(struct kicking_nr* ap_list);
void ubus_send_beacon_request(client *c, ap *a, int id);
void uloop_add_data_cbs();

View file

@ -277,68 +277,88 @@ static int compare_station_count(ap* ap_entry_own, ap* ap_entry_to_compare, stru
return 0;
}
static struct kicking_nr *find_position(struct kicking_nr *nrlist, int score) {
struct kicking_nr *ret = NULL;
dawnlog_debug_func("Entering...");
while (nrlist && nrlist->score < score) {
ret = nrlist;
nrlist = nrlist->next;
}
return ret;
}
static void remove_kicking_nr_list(struct kicking_nr *nr_list) {
struct kicking_nr *n;
dawnlog_debug_func("Entering...");
while(nr_list) {
n = nr_list->next;
dawn_free(nr_list);
nr_list = n;
}
}
static struct kicking_nr *prune_kicking_nr_list(struct kicking_nr *nr_list, int min_score) {
struct kicking_nr *next;
dawnlog_debug_func("Entering...");
while (nr_list && nr_list->score <= min_score) {
next = nr_list->next;
struct kicking_nr* next = nr_list->next;
dawn_free(nr_list);
nr_list = next;
}
return nr_list;
}
static struct kicking_nr *insert_kicking_nr(struct kicking_nr *head, char *nr, int score, bool prune) {
struct kicking_nr *new_entry, *pos;
#if 0
static void prune_kicking_nr_list(struct kicking_nr **nr_list, int min_score) {
dawnlog_debug_func("Entering...");
if (prune)
head = prune_kicking_nr_list(head, score - dawn_metric.kicking_threshold);
// we are giving no error information here (not really critical)
if (!(new_entry = dawn_malloc(sizeof (struct kicking_nr))))
return head;
strncpy(new_entry->nr, nr, NEIGHBOR_REPORT_LEN);
new_entry->score = score;
pos = find_position(head, score);
if (pos) {
new_entry->next = pos->next;
pos -> next = new_entry;
} else {
new_entry->next = head;
head = new_entry;
while (*nr_list) {
if ((*nr_list)->score <= min_score)
{
struct kicking_nr* next = (*nr_list)->next;
dawn_free(*nr_list);
*nr_list = next;
}
else
{
nr_list = &((*nr_list)->next);
}
}
return head;
return;
}
#endif
static void insert_kicking_nr_by_bssid(struct kicking_nr** nrlist, ap* nr_ap) {
dawnlog_debug_func("Entering...");
dawn_mutex_require(&ap_array_mutex);
// Look for the proposed BSSID in the current list
while (*nrlist && mac_compare_bb((*nrlist)->nr_ap->bssid_addr, nr_ap->bssid_addr) < 0) {
nrlist = &((*nrlist)->next);
}
// If it isn't there then add it
if (!*nrlist || mac_compare_bb((*nrlist)->nr_ap->bssid_addr, nr_ap->bssid_addr) != 0)
{
// we are giving no error information here (not really critical)
struct kicking_nr* new_entry = dawn_malloc(sizeof(struct kicking_nr));
if (new_entry)
{
new_entry->nr_ap = nr_ap;
new_entry->score = 0;
new_entry->next = *nrlist;
*nrlist = new_entry;
}
}
return;
}
static void insert_kicking_nr_by_score(struct kicking_nr** nrlist, ap* nr_ap, int score) {
dawnlog_debug_func("Entering...");
dawn_mutex_require(&ap_array_mutex);
// we are giving no error information here (not really critical)
struct kicking_nr* new_entry = dawn_malloc(sizeof(struct kicking_nr));
if (!new_entry)
return;
new_entry->nr_ap = nr_ap;
new_entry->score = score;
// Order entries high-low score so we can read first N highest values
while (*nrlist && (*nrlist)->score > score) {
nrlist = &((*nrlist)->next);
}
new_entry->next = *nrlist;
*nrlist = new_entry;
return;
}
int better_ap_available(ap *kicking_ap, struct dawn_mac client_mac, struct kicking_nr **neighbor_report) {
dawnlog_debug_func("Entering...");
@ -430,12 +450,14 @@ int better_ap_available(ap *kicking_ap, struct dawn_mac client_mac, struct kicki
else
{
dawnlog_trace("Better AP after full evaluation - add to NR (%s pruning)\n", ap_outcome == 2 ? "with" : "without");
// Pointer is NULL if we're only finding a better AP without actually using it
// NR is NULL if we're only testing for a better AP without actually using it
if (neighbor_report != NULL)
{
// Test this_kick_outcome for pruning
*neighbor_report = insert_kicking_nr(*neighbor_report, candidate_ap->neighbor_report,
score_to_compare, ap_outcome == 2);
// FIXME: Do we need to prune this list as we build it? trying new approach to send N best entries to hostapd
// if (ap_outcome == 2)
// prune_kicking_nr_list(neighbor_report, score_to_compare - dawn_metric.kicking_threshold);
insert_kicking_nr_by_score(neighbor_report, candidate_ap, score_to_compare);
}
// If we find a single candidate then we will be kicking
@ -471,6 +493,9 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
int kicked_clients = 0;
// Keep a list of nearby APs to update local AP Neighbor Report from
struct kicking_nr* ap_nr_list = NULL;
dawnlog_info("AP BSSID " MACSTR ": Looking for candidates to kick\n", MAC2STR(kicking_ap->bssid_addr.u8));
// Seach for BSSID
@ -478,7 +503,7 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
// Go through clients
while (j != NULL && mac_is_equal_bb(j->bssid_addr, kicking_ap->bssid_addr)) {
struct kicking_nr *neighbor_report = NULL;
struct kicking_nr *kick_nr_list = NULL;
int do_kick = 0;
@ -486,7 +511,14 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
dawnlog_info("Station " MACSTR ": Suppressing check due to MAC list entry\n", MAC2STR(j->client_addr.u8));
}
else {
do_kick = better_ap_available(kicking_ap, j->client_addr, &neighbor_report);
do_kick = better_ap_available(kicking_ap, j->client_addr, &kick_nr_list);
}
// If we found any candidates (even if too low to kick to) add the highest scoring one to local AP NR set
if (dawn_metric.set_hostapd_nr == 2 && kick_nr_list)
{
dawnlog_trace("Adding " MACSTR "as local NR entry candidate\n", MAC2STR(kick_nr_list->nr_ap->bssid_addr.u8));
insert_kicking_nr_by_bssid(&ap_nr_list, kick_nr_list->nr_ap);
}
// better ap available
@ -527,8 +559,8 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
if (dawnlog_showing(DAWNLOG_INFO))
{
for (struct kicking_nr* n = neighbor_report; n; n = n->next)
dawnlog_info("Kicking NR entry: " NR_MACSTR ", score=%d\n", NR_MAC2STR(n->nr), n->score);
for (struct kicking_nr* n = kick_nr_list; n; n = n->next)
dawnlog_info("Kicking NR entry: " NR_MACSTR ", score=%d\n", NR_MAC2STR(n->nr_ap->neighbor_report), n->score);
}
// here we should send a messsage to set the probe.count for all aps to the min that there is no delay between switching
@ -538,7 +570,7 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
// don't deauth station? <- deauth is better!
// maybe we can use handovers...
//del_client_interface(id, client_array[j].client_addr, NO_MORE_STAS, 1, 1000);
int sync_kick = wnm_disassoc_imminent(id, j->client_addr, neighbor_report, 12);
int sync_kick = wnm_disassoc_imminent(id, j->client_addr, kick_nr_list, 12);
// Synchronous kick is a test harness feature to indicate arrays have been updated, so don't change further
if (sync_kick)
@ -572,12 +604,18 @@ int kick_clients(struct dawn_mac bssid_mac, uint32_t id) {
j->kick_count = 0;
}
remove_kicking_nr_list(neighbor_report);
neighbor_report = NULL;
remove_kicking_nr_list(kick_nr_list);
kick_nr_list = NULL;
j = j->next_entry_bc;
}
if (dawn_metric.set_hostapd_nr == 2)
ubus_set_nr_from_clients(ap_nr_list);
remove_kicking_nr_list(ap_nr_list);
ap_nr_list = NULL;
dawnlog_trace("KICKING: --------- AP Finished ---------\n");
dawn_mutex_unlock(&ap_array_mutex);
@ -1318,9 +1356,9 @@ static void print_ap_entry(int level, ap *entry) {
{
dawnlog_info("ssid: %s, bssid_addr: " MACSTR ", freq: %d, ht: %d, vht: %d, chan_utilz: %d, neighbor_report: %s\n",
entry->ssid, MAC2STR(entry->bssid_addr.u8), entry->freq, entry->ht_support, entry->vht_support, entry->channel_utilization,
//entry->collision_domain, ap_get_collision_count(entry->collision_domain), // TODO: Fix format string if readding
entry->neighbor_report
);
//entry->collision_domain, ap_get_collision_count(entry->collision_domain), // TODO: Fix format string if readding
entry->neighbor_report
);
}
}

View file

@ -42,7 +42,7 @@ int ret = 0;
if (neighbor_list != NULL)
{
// Fake a client being disassociated and then rejoining on the recommended neoghbor
// Fake a client being disassociated and then rejoining on the recommended neighbor
client *mc = client_array_get_client(client_addr);
mc = client_array_delete(mc, true);
// Originally, there was only one AP, not a list of them; that AP is at the tail of the list
@ -50,9 +50,9 @@ int ret = 0;
while (neighbor_list && neighbor_list->next)
neighbor_list = neighbor_list->next;
for (int n=0; n < ETH_ALEN; n++)
sscanf(neighbor_list->nr + n*2, "%2hhx", mc->bssid_addr.u8 + n);
sscanf(neighbor_list->nr_ap->neighbor_report + n*2, "%2hhx", mc->bssid_addr.u8 + n);
insert_client_to_array(mc, 0);
printf("BSS TRANSITION TO " NR_MACSTR "\n", NR_MAC2STR(neighbor_list->nr));
printf("BSS TRANSITION TO " NR_MACSTR "\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report));
// Tell caller not to change the arrays any further
ret = 1;
@ -588,6 +588,7 @@ static int consume_actions(int argc, char* argv[], int harness_verbosity)
dawn_metric.min_number_to_kick = 3;
dawn_metric.chan_util_avg_period = 3;
dawn_metric.set_hostapd_nr = 1;
dawn_metric.disassoc_nr_length = 6;
dawn_metric.kicking = 0;
dawn_metric.duration = 0;
dawn_metric.rrm_mode_mask = WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
@ -624,6 +625,7 @@ static int consume_actions(int argc, char* argv[], int harness_verbosity)
else if (!strncmp(fn, "min_number_to_kick=", 19)) load_int(&dawn_metric.min_number_to_kick, fn + 19);
else if (!strncmp(fn, "chan_util_avg_period=", 21)) load_int(&dawn_metric.chan_util_avg_period, fn + 21);
else if (!strncmp(fn, "set_hostapd_nr=", 15)) load_int(&dawn_metric.set_hostapd_nr, fn + 15);
else if (!strncmp(fn, "disassoc_nr_length=", 19)) load_int(&dawn_metric.disassoc_nr_length, fn + 19);
else if (!strncmp(fn, "kicking=", 8)) load_int(&dawn_metric.kicking, fn + 8);
else if (!strncmp(fn, "duration=", 9)) load_int(&dawn_metric.duration, fn + 9);
else if (!strncmp(fn, "rrm_mode=", 9)) dawn_metric.rrm_mode_mask = parse_rrm_mode(dawn_metric.rrm_mode_order, fn + 9);
@ -873,44 +875,26 @@ static int consume_actions(int argc, char* argv[], int harness_verbosity)
}
else if (strcmp(*argv, "better_ap_available") == 0)
{
args_required = 4;
args_required = 5;
if (curr_arg + args_required <= argc)
{
struct dawn_mac bssid_mac;
struct dawn_mac client_mac;
uint32_t autokick;
uint32_t with_nr;
int tr = 9999; // Tamper evident value
hwaddr_aton(argv[1], bssid_mac.u8);
hwaddr_aton(argv[2], client_mac.u8);
load_u32(&autokick, argv[3]);
load_u32(&with_nr, argv[4]);
char nb[NEIGHBOR_REPORT_LEN] = "TAMPER EVIDENT NEIGHBOR REPORT INITIALISATION STRING";
struct kicking_nr neighbor = {0};
struct kicking_nr *neighbor_list = &neighbor;
struct kicking_nr *neighbor_list = NULL;
if (curr_arg + 5 <= argc)
{
args_required = 5;
tr = better_ap_available(ap_array_get_ap(bssid_mac), client_mac, with_nr ? &neighbor_list : NULL);
if (strcmp(argv[4], "\0") == 0) // Provide a way to set an empty string
{
strcpy(nb, "");
}
else
{
strcpy(nb, argv[4]);
}
strncpy(neighbor.nr, nb, NEIGHBOR_REPORT_LEN);
tr = better_ap_available(ap_array_get_ap(bssid_mac), client_mac, &neighbor_list);
}
else
{
tr = better_ap_available(ap_array_get_ap(bssid_mac), client_mac, NULL);
}
printf("better_ap_available returned %d (with neighbour report %s)\n", tr, nb);
printf("better_ap_available returned %d (with neighbour report %s)\n", tr, neighbor_list ? neighbor_list->nr_ap->neighbor_report : "NONE");
}
}
else if (strcmp(*argv, "eval_probe_metric") == 0)

View file

@ -241,6 +241,7 @@ struct probe_metric_s uci_get_dawn_metric() {
.eval_probe_req = 0,
.min_number_to_kick = 3,
.set_hostapd_nr = 1,
.disassoc_nr_length = 6,
.max_station_diff = 1,
.bandwidth_threshold = 6,
.use_driver_recog = 1,
@ -295,6 +296,7 @@ struct probe_metric_s uci_get_dawn_metric() {
DAWN_SET_CONFIG_INT(ret, global_s, eval_probe_req);
DAWN_SET_CONFIG_INT(ret, global_s, min_number_to_kick);
DAWN_SET_CONFIG_INT(ret, global_s, set_hostapd_nr);
DAWN_SET_CONFIG_INT(ret, global_s, disassoc_nr_length);
DAWN_SET_CONFIG_INT(ret, global_s, max_station_diff);
DAWN_SET_CONFIG_INT(ret, global_s, bandwidth_threshold);
DAWN_SET_CONFIG_INT(ret, global_s, use_driver_recog);

View file

@ -566,8 +566,16 @@ int parse_to_clients(struct blob_attr* msg) {
ap_entry->op_class = ap_entry->channel = 0;
if (tb[CLIENT_TABLE_NEIGHBOR]) {
// Copy part of report that we have space for, and discard remainder
strncpy(ap_entry->neighbor_report, blobmsg_get_string(tb[CLIENT_TABLE_NEIGHBOR]), NEIGHBOR_REPORT_LEN);
ap_entry->neighbor_report[NEIGHBOR_REPORT_LEN] = '\0';
sscanf(ap_entry->neighbor_report + NR_OP_CLASS, "%2x%2x", &ap_entry->op_class, &ap_entry->channel);
int nrl = strlen(ap_entry->neighbor_report);
if (nrl == NEIGHBOR_REPORT_LEN)
dawnlog_warning("Neighbor Report possibly truncated - hex length of %d\n", nrl);
else
dawnlog_debug("Neighbor Report - hex length of %d\n", nrl);
}
else {
ap_entry->neighbor_report[0] = '\0';

View file

@ -94,7 +94,7 @@ struct hostapd_sock_entry {
/*
[Elemen ID|1][LENGTH|1][BSSID|6][BSSID INFORMATION|4][Operating Class|1][Channel Number|1][PHY Type|1][Operational Subelements]
*/
char neighbor_report[NEIGHBOR_REPORT_LEN];
char neighbor_report[NEIGHBOR_REPORT_LEN + 1];
struct ubus_subscriber subscriber;
struct ubus_event_handler wait_handler;
@ -947,7 +947,7 @@ void update_clients(struct uloop_timeout *t) {
dawnlog_debug_func("Entering...");
ubus_get_clients();
if(dawn_metric.set_hostapd_nr)
if(dawn_metric.set_hostapd_nr == 1)
ubus_set_nr();
// maybe to much?! don't set timer again...
uloop_timeout_set(&client_timer, timeout_config.update_client * 1000);
@ -1084,6 +1084,7 @@ void ubus_set_nr(){
struct hostapd_sock_entry *sub;
int timeout = 1;
// FIXME: Should we then call ubus_get_nr() to cache it locally?
list_for_each_entry(sub, &hostapd_sock_list, list)
{
if (sub->subscribed) {
@ -1160,10 +1161,14 @@ int wnm_disassoc_imminent(uint32_t id, const struct dawn_mac client_addr, struct
blobmsg_add_u8(&b, "abridged", 1); // prefer aps in neighborlist
void* nbs = blobmsg_open_array(&b, "neighbors");
while(neighbor_list != NULL) {
dawnlog_info("BSS TRANSITION NEIGHBOR " NR_MACSTR ", Score=%d\n", NR_MAC2STR(neighbor_list->nr), neighbor_list->score);
blobmsg_add_string(&b, NULL, neighbor_list->nr);
// Add the first N AP, where list order id high->low score
int neighbors_added = 0;
while(neighbors_added < dawn_metric.disassoc_nr_length && neighbor_list != NULL) {
dawnlog_info("BSS TRANSITION NEIGHBOR " NR_MACSTR ", Score=%d\n", NR_MAC2STR(neighbor_list->nr_ap->neighbor_report), neighbor_list->score);
blobmsg_add_string(&b, NULL, neighbor_list->nr_ap->neighbor_report);
neighbor_list = neighbor_list->next;
neighbors_added++;
}
blobmsg_close_array(&b, nbs);
@ -1708,6 +1713,7 @@ int uci_send_via_network()
blobmsg_add_u32(&b, "min_number_to_kick", dawn_metric.min_number_to_kick);
blobmsg_add_u32(&b, "chan_util_avg_period", dawn_metric.chan_util_avg_period);
blobmsg_add_u32(&b, "set_hostapd_nr", dawn_metric.set_hostapd_nr);
blobmsg_add_u32(&b, "disassoc_nr_length", dawn_metric.disassoc_nr_length);
blobmsg_add_u32(&b, "duration", dawn_metric.duration);
blobmsg_add_string(&b, "rrm_mode", get_rrm_mode_string(dawn_metric.rrm_mode_order));
band_table = blobmsg_open_table(&b, "band_metrics");
@ -1966,7 +1972,7 @@ int build_network_overview(struct blob_buf *b) {
char *nr;
nr = blobmsg_alloc_string_buffer(b, "neighbor_report", NEIGHBOR_REPORT_LEN);
sprintf(nr, "%s", m->neighbor_report); // TODO: Why not strcpy()
strcpy(nr, m->neighbor_report);
blobmsg_add_string_buffer(b);
char *iface;
@ -2053,45 +2059,96 @@ static int mac_is_in_entry_list(const struct dawn_mac mac, const struct mac_entr
return 0;
}
// TODO: Does all APs constitute neighbor report? How about using list of AP connected
// clients can also see (from probe_set) to give more (physically) local set?
// Here, we let the user configure a list of preferred APs that clients can see, and then
// add the rest of all APs. hostapd inserts this list backwards, so we must start with
// the regular APs, then add the preferred ones, which are already ordered backwards.
int ap_get_nr(struct blob_buf *b_local, struct dawn_mac own_bssid_addr, const char *ssid) {
ap *i, *own_ap;
struct mac_entry_s *preferred_list, *n;
int ap_get_nr(struct blob_buf* b_local, struct dawn_mac own_bssid_addr, const char* ssid) {
dawnlog_debug_func("Entering...");
void* nbs = blobmsg_open_array(b_local, "list");
int ret = 0;
dawn_mutex_lock(&ap_array_mutex);
dawn_mutex_require(&ap_array_mutex);
own_ap = ap_array_get_ap(own_bssid_addr);
ap* own_ap = ap_array_get_ap(own_bssid_addr);
if (!own_ap)
return -1;
for (int band = 0; band < __DAWN_BAND_MAX; band++) {
preferred_list = dawn_metric.neighbors[band];
if (own_ap->freq <= max_band_freq[band])
break;
ret = -1;
else
{
void* nbs = blobmsg_open_array(b_local, "list");
struct mac_entry_s* preferred_list = NULL;
for (int band = 0; band < __DAWN_BAND_MAX; band++) {
preferred_list = dawn_metric.neighbors[band];
if (own_ap->freq <= max_band_freq[band])
break;
}
for (ap* i = ap_set; i != NULL; i = i->next_ap) {
if (i != own_ap && !strncmp((char*)i->ssid, ssid, SSID_MAX_LEN) &&
!mac_is_in_entry_list(i->bssid_addr, preferred_list))
{
blobmsg_add_nr(b_local, i);
}
}
for (struct mac_entry_s* n = preferred_list; n; n = n->next_mac) {
dawn_mutex_require(&ap_array_mutex);
ap* j = ap_array_get_ap(n->mac);
if (j)
blobmsg_add_nr(b_local, j);
}
blobmsg_close_array(b_local, nbs);
}
for (i = ap_set; i != NULL; i = i->next_ap) {
if (i != own_ap && !strncmp((char *)i->ssid, ssid, SSID_MAX_LEN) &&
!mac_is_in_entry_list(i->bssid_addr, preferred_list))
{
blobmsg_add_nr(b_local, i);
dawn_mutex_unlock(&ap_array_mutex);
return ret;
}
// Use list of specifc AP to create local NR
// TODO: Do we need to do the reverse insert thing mentioned above?
void ubus_set_nr_from_clients(struct kicking_nr *ap_list) {
dawnlog_debug_func("Entering...");
struct hostapd_sock_entry* sub;
int timeout = 1;
int has_content = 0;
list_for_each_entry(sub, &hostapd_sock_list, list)
{
if (sub->subscribed) {
struct blob_buf b = { 0 };
blob_buf_init(&b, 0);
dawn_regmem(&b);
void* nbs = blobmsg_open_array(&b, "list");
struct kicking_nr* this_ap = ap_list;
while (this_ap)
{
// Candidates in this list will be for one SSID - so match that to the hostapd socket
if (strncmp((char*)this_ap->nr_ap->ssid, sub->ssid, SSID_MAX_LEN) == 0)
{
blobmsg_add_nr(&b, this_ap->nr_ap);
has_content = 1;
}
this_ap = this_ap->next;
}
blobmsg_close_array(&b, nbs);
if (has_content)
ubus_invoke(ctx, sub->id, "rrm_nr_set", b.head, NULL, NULL, timeout * 1000);
blob_buf_free(&b);
dawn_unregmem(&b);
}
}
for (n = preferred_list; n; n = n->next_mac) {
if ((i = ap_array_get_ap(n->mac)))
blobmsg_add_nr(b_local, i);
}
blobmsg_close_array(b_local, nbs);
return 0;
}
void uloop_add_data_cbs() {