Newer
Older
* cntlr.c - Multi-AP controller
* Copyright (C) 2020-2022 IOPSYS Software Solutions AB. All rights reserved.
* See LICENSE file for source code license information.
#include <1905_tlvs.h>
#include <easymesh.h>
#include <errno.h>
#include <easy/easy.h>
#include <inttypes.h>
#include <json-c/json.h>
#include <libubox/blob.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubus.h>
#include <cmdu_ackq.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "timeutils.h"
#include "acs.h"
#include "cntlr_cmdu.h"
#include "cntlr_map.h"
#include "cntlr_ubus.h"
#include "config.h"
#if (EASYMESH_VERSION >= 3)
#include "libubox/blobmsg.h"
#include "libubox/list.h"
#include "mactable.h"
#include "sta.h"
#include "stdbool.h"
#include "timer_impl.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "wifi_dataelements.h"
#include "steer.h"
#include "son.h"
extern bool waitext;
struct netif_iface *cntlr_find_iface(struct controller *c, uint8_t *macaddr)
struct macaddr_entry *entry = NULL;
entry = mactable_lookup(c->mac_table, macaddr, MAC_ENTRY_FBSS);
return NULL;
return (struct netif_iface *)entry->data;
struct netif_iface *cntlr_find_iface_type(struct controller *c, uint8_t *macaddr,
{
struct macaddr_entry *entry = NULL;
entry = mactable_lookup(c->mac_table, macaddr, type);
if (WARN_ON(!entry))
return NULL;
return (struct netif_iface *)entry->data;
}
struct netif_iface *cntlr_find_bss(struct controller *c, uint8_t *macaddr)
{
return cntlr_find_iface_type(c, macaddr, MAC_ENTRY_FBSS | MAC_ENTRY_BBSS);
}
struct netif_iface *cntlr_find_fbss(struct controller *c, uint8_t *macaddr)
{
//return cntlr_find_iface_type(c, macaddr, MAC_ENTRY_FBSS); //FIXME
struct netif_iface *iface;
iface = cntlr_find_bss(c, macaddr);
if (iface && iface->bss->is_fbss)
return iface;
return NULL;
}
struct netif_iface *cntlr_find_bbss(struct controller *c, uint8_t *macaddr)
{
// return cntlr_find_iface_type(c, macaddr, MAC_ENTRY_BBSS); //FIXME
struct netif_iface *iface;
iface = cntlr_find_bss(c, macaddr);
if (iface && iface->bss->is_bbss)
return iface;
return NULL;
}
struct netif_iface *cntlr_find_bsta(struct controller *c, uint8_t *macaddr)
{
struct netif_iface *iface;
iface = cntlr_find_bss(c, macaddr);
if (iface && iface->bss->is_bbss == false && iface->bss->is_fbss == false)
return iface;
return NULL;
int cntlr_set_iface_type(struct controller *c, uint8_t *macaddr, uint32_t type)
return mactable_set_entry_type(c->mac_table, macaddr, type);
int cntlr_get_iface_type(struct controller *c, uint8_t *macaddr, uint32_t *type)
{
return mactable_get_entry_type(c->mac_table, macaddr, type);
}
struct netif_radio *cntlr_find_radio(struct controller *c, uint8_t *macaddr)
struct macaddr_entry *entry = NULL;
entry = mactable_lookup(c->mac_table, macaddr, MAC_ENTRY_RADIO);
if (WARN_ON(!entry))
return NULL;
return (struct netif_radio *)entry->data;
struct node *cntlr_find_node(struct controller *c, uint8_t *macaddr)
struct macaddr_entry *entry = NULL;
entry = mactable_lookup(c->mac_table, macaddr, MAC_ENTRY_ALID);
return NULL;
return (struct node *)entry->data;
/* finds radio struct from interface macaddr */
struct netif_radio *cntlr_find_radio_with_bssid(struct controller *c, uint8_t *bssid)
struct node *n = NULL;
struct netif_radio *r = NULL;
struct netif_iface *p = NULL;
list_for_each_entry(n, &c->nodelist, list) {
list_for_each_entry(r, &n->radiolist, list) {
list_for_each_entry(p, &r->iflist, list) {
if (!memcmp(p->bss->bssid, bssid, 6))
return r;
}
}
}
return NULL;
}
struct netif_radio *cntlr_find_radio_in_node_by_band(struct controller *c, struct node *n,
enum wifi_band band)
{
struct netif_radio *r = NULL;
list_for_each_entry(r, &n->radiolist, list) {
if (r->radio_el->band == band)
return r;
}
return NULL;
}
struct netif_link *cntlr_find_link(struct controller *c, uint8_t *ul_macaddr, uint8_t *dl_macaddr)
list_for_each_entry(l, &c->linklist, list) {
if (!memcmp(l->upstream->bss->bssid, ul_macaddr, 6)
&& !memcmp(l->downstream->bss->bssid, dl_macaddr, 6))
return l;
}
return NULL;
}
int cntlr_get_node_assoclist(struct controller *c, uint8_t *node_alid,
int *num_sta, uint8_t *sta_macaddrs)
{
struct sta *s = NULL;
int limit = *num_sta;
int ret = 0;
int i = 0;
*num_sta = 0;
n = cntlr_find_node(c, node_alid);
if (!n)
return -1;
list_for_each_entry(s, &n->stalist, list) {
if (i < limit)
memcpy(&sta_macaddrs[i*6], s->macaddr, 6);
}
*num_sta = i;
return ret;
}
struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta, uint8_t *almacaddr)
list_for_each_entry(br, &c->bcnreqlist, list) {
if (!memcmp(br->sta_mac, sta, 6) && !memcmp(br->agent_mac, almacaddr, 6))
return br;
}
return NULL;
}
/* find node based on bssid */
struct node *cntlr_find_node_with_bssid(struct controller *c, uint8_t *bssid)
list_for_each_entry(n, &c->nodelist, list) {
struct netif_radio *r = NULL;
list_for_each_entry(r, &n->radiolist, list) {
struct netif_iface *p = NULL;
list_for_each_entry(p, &r->iflist, list) {
if (!memcmp(p->bss->bssid, bssid, 6))
}
}
}
return NULL;
}
/* returns first found node with given STA MAC on its stalist */
struct node *cntlr_find_node_with_sta(struct controller *c, uint8_t *stamacaddr)
{
struct node *n = NULL;
list_for_each_entry(n, &c->nodelist, list) {
struct sta *e = NULL;
list_for_each_entry(e, &n->stalist, list) {
if (!memcmp(e->macaddr, stamacaddr, 6))
return n;
}
}
return NULL;
}
/* returns topology json string */
char *cntlr_get_topology(struct controller *c)
{
struct json_object *jobj = json_object_new_object();
struct bh_topology_dev *n = NULL;
uint8_t root_aladdr[6] = {0};
const char *json_str = NULL;
char macbuf_if[32] = {0};
char macbuf[32] = {0};
int num_links = 0;
char *out;
list_for_each_entry(n, &c->topology.dev_list, list) {
if (n->bh.depth == 0)
memcpy(root_aladdr, n->al_macaddr, 6);
if (n->bh.parent)
num_links++;
}
if (hwaddr_is_zero(root_aladdr)) {
errno = EAGAIN;
return NULL;
}
json_object_object_add(jobj, "num_nodes", json_object_new_int(c->topology.num_devs));
json_object_object_add(jobj, "num_links", json_object_new_int(num_links));
hwaddr_ntoa(root_aladdr, macbuf);
json_object_object_add(jobj, "root", json_object_new_string(macbuf));
/* nodes */
struct json_object *jnodes = json_object_new_array();
list_for_each_entry(n, &c->topology.dev_list, list) {
hwaddr_ntoa(n->al_macaddr, macbuf);
json_object_array_add(jnodes, json_object_new_string(macbuf));
}
json_object_object_add(jobj, "nodes", jnodes);
/* links */
struct json_object *jlinks = json_object_new_array();
list_for_each_entry(n, &c->topology.dev_list, list) {
if (n->bh.parent) {
struct json_object *jlink = json_object_new_object();
hwaddr_ntoa(n->bh.parent->al_macaddr, macbuf);
hwaddr_ntoa(n->bh.parent_iface->macaddr, macbuf_if); /* is bBSS for wifi link */
json_object_object_add(jlink, "u", json_object_new_string(macbuf));
json_object_object_add(jlink, "u_iface", json_object_new_string(macbuf_if));
hwaddr_ntoa(n->al_macaddr, macbuf);
hwaddr_ntoa(n->bh.own_iface->macaddr, macbuf_if); /* is bSTA for wifi link */
json_object_object_add(jlink, "v", json_object_new_string(macbuf));
json_object_object_add(jlink, "v_iface", json_object_new_string(macbuf_if));
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
if (!!(n->bh.own_iface->media_type & 0x0100)) {
uint32_t max_thput;
int w = 0;
json_object_object_add(jlink, "f", json_object_new_int(0));
max_thput = cntlr_estimate_max_throughput_for_node(c, n->al_macaddr);
w = 100 - (float)max_thput * (float)(100) / (float)(10000); //FIXME
json_object_object_add(jlink, "w", json_object_new_int(w));
} else {
json_object_object_add(jlink, "w", json_object_new_int(0));
json_object_object_add(jlink, "f", json_object_new_int(1));
}
json_object_array_add(jlinks, jlink);
}
}
json_object_object_add(jobj, "links", jlinks);
json_str = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY);
out = strdup(json_str);
json_object_put(jobj);
cntlr_info(LOG_SON, "%s", out);
return out;
}
#if (EASYMESH_VERSION >= 6)
Jakob Olsson
committed
bool cntlr_radio_support_ap_wifi7(struct wifi7_radio_capabilities *wifi7_caps)
{
if (wifi7_caps->ap.str_support || wifi7_caps->ap.nstr_support ||
wifi7_caps->ap.emlsr_support || wifi7_caps->ap.emlmr_support)
return true;
return false;
}
Jakob Olsson
committed
bool cntlr_radio_support_bsta_wifi7(struct wifi7_radio_capabilities *wifi7_caps)
{
if (wifi7_caps->bsta.str_support || wifi7_caps->bsta.nstr_support ||
wifi7_caps->bsta.emlsr_support || wifi7_caps->bsta.emlmr_support)
return true;
return false;
}
Jakob Olsson
committed
bool cntlr_node_support_ap_wifi7(struct node *n)
{
struct netif_radio *r = NULL;
Jakob Olsson
committed
list_for_each_entry(r, &n->radiolist, list) {
if (cntlr_radio_support_ap_wifi7(&r->wifi7_caps))
return true;
}
return false;
}
#endif
int cntlr_update_txlink_metric_data(struct controller *c, struct tlv_tx_linkmetric *txl, int len)
{
int i = 0;
int size = 0;
struct tx_link_info *txlinfo;
struct netif_link *txlink;
struct link_metrics *metrics;
/* For each tx link in the mesg */
size = len - (sizeof(struct tlv_tx_linkmetric));
size = size / (sizeof(struct tx_link_info));
for (i = 0; i < size; i++) {
txlinfo = (struct tx_link_info *)&txl->link[i];
/* Find or alloc the backhaul link to the controller */
txlink = cntlr_alloc_link(c, txlinfo->local_macaddr, txlinfo->neighbor_macaddr);
if (!txlink) {
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
return -1;
}
metrics = txlink->metrics;
metrics->l = txlink;
memcpy(metrics->l->upstream->bss->bssid, txlinfo->local_macaddr, 6);
memcpy(metrics->l->downstream->bss->bssid, txlinfo->neighbor_macaddr, 6);
metrics->type = BUF_GET_BE16(txlinfo->mediatype);
metrics->bridge = txlinfo->has_bridge;
metrics->packet_tx_error = BUF_GET_BE32(txlinfo->errors);
metrics->packet_trans = BUF_GET_BE32(txlinfo->packets);
metrics->thp = BUF_GET_BE16(txlinfo->max_throughput);
metrics->link_av = BUF_GET_BE16(txlinfo->availability);
metrics->phy_rate = BUF_GET_BE16(txlinfo->phyrate);
}
c->num_tx_links += size;
return 0;
}
int cntlr_update_rxlink_metric_data(struct controller *c, struct tlv_rx_linkmetric *rxl, int len)
{
int i = 0;
int size = 0;
struct rx_link_info *rxlinfo;
struct netif_link *rxlink;
struct link_metrics *metrics;
/* For each rx link in the mesg */
size = len - (sizeof(struct tlv_rx_linkmetric));
size = size / (sizeof(struct rx_link_info));
for (i = 0; i < size; i++) {
rxlinfo = (struct rx_link_info *)&rxl->link[i];
/* Find or alloc the backhaul link to the controller */
rxlink = cntlr_alloc_link(c, rxlinfo->local_macaddr, rxlinfo->neighbor_macaddr);
if (!rxlink) {
return 0;
}
metrics = rxlink->metrics;
metrics->l = rxlink;
memcpy(metrics->l->upstream->bss->bssid, rxlinfo->local_macaddr, 6);
memcpy(metrics->l->downstream->bss->bssid, rxlinfo->neighbor_macaddr, 6);
metrics->type = BUF_GET_BE16(rxlinfo->mediatype);
metrics->packet_rec = BUF_GET_BE32(rxlinfo->packets);
metrics->packet_rx_error = BUF_GET_BE32(rxlinfo->errors);
metrics->rssi = rxlinfo->rssi;
}
c->num_rx_links += size;
return 0;
}
struct node *cntlr_add_node(struct controller *c, uint8_t *almacaddr)
struct cmdu_buff *cmdu;
if (!hwaddr_ntoa(almacaddr, mac_str))
n = cntlr_find_node(c, almacaddr);
n = cntlr_alloc_node(c, almacaddr);
err("%s: failed to allocate node "MACFMT"\n",
__func__, MAC2STR(almacaddr));
} else {
return n;
}
ret = cntlr_config_add_node(&c->cfg, mac_str);
if (!ret) {
dbg("|%s:%d| resync config\n", __func__, __LINE__);
cntlr_resync_config(c, true);
}
/* update the timestamp of the node */
if (n)
timestamp_update(&n->last_tsp_seen);
if (!hwaddr_equal(c->almacaddr, almacaddr))
cntlr_sync_dyn_controller_config(c, almacaddr);
cntlr_qos_sync_node(c, n->almacaddr);
cmdu = cntlr_gen_bk_caps_query(c, n->almacaddr);
if (!cmdu)
return n;
send_cmdu(c, cmdu);
cmdu_free(cmdu);
cmdu = cntlr_gen_ap_capability_query(c, n->almacaddr);
if (!cmdu)
return n;
send_cmdu(c, cmdu);
cmdu_free(cmdu);
int i = 0;
list_for_each_entry(n, &c->nodelist, list) {
cntlr_dbg(LOG_MISC,
" %d | agent = %p, hwaddr = '"MACFMT"')\n",
i++, n, MAC2STR(n->almacaddr));
void cntlr_send_max_wifi_bh_hops_policy(struct controller *c)
struct node *node = NULL;
if (c->cfg.max_node_bh_hops == 0)
return;
trace("%s: max_node_bh_hops %d\n",
__func__, c->cfg.max_node_bh_hops);
list_for_each_entry(node, &c->nodelist, list) {
enum bh_control {
BLOCK = 0x00,
UNBLOCK = 0x01
} bh_control = UNBLOCK;
struct bh_topology_dev *topo_dev = topology_find_device(&c->topology, node->almacaddr);
struct netif_radio *radio = NULL;
if (!topo_dev) {
err("%s: device not found in bh topo model, logical error.\n", __func__);
continue;
}
if (topo_dev->bh.depth == UNKNOWN_TREE_LEVEL) {
warn("%s: Level in BH treee unknown for: " MACFMT "\n", __func__, MAC2STR(node->almacaddr));
if (c->cfg.max_node_bh_hops <= topo_dev->bh.wifi_hops_from_root)
bh_control = BLOCK;
list_for_each_entry(radio, &node->radiolist, list) {
list_for_each_entry(iface, &radio->iflist, list) {
if (iface->bss->is_bbss) {
const uint8_t NO_VALIDITY_PERIOD = 0;
uint8_t ALL_BSTAS[6] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
uint16_t mid;
trace("%s: sending BH assoc_mode %s, node al_mac: " MACFMT
", wifi_hops_from_root: %d, bssid: " MACFMT"\n",
__func__,
bh_control ? "UNBLOCK" : "BLOCK",
MAC2STR(node->almacaddr),
topo_dev->bh.wifi_hops_from_root,
MAC2STR(iface->bss->bssid));
cntlr_send_client_assoc_ctrl_request(
c, node->almacaddr, iface->bss->bssid,
bh_control, NO_VALIDITY_PERIOD, 1,
ALL_BSTAS, &mid);
}
}
}
}
}
void cntlr_bcn_metrics_timer_cb(atimer_t *t)
{
struct sta *s = container_of(t, struct sta, bcn_metrics_timer);
struct controller *c = s->cntlr;
cntlr_dbg(LOG_STA, "%s: No Beacon Report from STA " MACFMT" yet.\n",
if (!s->bcn_response_tmo) {
s->bcn_response_tmo = true;
timer_set(&s->bcn_metrics_timer, 30 * 1000);
cntlr_dbg(LOG_STA, "%s: Wait for 30s more.\n", __func__);
} else {
cntlr_del_bcnreq(c, s->macaddr, s->agent_almacaddr);
s->bcn_response_tmo = false;
}
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
static void cntlr_freeze_sta(struct controller *c, struct sta *s)
{
timer_del(&s->bcn_metrics_timer);
timer_del(&s->btm_req_timer);
timer_del(&s->ageout_timer);
sta_free_assoc_frame(s);
sta_free_bcn_metrics(s);
cntlr_clean_bcnreqlist_sta(c, s);
}
static void cntlr_remove_sta(struct controller *c, struct sta *s)
{
struct node *n = NULL;
do {
n = cntlr_find_node_with_sta(c, s->macaddr);
if (n)
node_del_sta(n, s);
} while (n);
cntlr_freeze_sta(c, s);
cntlr_del_sta(c->sta_table, s);
}
void cntlr_sta_ageout_timer_cb(atimer_t *t)
{
trace("%s: --->\n", __func__);
struct sta *s = container_of(t, struct sta, ageout_timer);
struct controller *c = s->cntlr;
if (!c)
return;
cntlr_dbg(LOG_STA, "%s: Delete STA " MACFMT " after %ds of disassociation\n",
__func__, MAC2STR(s->macaddr), c->cfg.stale_sta_timeout);
cntlr_remove_sta(c, s);
}
int node_add_sta(struct node *n, struct sta *s)
if (WARN_ON(node_find_sta(n, s->macaddr))) {
cntlr_dbg(LOG_STA,
"%s: Warn! STA " MACFMT " already in node " MACFMT"\n",
__func__, MAC2STR(s->macaddr), MAC2STR(n->almacaddr));
memcpy(s->agent_almacaddr, n->almacaddr, 6);
list_add(&s->list, &n->stalist);
n->sta_count++;
info("%s: Client " MACFMT " attached to node " MACFMT "\n",
__func__, MAC2STR(s->macaddr), MAC2STR(n->almacaddr));
int node_del_sta(struct node *n, struct sta *s)
if (WARN_ON(!node_find_sta(n, s->macaddr))) {
cntlr_dbg(LOG_STA,
"%s: Warn! STA " MACFMT " not in node " MACFMT"\n",
__func__, MAC2STR(s->macaddr), MAC2STR(n->almacaddr));
list_del(&s->list);
n->sta_count--;
struct sta *node_find_sta(struct node *n, uint8_t *macaddr)
{
struct sta *s = NULL;
list_for_each_entry(s, &n->stalist, list) {
if (!memcmp(s->macaddr, macaddr, 6))
return s;
}
return NULL;
}
void node_update_stalist(struct node *n, uint8_t *stalist, int num)
{
struct sta *s = NULL, *tmp;
list_for_each_entry_safe(s, tmp, &n->stalist, list) {
bool found = false;
for (int i = 0; i < num; i++) {
if (!memcmp(s->macaddr, &stalist[i*6], 6)) {
found = true;
break;
}
}
if (!found) {
list_del(&s->list);
n->sta_count--;
struct unassoc_sta_metrics *cntlr_find_usta_metric(struct controller *c,
struct sta *s,
uint8_t *agent_macaddr)
struct unassoc_sta_metrics *u = NULL;
list_for_each_entry(u, &s->de_sta->umetriclist, list) {
if (!memcmp(u->agent_macaddr, agent_macaddr, 6))
return u;
}
struct cmdu_buff *cntlr_query_sta_metric(struct controller *c, struct sta *s)
{
if (!c || !s)
return NULL;
if (hwaddr_is_zero(s->agent_almacaddr)) {
cntlr_warn(LOG_STA, "%s: Agent macaddr is zero\n", __func__);
return cntlr_gen_sta_metric_query(c, s->agent_almacaddr, s->macaddr);
static void cntlr_get_all_sta_metrics(struct controller *c)
list_for_each_entry(n, &c->nodelist, list) {
struct sta *s = NULL;
list_for_each_entry(s, &n->stalist, list) {
struct cmdu_buff *cmdu;
cmdu = cntlr_query_sta_metric(c, s);
if (!cmdu)
continue;
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
struct wifi_bss_element *cntlr_alloc_wifi_bss(struct controller *c,
uint8_t *macaddr)
{
struct wifi_bss_element *bss = NULL;
bss = calloc(1, sizeof(struct wifi_bss_element));
if (!bss)
return NULL;
memcpy(bss->bssid, macaddr, 6);
bss->sta_assoc_allowed = true;
return bss;
}
struct netif_iface *cntlr_radio_add_iface(struct controller *c,
struct netif_radio *r,
n = cntlr_find_iface(c, macaddr);
n->bss->enabled = true;
n = calloc(1, sizeof(*n));
if (!n)
return NULL;
n->bss = cntlr_alloc_wifi_bss(c, macaddr);
n->band = wifi_opclass_get_band(r->radio_el->cur_opclass.opclass[0].id);
if (n->band == BAND_UNKNOWN) {
int ret;
ret = cntlr_config_add_node_radio(&c->cfg, r->agent->almacaddr, r->radio_el->macaddr, n->band);
if (!ret)
cntlr_resync_config(c, true);
}
n->bss->is_fbss = true;
n->bss->is_bbss = false;
n->bss->enabled = true;
list_add(&n->list, &r->iflist);
n->agent = r->agent;
#if (EASYMESH_VERSION >= 6)
/* send channel selection request to propagate puncture bitmap */
cntlr_send_channel_selection(c, r->agent->almacaddr, r->radio_el->macaddr, NULL);
mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_FBSS, (void *)n);
static struct wifi_radio_element *cntlr_alloc_wifi_radio(struct controller *c, struct node *n,
struct netif_radio *r)
struct wifi_radio_element *radio_el = NULL;
radio_el = calloc(1, sizeof(struct wifi_radio_element));
if (!radio_el)
return NULL;
INIT_LIST_HEAD(&radio_el->scanlist);
radio_el->acs = cntlr_radio_acs_alloc(c, n, r);
struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n,
r = cntlr_find_radio(c, macaddr);
r = calloc(1, sizeof(*r));
if (!r)
return NULL;
r->radio_el = cntlr_alloc_wifi_radio(c, n, r);
if (!r->radio_el) {
free(r);
return NULL;
}
INIT_LIST_HEAD(&r->iflist);
list_add(&r->list, &n->radiolist);
r->agent = n;
memcpy(r->radio_el->macaddr, macaddr, 6);
mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_RADIO, (void *)r);
cntlr_send_channel_selection(c, n->almacaddr, macaddr, NULL);
p = cntlr_get_radio_policy(&c->cfg, macaddr);
if (p)
r->radio_el->band = p->band;
int cntlr_radio_get_beacon_channel(struct wifi_radio_element *radio,
uint8_t *opclass, uint8_t *channel)
{
struct wifi_radio_opclass *curr = &radio->cur_opclass;
*channel = 0;
*opclass = 0;
for (int i = 0; i < curr->num_opclass; i++) {
struct wifi_radio_opclass_entry *e = &curr->opclass[i];
if (e->bandwidth == 20) {
*opclass = e->id;
*channel = e->channel[0].channel;
return 0;
}
}
return -1;
}
struct node *cntlr_alloc_node(struct controller *c, uint8_t *almacaddr)
n = calloc(1, sizeof(struct node));
if (!n) {
n->cntlr = c;
n->depth = -1;
n->scan_supported = true;
n->np = NULL; //c->cfg.apolicy;
memcpy(n->almacaddr, almacaddr, 6);
n->map_profile = MULTIAP_PROFILE_1;
INIT_LIST_HEAD(&n->radiolist);
list_add(&n->list, &c->nodelist);
mactable_add_entry(c->mac_table, almacaddr, MAC_ENTRY_ALID, (void *)n);
info("%s: New node " MACFMT " added\n", __func__, MAC2STR(almacaddr));
uint32_t cntlr_estimate_max_throughput_for_node(struct controller *c, uint8_t *node_almacaddr)
{
const struct backhaul_info *b = NULL;
const struct bh_topology_dev *dev;
uint32_t est_thput = 0xffffffff;
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
dev = topology_find_device(&c->topology, node_almacaddr);
if (!dev)
return est_thput;
while (dev->bh.parent) {
b = &dev->bh;
if (b->own_iface) {
bool is_wifi = !!(b->own_iface->media_type & 0x0100);
struct sta *s = NULL;
if (!is_wifi) {
dev = b->parent;
continue;
}
s = cntlr_find_sta(c->sta_table, (uint8_t *)b->own_iface->macaddr);
if (s && s->is_bsta && s->de_sta) {
if (s->de_sta->dl_est_thput < est_thput)
est_thput = s->de_sta->dl_est_thput;
}
}
dev = b->parent;
}
if (est_thput != 0xffffffff)
est_thput /= 2; /* TODO: MLO link */
return est_thput;
}
void cntlr_clean_bcnreqlist(struct controller *c)
{
struct bcnreq *b = NULL, *tmp;
list_for_each_entry_safe(b, tmp, &c->bcnreqlist, list) {
list_del(&b->list);
free(b);
}
}
void cntlr_clean_bcnreqlist_sta(struct controller *c, struct sta *s)
{
struct bcnreq *b = NULL, *tmp;
list_for_each_entry_safe(b, tmp, &c->bcnreqlist, list) {
if (!memcmp(b->sta_mac, s->macaddr, 6)) {
dbg("%s Remove Beacon Request for STA " MACFMT "\n",
__func__, MAC2STR(b->sta_mac));
list_del(&b->list);
free(b);
}
}
}
void cntlr_clean_linklist(struct controller *c)
{
struct netif_link *l = NULL, *tmp;
list_for_each_entry_safe(l, tmp, &c->linklist, list) {
free(l->metrics);
list_del(&l->list);
free(l);
}
static void radio_clean_iflist(struct controller *c, struct netif_radio *r)
struct netif_link *l = NULL, *temp;
list_for_each_entry_safe(ni, tmp, &r->iflist, list) {
/* Here we need to clean the linklist */
list_for_each_entry_safe(l, temp, &c->linklist, list) {
if (l->upstream == ni || l->downstream == ni) {
free(l->metrics);
list_del(&l->list);
free(l);
}
}
mactable_del_entry(c->mac_table, ni->bss->bssid, MAC_ENTRY_FBSS);
list_del(&ni->list);
free(ni);
}
}
static void radio_clean_radio_el(struct netif_radio *r)
{
struct wifi_scanres_element *b = NULL, *tmp;
list_for_each_entry_safe(b, tmp, &r->radio_el->scanlist, list) {
cntlr_radio_clean_scanlist_el(b);
}
cntlr_radio_acs_free(r->radio_el->acs);
r->radio_el->acs = NULL;
free(r->radio_el);
}
static void cntlr_clean_radiolist(struct controller *c, struct node *n)
{
struct netif_radio *r = NULL, *tmp;
list_for_each_entry_safe(r, tmp, &n->radiolist, list) {
mactable_del_entry(c->mac_table, r->radio_el->macaddr, MAC_ENTRY_RADIO);
radio_clean_radio_el(r);
list_del(&r->list);
free(r);
}
}
static void node_clean_stalist(struct controller *c, struct node *n)
{
struct sta *s = NULL, *tmp;
if (!c || !n)
return;
list_for_each_entry_safe(s, tmp, &n->stalist, list) {
if (WARN_ON(n->sta_count != 0)) {
dbg("%s: Misalligned station counter (cnt=%d)!\n",
__func__, n->sta_count);
static void cntlr_clean_mac_hashtable(struct controller *c)
{
mactable_flush(c->mac_table);
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
static void cntlr_clean_all_sta(struct controller *c)
{
struct hlist_node *tmp = NULL;
int i;
for (i = 0; i < MAC_HASHTABLE_SIZE; i++) {
struct sta *s = NULL;
if (hlist_empty(&c->sta_table[i]))
continue;
hlist_for_each_entry_safe(s, tmp, &c->sta_table[i], hlist) {
hlist_del(&s->hlist, &c->sta_table[i]);
cntlr_freeze_sta(c, s);
cntlr_free_sta(s);
}
}
c->num_sta = 0;
}
static void cntlr_clean_nodelist(struct controller *c)
{
struct node *n = NULL, *tmp;
list_for_each_entry_safe(n, tmp, &c->nodelist, list) {
cntlr_clean_radiolist(c, n);
struct netif_link *cntlr_alloc_link(struct controller *c,
uint8_t *upstream, uint8_t *downstream)
{
struct netif_link *l;
l = cntlr_find_link(c, upstream, downstream);
if (l)
return l;
l = calloc(1, sizeof(struct netif_link));
if (!l)
return NULL;
l->metrics = calloc(1, sizeof(struct link_metrics));
if (!l->metrics)
l->upstream = cntlr_find_iface(c, upstream);
l->downstream = cntlr_find_iface(c, downstream);
MAC2STR(l->upstream->bss->bssid),
MAC2STR(l->downstream->bss->bssid));
list_add(&l->list, &c->linklist);
return l;
out_metrics:
free(l->metrics);
out:
free(l);
static void cntlr_radar_exit(atimer_t *t)
{
/*TODO: before change channel due to radar, save old chandef.
* Restore that chandef upon exit from radar nop.
*/
}
static void cntlr_ieee1905_cmdu_event_handler(void *cntlr,
struct blob_attr *msg)
static const struct blobmsg_policy cmdu_attrs[6] = {
[0] = { .name = "type", .type = BLOBMSG_TYPE_INT16 },
[1] = { .name = "mid", .type = BLOBMSG_TYPE_INT16 },
[2] = { .name = "ifname", .type = BLOBMSG_TYPE_STRING },
[3] = { .name = "source", .type = BLOBMSG_TYPE_STRING },
[4] = { .name = "origin", .type = BLOBMSG_TYPE_STRING },
[5] = { .name = "cmdu", .type = BLOBMSG_TYPE_STRING },
struct controller *c = (struct controller *)cntlr;
char in_ifname[16] = {0};
struct blob_attr *tb[6];
char src[18] = { 0 }, src_origin[18] = { 0 };
uint8_t *tlv = NULL;
char *tlvstr = NULL;
uint16_t type;
uint8_t srcmac[6], origin[6];
uint16_t mid = 0;
int len = 0;
sigset_t waiting_mask;
sigpending(&waiting_mask);
if (sigismember(&waiting_mask, SIGINT) ||
sigismember(&waiting_mask, SIGTERM))
return;
blobmsg_parse(cmdu_attrs, 6, tb, blob_data(msg), blob_len(msg));
if (!tb[0] || !tb[1])
return;
if (tb[0]) {
int t;
t = blobmsg_get_u16(tb[0]);
if (t < 0)
return;
type = (uint16_t)t;
if (!is_cmdu_for_us(c, type))
return;
}
if (tb[1])
mid = (uint16_t)blobmsg_get_u16(tb[1]);
strncpy(in_ifname, blobmsg_data(tb[2]), 15);
if (tb[3]) {
strncpy(src, blobmsg_data(tb[3]), 17);
hwaddr_aton(src, srcmac);
}
if (tb[4]) {
strncpy(src_origin, blobmsg_data(tb[4]), 17);
hwaddr_aton(src_origin, origin);
}
if (tb[5]) {
len = blobmsg_data_len(tb[5]) - 16;
tlvstr = calloc(1, len + 1);
if (!tlvstr)
return;
strncpy(tlvstr, (blobmsg_data(tb[5]) + 16), len);
len = (len - 1) / 2;
tlv = calloc(1, len);
if (!tlv) {
free(tlvstr);
return;
strtob(tlvstr, len, tlv);
free(tlvstr);
cntlr_handle_map_event(c, type, mid, in_ifname, srcmac, origin, tlv, len);
if (tlv)
free(tlv);
static void cntlr_query_nodes(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, query_nodes);
struct node *n = NULL;
list_for_each_entry(n, &c->nodelist, list) {
struct cmdu_buff *cmdu;
cmdu = cntlr_gen_bk_caps_query(c, n->almacaddr);
if (cmdu) {
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
cmdu = cntlr_gen_ap_capability_query(c, n->almacaddr);
if (cmdu) {
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
cmdu = cntlr_gen_topology_query(c, n->almacaddr);
if (cmdu) {
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
}
timer_set(&c->query_nodes, 60 * 1000);
bool cntlr_check_config_diff(struct controller *c, uint32_t diff)
{
bool reloaded = false;
if (diff & CONFIG_DIFF_CREDENTIALS || diff & CONFIG_DIFF_VLAN) {
struct cmdu_buff *cmdu;
uint8_t origin[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x13};
if (diff & CONFIG_DIFF_CREDENTIALS)
info("%s: credentials changed!\n", __func__);
if (diff & CONFIG_DIFF_VLAN)
info("%s: traffic separation toggled!\n", __func__);
cmdu = cntlr_gen_ap_autoconfig_renew(c, origin);
if (cmdu) {
dbg("%s: Sending autoconfig renew to " MACFMT "\n",
__func__, MAC2STR(origin));
send_cmdu(c, cmdu);
cmdu_free(cmdu);
reloaded = true;
}
} else if (diff & (CONFIG_DIFF_AGENT_POLICY | CONFIG_DIFF_AGENT_POLICY_CNT)) {
struct node_policy *p = NULL;
/* send the policy config cmdu to the marked agent */
list_for_each_entry(n, &c->nodelist, list) {
struct cmdu_buff *cmdu;
int num_bk = 0;
uint8_t bk_id[16 * 6] = {0};
int num_radio = 0;
if ((diff & CONFIG_DIFF_AGENT_POLICY) && !n->np->is_policy_diff)
continue;
list_for_each_entry(r, &n->radiolist, list) {
struct netif_iface *iface = NULL;
memcpy(&radio_id[num_radio * 6],
num_radio++;
list_for_each_entry(iface, &r->iflist, list) {
if (!iface->bss->is_bbss)
continue;
memcpy(&bk_id[num_bk * 6],
iface->bss->bssid, 6);
num_bk++;
}
}
cmdu = cntlr_gen_policy_config_req(c, n->almacaddr,
n->np, num_radio, radio_id, num_bk,
bk_id);
if (cmdu) {
send_cmdu(c, cmdu);
cmdu_free(cmdu);
reloaded = true;
}
}
/* reset is_policy_diff to false; */
p->is_policy_diff = false;
}
#if (EASYMESH_VERSION > 2)
if ((diff & CONFIG_DIFF_QOS) && c->cfg.qos.enabled) {
struct node *n = NULL;
cntlr_dbg(LOG_QOS, "%s: qos config changed\n", __func__);
/* send the policy config cmdu to the marked agent */
list_for_each_entry(n, &c->nodelist, list) {
cntlr_qos_sync_node(c, n->almacaddr);
#if (EASYMESH_VERSION >= 6)
if ((diff & CONFIG_DIFF_AP_MLD) || (diff & CONFIG_DIFF_PUNCT_BITMAP)) {
struct node *n = NULL;
/* send the ap mld config cmdu to the agent */
list_for_each_entry(n, &c->nodelist, list)
cntlr_send_ap_mld_configuration_request(c, n);
}
if (diff & CONFIG_DIFF_BSTA_MLD) {
struct node *n = NULL;
/* send the ap mld config cmdu to the agent */
list_for_each_entry(n, &c->nodelist, list)
cntlr_send_bsta_mld_configuration_request(c, n);
}
return reloaded;
}
#ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
int cntlr_sync_dyn_controller_config(struct controller *c, uint8_t *agent)
{
struct node *n = NULL;
uint8_t proto = 0xab;
struct cmdu_buff *cmdu;
if (agent && !hwaddr_is_zero(agent)) {
cmdu = cntlr_gen_higher_layer_data(c, agent, proto, NULL, 0);
if (!cmdu)
return -1;
send_cmdu(c, cmdu);
cmdu_free(cmdu);
} else {
list_for_each_entry(n, &c->nodelist, list) {
if (hwaddr_equal(c->almacaddr, n->almacaddr))
continue; /* skip locally running agent */
cmdu = cntlr_gen_higher_layer_data(c, n->almacaddr, proto, NULL, 0);
if (!cmdu)
return -1;
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
}
return 0;
}
bool cntlr_resync_config(struct controller *c, bool reload)
diff = cntlr_config_reload(&c->cfg);
list_for_each_entry(np, &c->cfg.nodelist, list) {
struct node *n;
if (n)
n->np = np;
if (reload)
cntlr_check_config_diff(c, diff);
#ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
/* in dyn-controller mode, sync controller's config in network */
if (diff)
cntlr_sync_dyn_controller_config(c, NULL);
return !!diff;
static void cntlr_signal_periodic_run(atimer_t *t)
struct controller *c = container_of(t, struct controller, signal_handler);
sigset_t waiting_mask;
sigpending(&waiting_mask);
if (sigismember(&waiting_mask, SIGHUP)) {
cntlr_info(LOG_TIMER, "%s: Received SIGHUP, reload config\n", __func__);
signal(SIGHUP, SIG_IGN);
cntlr_resync_config(c, true);
timer_set(&c->signal_handler, 1 * 1000);
static void cntlr_ageout_nodes(struct controller *c)
{
struct node *n = NULL, *tmp;
struct netif_radio *r = NULL, *temp;
/* Here we need to see that all nodes in nodelist */
/* and check there timestamp */
list_for_each_entry_safe(n, tmp, &c->nodelist, list) {
if (timestamp_expired(&n->last_tsp_seen, NODE_EXPIRE_TIME * 1000)) {
list_for_each_entry_safe(r, temp, &n->radiolist, list) {
cntlr_config_del_radio(n->almacaddr);
cntlr_config_del_node(n->almacaddr);
topology_remove_device(&c->topology, n->almacaddr);
cntlr_clean_radiolist(c, n);
mactable_del_entry(c->mac_table, n->almacaddr, MAC_ENTRY_ALID);
info("%s: Node " MACFMT " removed due to ageout\n",
__func__, MAC2STR(n->almacaddr));
list_del(&n->list);
free(n);
if (son /* && c->son_enabled */)
timer_set(&c->son_timer, 0);
static void cntlr_renew_nodes_configuration(struct controller *c)
{
trace("%s: --->\n", __func__);
struct node *node = NULL;
/* When at least one running node was configured with older config, */
/* send autoconfig renew */
list_for_each_entry(node, &c->nodelist, list) {
if (!timestamp_invalid(&node->last_config) &&
timestamp_less_than(&node->last_config, &c->cfg.last_change) &&
timestamp_greater_than(&node->last_cmdu, &c->cfg.last_change)) {
uint8_t multicast_addr[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x13};
struct cmdu_buff *cmdu =
cntlr_gen_ap_autoconfig_renew(c, multicast_addr);
info("%s: Renewing configuration of " MACFMT "\n",
__func__, MAC2STR(node->almacaddr));
if (cmdu) {
dbg("%s: Sending autoconfig renew to " MACFMT "\n",
__func__, MAC2STR(multicast_addr));
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
break;
}
#if (EASYMESH_VERSION >= 6)
else if (timestamp_less_than(&node->last_apmld_ack, &c->cfg.last_apmld_change) &&
timestamp_greater_than(&node->last_cmdu, &c->cfg.last_apmld_change)) {
dbg("Found possibly unconfigured AP MLD node: "MACFMT"\n", MAC2STR(node->almacaddr));
cntlr_send_ap_mld_configuration_request(c, node);
} else if (timestamp_less_than(&node->last_bstamld_ack, &c->cfg.last_bstamld_change) &&
timestamp_greater_than(&node->last_cmdu, &c->cfg.last_bstamld_change)) {
dbg("Found possibly unconfigured BSTA MLD node: "MACFMT"\n", MAC2STR(node->almacaddr));
cntlr_send_bsta_mld_configuration_request(c, node);
}
#endif
static void combined_link_metric_periodic_collection(struct controller *c)
{
trace("%s: --->\n", __func__);
uint8_t *radiolist = NULL, *bsslist = NULL;
struct cmdu_buff *cmdu;
struct node *n = NULL;
/* AP metrics query for each agent */
/* For each agent */
list_for_each_entry(n, &c->nodelist, list) {
uint8_t *new_radiolist;
struct netif_radio *r = NULL;
int num_bss = 0, num_radio = 0;
uint8_t hwaddr[6];
num_radio = 0;
num_bss = 0;
memcpy(hwaddr, n->almacaddr, 6);
list_for_each_entry(r, &n->radiolist, list) {
struct netif_iface *bss = NULL;
int radio_index;
/* Building a radiolist of all radios */
new_radiolist = (uint8_t *)realloc(radiolist,
6 * (num_radio + 1) * sizeof(uint8_t));
if (!new_radiolist) {
radiolist = new_radiolist;
num_radio++;
radio_index = (num_radio - 1) * 6;
memcpy(radiolist + radio_index, r->radio_el->macaddr, 6);
list_for_each_entry(bss, &r->iflist, list) {
int bss_index;
uint8_t *new_bsslist;
if (!bss->bss->is_fbss && !bss->bss->is_bbss)
/* if bss is a bsta */
/* Building a bsslist of all BSS */
new_bsslist = (uint8_t *)realloc(bsslist,
6 * (num_bss + 1) * sizeof(uint8_t));
if (!new_bsslist) {
bsslist = new_bsslist;
num_bss++;
bss_index = (num_bss - 1) * 6;
memcpy(bsslist + bss_index, bss->bss->bssid, 6);
if (num_bss > 0) {
cmdu = cntlr_gen_ap_metrics_query(c, hwaddr, num_bss, bsslist, num_radio, radiolist);
if (!cmdu) {
goto error;
}
send_cmdu(c, cmdu);
cmdu_free(cmdu);
} else {
dbg("Skip sending AP metrics query, no BSS to query\n");
}
cmdu = cntlr_gen_1905_link_metric_query(c, n->almacaddr);
warn("cmdu cntlr_gen_1905_link_metric_query failed!\n");
goto error;
}
send_cmdu(c, cmdu);
cmdu_free(cmdu);
}
/* query i1905d base CMDU */
cmdu = ieee1905_ubus_buildcmdu(c->ubus_ctx, CMDU_TYPE_LINK_METRIC_RESPONSE);
if (!cmdu)
warn("No response from stack when generating 0x%04x\n",
CMDU_TYPE_LINK_METRIC_RESPONSE);
n = cntlr_find_node(c, c->almacaddr);
handle_link_metrics_response(c, cmdu, n);
cmdu_free(cmdu);
error:
if (radiolist)
free(radiolist);
if (bsslist)
free(bsslist);
static void cntlr_metric_collection(struct controller *c)
//forall_node_get_fhinfo(c); /* replaced from per-node refresh bss */
cntlr_get_all_sta_metrics(c);
/* TODO: */
//forall_node_get_usta_metrics(c);
//forall_node_update_neighbors(c);
/* Call AP metrics query and 1905 link metrics query for data collection */
combined_link_metric_periodic_collection(c);
cntlr_renew_nodes_configuration(c);
static void cntlr_acs_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, acs);
/* Run ACS recalc here */
dbg("acs timeout - run recalc\n");
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
static void cntlr_steer_sched_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, steer_sched_timer);
int i;
for (i = 0; i < c->inform_sta_num; i++) {
struct sta *s = cntlr_find_sta(c->sta_table, &c->inform_stalist[i * 6]);
if (!s)
continue;

Anjan Chanda
committed
if (c->cfg.steer.enable_sta_steer || c->cfg.steer.enable_bsta_steer)
cntlr_inform_steer_modules(c, s, c->inform_cmdu_type);
}
}
static void cntlr_son_timer_cb(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, son_timer);
if (!c->son || !c->son_doit) {
cntlr_dbg(LOG_SON, "%s: SON plugin not available\n", __func__);
return;
}
if (c->topology.num_devs < 3)
return;
ret = c->son_doit(c, NULL, cntlr_son_response_cb);
if (ret == -1 && errno == EAGAIN) {
cntlr_dbg(LOG_SON, "%s: -- %d --\n", __func__, __LINE__);
timer_set(&c->son_timer, 2000);
}
static void cntlr_start(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, start_timer);
if (c->state == CNTLR_INIT) {
c->state = CNTLR_START;
cntlr_publish_object(c, "map.controller");
//cntlr_publish_dbg_object(c, "map.controller.dbg"); //TODO: open
static void cntlr_discovery(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, discovery_timer);
struct cmdu_buff *cmdu;
cmdu = cntlr_gen_ap_autoconfig_search(c, 0x02, 0x00);
if (!cmdu)
return;
c->mid_2g = send_cmdu(c, cmdu);
cmdu = cntlr_gen_ap_autoconfig_search(c, 0x02, 0x01);
if (!cmdu)
return;
c->mid_5g = send_cmdu(c, cmdu);
int cntlr_map_sub_cb(void *bus, void *priv, void *data)
struct blob_attr *msg = (struct blob_attr *)data;
char *str;
str = blobmsg_format_json(msg, true);
trace("Received notification '%s'\n", str);
cntlr_ieee1905_cmdu_event_handler(priv, msg);
return 0;
}
int cntlr_map_del_cb(void *bus, void *priv, void *data)
struct controller *c = (struct controller *)priv;
uint32_t *obj = (uint32_t *)data;
c->subscribed = false;
fprintf(stdout, "Object 0x%x no longer present\n", *obj);
static int cntlr_subscribe_for_cmdus(struct controller *c)
mapmodule_cmdu_mask_t cmdu_mask = {0};
map_prepare_cmdu_mask(cmdu_mask,
CMDU_TYPE_TOPOLOGY_DISCOVERY,
CMDU_TYPE_TOPOLOGY_NOTIFICATION,
CMDU_TYPE_TOPOLOGY_QUERY,
CMDU_TYPE_TOPOLOGY_RESPONSE,
CMDU_TYPE_VENDOR_SPECIFIC,
CMDU_TYPE_AP_AUTOCONFIGURATION_SEARCH,
CMDU_TYPE_AP_AUTOCONFIGURATION_RESPONSE,
CMDU_TYPE_AP_AUTOCONFIGURATION_WSC,
CMDU_1905_ACK,
CMDU_BEACON_METRICS_RESPONSE,
CMDU_AP_METRICS_RESPONSE,
CMDU_ASSOC_STA_LINK_METRICS_RESPONSE,
CMDU_UNASSOC_STA_LINK_METRIC_RESPONSE,
CMDU_CHANNEL_SCAN_REQUEST,
CMDU_CHANNEL_SCAN_REPORT,
CMDU_CLIENT_DISASSOCIATION_STATS,
CMDU_ASSOCIATION_STATUS_NOTIFICATION,
CMDU_BACKHAUL_STA_CAPABILITY_QUERY,
CMDU_BACKHAUL_STA_CAPABILITY_REPORT,
CMDU_CHANNEL_PREFERENCE_REPORT,
CMDU_CLIENT_STEERING_BTM_REPORT,
CMDU_STEERING_COMPLETED,
CMDU_CHANNEL_SELECTION_RESPONSE,
CMDU_OPERATING_CHANNEL_REPORT,
CMDU_AP_CAPABILITY_QUERY,
CMDU_AP_CAPABILITY_REPORT,
CMDU_CLIENT_CAPABILITY_REPORT,
CMDU_BACKHAUL_STEER_RESPONSE,
#if (EASYMESH_VERSION > 2)
CMDU_BSS_CONFIG_REQUEST,
CMDU_BSS_CONFIG_RESULT,
CMDU_CHIRP_NOTIFICATION,
#endif
#if (EASYMESH_VERSION > 5)
CMDU_EARLY_AP_CAPABILITY_REPORT,
memcpy(c->cmdu_mask, cmdu_mask, sizeof(c->cmdu_mask));
cntlr_wait_for_object_timeout(c, map_plugin, -1, &map_id);
c->map_oid = map_id;
/* register as client to the map module */
ret = map_subscribe(c->ubus_ctx,
&c->map_oid,
"mapcontroller", &cmdu_mask, c,
cntlr_map_sub_cb,
cntlr_map_del_cb,
&c->subscriber);
if (!ret) {
c->subscribed = true;
} else {
map_plugin, ubus_strerror(ret));
static int cntlr_ackq_timeout_cb(struct cmdu_ackq *q, struct cmdu_ackq_entry *e)
{
struct controller *a = container_of(q, struct controller, cmdu_ack_q);
struct cmdu_buff *cmdu = (struct cmdu_buff *) e->cookie;
int ret;
trace("%s: ---> cmdu = %04x to "MACFMT" \n", __func__,
cmdu_get_type(cmdu), MAC2STR(cmdu->origin));
if (e->resend_cnt-- > 0) {
ret = send_cmdu_ubus(a, cmdu);
if (ret < 0)
return CMDU_ACKQ_TMO_REARM;
}
return CMDU_ACKQ_TMO_DELETE;
}
static void cntlr_ackq_delete_cb(struct cmdu_ackq *q, struct cmdu_ackq_entry *e)
{
struct cmdu_buff *cmdu = (struct cmdu_buff *) e->cookie;
trace("%s: ---> cmdu = %04x to "MACFMT" \n", __func__,
cmdu_get_type(cmdu), MAC2STR(cmdu->origin));
cmdu_free(cmdu);
}
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
static void uobj_add_event_handler(void *cntlr, struct blob_attr *msg)
{
char path[32] = {0};
uint32_t id = 0;
struct controller *c = (struct controller *) cntlr;
struct blob_attr *tb[2];
static const struct blobmsg_policy ev_attr[2] = {
[0] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
[1] = { .name = "path", .type = BLOBMSG_TYPE_STRING }
};
blobmsg_parse(ev_attr, 2, tb, blob_data(msg), blob_len(msg));
if (!tb[0] || !tb[1])
return;
strncpy(path, blobmsg_data(tb[1]), sizeof(path) - 1);
id = (uint32_t) blobmsg_get_u32(tb[0]);
dbg("|%s:%d| path = [%s] id = [%d] [%u]\n", __func__, __LINE__, path,
id, id);
if (!strncmp(path, map_plugin, strlen(map_plugin))) {
/* TODO: how to handle failure? */
cntlr_subscribe_for_cmdus(c);
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
}
}
static void cntlr_event_handler(struct ubus_context *ctx,
struct ubus_event_handler *ev,
const char *type, struct blob_attr *msg)
{
int i;
char *str;
struct controller *c = container_of(ev, struct controller, evh);
struct wifi_ev_handler {
const char *ev_type;
void (*handler)(void *ctx, struct blob_attr *ev_data);
} evs[] = {
{ "ubus.object.add", uobj_add_event_handler }
};
str = blobmsg_format_json(msg, true);
if (!str)
return;
dbg("[ &controller = %p ] Received [event = %s] [val = %s]\n",
c, type, str);
for (i = 0; i < ARRAY_SIZE(evs); i++) {
if (!strcmp(type, evs[i].ev_type))
evs[i].handler(c, msg);
}
free(str);
}
static void cntlr_periodic_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, heartbeat);
c->uptime += 1;
cntlr_trace(LOG_TIMER, "%s: periodic time (elapsed:%"PRIu64")\n",
__func__, c->uptime);
if ((c->uptime % METRIC_REP_INTERVAL) == 0)
cntlr_metric_collection(c);
#if 1 //TODO: move from here
if (c->uptime % 7 == 0) {
struct node *n = NULL;
list_for_each_entry(n, &c->nodelist, list) {
uint32_t est_thput = cntlr_estimate_max_throughput_for_node(c, n->almacaddr);
dbg("** Node = " MACFMT ": est-throughput = %d\n",
MAC2STR(n->almacaddr), est_thput);
n->est_thput_dl = est_thput;
}
}
#endif
cntlr_ageout_nodes(c);
timer_set(&c->heartbeat, 1 * 1000);
}
struct log_options *lopts = (struct log_options *)opts;
sigset_t base_mask;
sigemptyset(&base_mask);
sigaddset(&base_mask, SIGHUP);
sigprocmask(SIG_SETMASK, &base_mask, NULL);
set_sighandler(SIGPIPE, SIG_IGN);
c = calloc(1, sizeof(struct controller));
if (!c)
INIT_LIST_HEAD(&c->bcnreqlist);
INIT_LIST_HEAD(&c->linklist);
INIT_LIST_HEAD(&c->sclist);
#if (EASYMESH_VERSION > 2)
{
char *argv[] = {"-I", "-C", "-V", "2"};
int argc = 4;
int ret;
ret = dpp_init(&c->dpp, argc, argv);
if (ret) {
}
dpp_register_cb(c->dpp, dpp_frame_handler);
dpp_set_ctx_private_data(c->dpp, c);
memcpy(&c->log, lopts, sizeof(*lopts));
cntlr_config_defaults(c, &c->cfg);
cntlr_resync_config(c, false);
info("Starting Controller build %s %s, EasyMesh ver%d.\n",
__DATE__, __TIME__, EASYMESH_VERSION);
dbg("%s: cntlr = %p\n", __func__, c);
uloop_init();
ctx = ubus_connect(ubus_socket);
if (!ctx) {
err("Failed to connect to ubus\n");
goto out_exit;
}
c->ubus_ctx = ctx;
ret = cntlr_get_ieee1905_almac(c, c->almacaddr);
if (ret)
goto out_exit;
memcpy(c->cfg.id, c->almacaddr, 6);
init_topology(&c->topology);
cmdu_ackq_init(&c->cmdu_ack_q);
c->cmdu_ack_q.timeout_cb = cntlr_ackq_timeout_cb;
c->cmdu_ack_q.delete_cb = cntlr_ackq_delete_cb;
ubus_add_uloop(ctx);
if (!c->cfg.enabled)
goto out_exit;
/* diff always 1 after first round, will cause failures on
* first reload if not un-set
*/
struct node_policy *np = NULL;
list_for_each_entry(np, &c->cfg.nodelist, list) {
struct node *n;
np->is_policy_diff = false;
n = cntlr_add_node(c, np->agent_id);
if (!n)
goto out_exit;
n->np = np;
c->state = CNTLR_INIT;
timer_init(&c->discovery_timer, cntlr_discovery);
timer_init(&c->start_timer, cntlr_start);
timer_init(&c->heartbeat, cntlr_periodic_run);
timer_init(&c->radar_timer, cntlr_radar_exit);
timer_init(&c->signal_handler, cntlr_signal_periodic_run);
timer_init(&c->query_nodes, cntlr_query_nodes);
timer_init(&c->acs, cntlr_acs_run);
timer_init(&c->steer_sched_timer, cntlr_steer_sched_run);
timer_set(&c->heartbeat, 1 * 1000);
timer_set(&c->start_timer, waitext ? 5 * 1000 : 0);
timer_set(&c->discovery_timer, 0);
timer_set(&c->signal_handler, 5 * 1000);
timer_set(&c->query_nodes, 60 * 1000);
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
c->evh.cb = cntlr_event_handler;
ubus_register_event_handler(ctx, &c->evh, "ubus.object.*");
cntlr_subscribe_for_cmdus(c);
/* The counters in MultiAPSteeringSummaryStats are all reset on reboot. */
memset(&c->dlem.network.steer_summary, 0, sizeof(struct wifi_steer_summary));
info("Configured Multi-AP Profile %d\n", c->cfg.map_profile);
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
dpp_free(c->dpp);
#endif
#endif
cntlr_unload_steer_modules(c);
cntlr_clean_mac_hashtable(c);
cntlr_clean_bcnreqlist(c);
cntlr_clean_linklist(c);
free_topology(&c->topology);
ubus_unregister_event_handler(ctx, &c->evh);
//cntlr_remove_dbg_object(c); //TODO
cmdu_ackq_free(&c->cmdu_ack_q);
cntlr_config_clean(&c->cfg);