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 <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 "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"
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_fbss(struct controller *c, uint8_t *macaddr)
{
return cntlr_find_iface_type(c, macaddr, MAC_ENTRY_FBSS);
}
struct netif_iface *cntlr_find_bbss(struct controller *c, uint8_t *macaddr)
{
return cntlr_find_iface_type(c, macaddr, MAC_ENTRY_BBSS);
}
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_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;
}
#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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
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) {
trace("No link!\n");
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) {
trace("No link!\n");
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);
if (!n) {
err("|%s:%d| failed to allocate node "MACFMT"\n",
__func__, __LINE__, MAC2STR(almacaddr));
return NULL;
}
} 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;
}
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
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++;
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) {
warn("OOM: node malloc failed!\n");
return NULL;
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);
dbg("%s %d --------- " MACFMT "\n", __func__, __LINE__, 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;
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
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)
{
dbg("%s: --->\n", __func__);
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);
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)) {
err("%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);
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);
trace("Adding link | " MACFMT " <---> " MACFMT " |\n",
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};
trace("Config changed, triggering renew!\n");
cmdu = cntlr_gen_ap_autoconfig_renew(c, origin);
if (cmdu) {
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;
trace("agent policy config changed\n");
/* 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;
/* 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;
trace("ap mld config changed\n");
/* 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;
trace("bsta mld config changed\n");
/* 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_dbg(LOG_TIMER, "|%s:%d| Received SIGHUP, reload config\n", __func__, __LINE__);
signal(SIGHUP, SIG_IGN);
cntlr_resync_config(c, true);
cntlr_dbg(LOG_TIMER, "%s --->\n", __func__);
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;
trace("%s: --->\n", __func__);
/* 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);
list_del(&n->list);
free(n);
if (c->num_nodes > 0)
c->num_nodes--;
}
}
}
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);
dbg("Found possibly unconfigured node: "MACFMT"\n", MAC2STR(node->almacaddr));
if (cmdu) {
dbg("Sending AP_AUTOCONFIGURATION_RENEW\n");
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) {
trace("realloc of radiolist failed\n");
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) {
trace("realloc of bsslist failed\n");
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) {
trace("cmdu cntlr_gen_ap_metrics_query failed!\n");
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);
if (!cmdu) {
trace("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)
dbg("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;
cntlr_dbg(LOG_STEER, "%s: --->\n", __func__);
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;
if (c->cfg.steer.enable_sta_steer && !s->is_bsta)
cntlr_inform_steer_modules(c, s, c->inform_cmdu_type);
else if (c->cfg.steer.enable_bsta_steer && s->is_bsta)
cntlr_inform_bsteer_modules(c, s, c->inform_cmdu_type);
}
}
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));
trace("%s: wait for map-plugin\n", __func__);
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 {
trace("Failed to 'register' with %s (err = %s)\n",
map_plugin, ubus_strerror(ret));
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
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)
err("%s fail to send cmdu\n", __func__);
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);
}
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
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);
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
}
}
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;
info("[ &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_dbg(LOG_TIMER, "|%s:%d| periodic time (elapsed:%"PRIu64")\n",
__func__, __LINE__, 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);
cntlr_info(LOG_DEFAULT, "** 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)
memcpy(&c->log, lopts, sizeof(*lopts));
restart_logging(&c->log);
cntlr_info(LOG_MISC,
"Starting Controller build %s %s, EasyMesh ver%d.\n",
__DATE__, __TIME__, EASYMESH_VERSION);
cntlr_dbg(LOG_MISC, "%s: cntlr = %p\n", __func__, c);
uloop_init();
ctx = ubus_connect(ubus_socket);
if (!ctx) {
err("Failed to connect to ubus\n");
free(c);
}
c->ubus_ctx = ctx;
INIT_LIST_HEAD(&c->nodelist);
INIT_LIST_HEAD(&c->bcnreqlist);
c->num_nodes = 0;
INIT_LIST_HEAD(&c->linklist);
INIT_LIST_HEAD(&c->sclist);
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 (EASYMESH_VERSION > 2)
{
char *argv[] = {"-I", "-C", "-V", "2"};
int argc = 4;
int ret;
ret = dpp_init(&c->dpp, argc, argv);
if (ret) {
cntlr_dbg(LOG_DPP, "Failed to init dpp context\n");
goto out_exit;
}
dpp_register_cb(c->dpp, dpp_frame_handler);
dpp_set_ctx_private_data(c->dpp, c);
cntlr_config_defaults(c, &c->cfg);
ret = cntlr_get_ieee1905_almac(c, c->almacaddr);
if (ret)
goto out_exit;
memcpy(c->cfg.id, c->almacaddr, 6);
cntlr_resync_config(c, false);
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));
cntlr_dbg(LOG_MISC, "current wifi_cntlr profile %d\n", c->cfg.map_profile);
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
err("free dpp!!\n");
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);