uci: add neighbor list priority options

This adds config options to define MACSs of neighbors to be on top of
each band's neighbor list.  This is important, because some clients will
limit the number of entries they use.  iOS devices, for exmample, use
only the top 6 APs.

The neighbors_11g and neighbors_11a take a list or space-separated
string of AP BSSIDs to keep at the top of each band's neighbor report.

The list is used when the neighbor report is being assembled.  Any
listed AP that is not online will not be included in that report.  When
the next report is assembled, if the AP is back online, then they are
restored to the indicated order.

There's only one list for each band.  In case of multiple SSIDs, you can
include BSSIDs for all of them, and they will be filtered by SSID when
assembling the reports.

Signed-off-by: Eneas U de Queiroz <cotequeiroz@gmail.com>
This commit is contained in:
Eneas U de Queiroz 2021-07-07 15:48:28 -03:00 committed by Polynomdivision
parent 2b1a53cddc
commit 97e5de157f
3 changed files with 105 additions and 19 deletions

View file

@ -109,6 +109,7 @@ struct probe_metric_s {
int low_rssi_val[__DAWN_BAND_MAX]; // eval_probe_metric()()
int chan_util_val[__DAWN_BAND_MAX]; // eval_probe_metric()()
int max_chan_util_val[__DAWN_BAND_MAX]; // eval_probe_metric()()
struct mac_entry_s* neighbors[__DAWN_BAND_MAX]; // ap_get_nr()
};
struct time_config_s {
@ -250,7 +251,7 @@ typedef struct client_s {
char signature[SIGNATURE_LEN]; // TODO: Never evaluated?
uint8_t ht_supported; // TODO: Never evaluated?
uint8_t vht_supported; // TODO: Never evaluated?
uint32_t freq; // TODO: Never evaluated?
uint32_t freq; // ap_get_nr()
uint8_t auth; // TODO: Never evaluated?
uint8_t assoc; // TODO: Never evaluated?
uint8_t authorized; // TODO: Never evaluated?

View file

@ -140,6 +140,58 @@ static int parse_rrm_mode(int *rrm_mode_order, const char *mode_string) {
}
static struct mac_entry_s *insert_neighbor_mac(struct mac_entry_s *head, const char* mac) {
struct mac_entry_s *new;
if (!(new = dawn_malloc(sizeof (struct mac_entry_s)))) {
fprintf(stderr, "Warning: Failed to create neighbor entry for '%s'\n", mac);
return head;
}
memset(new, 0, sizeof (struct mac_entry_s));
if (hwaddr_aton(mac, new->mac.u8) != 0) {
fprintf(stderr, "Warning: Failed to parse MAC from '%s'\n", mac);
dawn_free(new);
return head;
}
new->next_mac = head;
return new;
}
static void free_neighbor_mac_list(struct mac_entry_s *list) {
struct mac_entry_s *ptr = list;
while (list) {
ptr = list;
list = list->next_mac;
dawn_free(ptr);
}
}
static struct mac_entry_s* uci_lookup_mac_list(struct uci_option *o) {
struct uci_element *e;
struct mac_entry_s *head = NULL;
char *str;
if (o == NULL)
return NULL;
// hostapd inserts the new neigbor reports at the top of the list.
// Therefore, we must also do this backwares somewhere. Let's do it
// here instead of when sending the list through ubus.
switch (o->type) {
case UCI_TYPE_LIST:
uci_foreach_element(&o->v.list, e)
head = insert_neighbor_mac(head, e->name);
break;
case UCI_TYPE_STRING:
if (!(str = strdup (o->v.string)))
return NULL;
for (char *mac = strtok(str, " "); mac; mac = strtok(NULL, " "))
head = insert_neighbor_mac(head, mac);
free(str);
}
return head;
}
static struct uci_section *uci_find_metric_section(const char *name) {
struct uci_section *s;
struct uci_element *e;
@ -201,6 +253,7 @@ struct probe_metric_s uci_get_dawn_metric() {
.low_rssi_val = { -80, -80 },
};
struct uci_section *global_s, *band_s[__DAWN_BAND_MAX];
struct uci_option *global_neighbors = NULL, *neighbors;
if (!(global_s = uci_find_metric_section("global"))) {
if (!(global_s = uci_find_metric_section(NULL))) {
@ -230,9 +283,13 @@ struct probe_metric_s uci_get_dawn_metric() {
DAWN_SET_CONFIG_INT(ret, global_s, duration);
ret.rrm_mode_mask = parse_rrm_mode(ret.rrm_mode_order,
uci_lookup_option_string(uci_ctx, global_s, "rrm_mode"));
global_neighbors = uci_lookup_option(uci_ctx, global_s, "neighbors");
}
for (int band = 0; band < __DAWN_BAND_MAX; band++)
for (int band = 0; band < __DAWN_BAND_MAX; band++) {
band_s[band] = uci_find_metric_section(band_config_name[band]);
neighbors = band_s[band] ? uci_lookup_option(uci_ctx, band_s[band], "neighbors") : NULL;
ret.neighbors[band] = uci_lookup_mac_list(neighbors ? : global_neighbors);
}
DAWN_SET_BANDS_CONFIG_INT(ret, global_s, band_s, ap_weight);
DAWN_SET_BANDS_CONFIG_INT(ret, global_s, band_s, ht_support);
@ -379,6 +436,9 @@ int uci_init() {
}
int uci_clear() {
for (int band = 0; band < __DAWN_BAND_MAX; band++)
free_neighbor_mac_list(dawn_metric.neighbors[band]);
if (uci_pkg != NULL) {
uci_unload(uci_ctx, uci_pkg);
dawn_unregmem(uci_pkg);

View file

@ -1617,36 +1617,61 @@ int build_network_overview(struct blob_buf *b) {
}
static void blobmsg_add_nr(struct blob_buf *b_local, ap *i) {
void* nr_entry = blobmsg_open_array(b_local, NULL);
char mac_buf[20];
sprintf(mac_buf, MACSTRLOWER, MAC2STR(i->bssid_addr.u8));
blobmsg_add_string(b_local, NULL, mac_buf);
blobmsg_add_string(b_local, NULL, (char *) i->ssid);
blobmsg_add_string(b_local, NULL, i->neighbor_report);
blobmsg_close_array(b_local, nr_entry);
}
static int mac_is_in_entry_list(const struct dawn_mac mac, const struct mac_entry_s *list) {
for (const struct mac_entry_s *i = list; i; i = i->next_mac)
if (mac_is_equal_bb(i->mac, mac))
return 1;
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) {
pthread_mutex_lock(&ap_array_mutex);
ap *i;
ap *i, *own_ap;
struct mac_entry_s *preferred_list, *n;
void* nbs = blobmsg_open_array(b_local, "list");
own_ap = ap_array_get_ap(own_bssid_addr, (uint8_t*)ssid);
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;
}
pthread_mutex_lock(&ap_array_mutex);
for (i = ap_set; i != NULL; i = i->next_ap) {
if (mac_is_equal_bb(own_bssid_addr, i->bssid_addr) ||
strncmp((char *)i->ssid, ssid, SSID_MAX_LEN)) {
continue;
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);
}
}
pthread_mutex_unlock(&ap_array_mutex);
void* nr_entry = blobmsg_open_array(b_local, NULL);
char mac_buf[20];
sprintf(mac_buf, MACSTRLOWER, MAC2STR(i->bssid_addr.u8));
blobmsg_add_string(b_local, NULL, mac_buf);
blobmsg_add_string(b_local, NULL, (char *) i->ssid);
blobmsg_add_string(b_local, NULL, i->neighbor_report);
blobmsg_close_array(b_local, nr_entry);
for (n = preferred_list; n; n = n->next_mac) {
if ((i = ap_array_get_ap(n->mac, (uint8_t*)ssid)))
blobmsg_add_nr(b_local, i);
}
blobmsg_close_array(b_local, nbs);
pthread_mutex_unlock(&ap_array_mutex);
return 0;
}