-
Anjan Chanda authoredAnjan Chanda authored
cntlr.c 43.23 KiB
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libubox/uloop.h>
#include <libubus.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <easy/easy.h>
#include <cmdu.h>
#include <1905_tlvs.h>
#include <i1905_wsc.h>
#include <easymesh.h>
#include <map_module.h>
#include <uci.h>
#include <wifidefs.h>
#include "timer.h"
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
#include <dpp_api.h>
#endif
#include "dpp.h"
#endif
#include "wifi_dataelements.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "sta.h"
#include "mactable.h"
#include "cntlr.h"
#include "steer.h"
#include "steer_module.h"
#include "backhaul_topology.h"
#include "cntlr_ubus.h"
#include "cntlr_ubus_dbg.h"
#include "cntlr_map.h"
#include "cntlr_cmdu.h"
#include "cntlr_acs.h"
#if (EASYMESH_VERSION > 2)
#include "cntlr_qos.h"
#endif
#include "wifi_opclass.h"
#define map_plugin "ieee1905.map"
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);
if (WARN_ON(!entry))
return NULL;
return (struct netif_iface *)entry->data;
}
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);
if (WARN_ON(!entry))
return NULL;
return (struct node *)entry->data;
}
//TODO: how used ?
struct netif_iface *cntlr_find_iface_in_radio(struct controller *c,
struct netif_radio *r,
uint8_t *macaddr)
{
struct macaddr_entry *entry = NULL;
entry = mactable_lookup(c->mac_table, macaddr, MAC_ENTRY_FBSS);
if (WARN_ON(!entry))
return NULL;
return (struct netif_iface *)entry->data;
}
//TODO: how used ?
struct netif_radio *cntlr_find_radio_in_node(struct controller *c,
struct node *n,
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;
}
/* 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;
}
/* find link by macaddress */
struct netif_link *cntlr_find_link(struct controller *c, uint8_t *ul_macaddr, uint8_t *dl_macaddr)
{
struct netif_link *l = NULL;
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;
}
/* TODO: untested */
int cntlr_get_node_assoclist(struct controller *c, uint8_t *node_alid,
int *num_sta, uint8_t *sta_macaddrs)
{
struct sta *s = NULL;
struct node *n;
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->de_sta->macaddr, 6);
else
ret = -E2BIG;
i++;
}
*num_sta = i;
return ret;
}
struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta, uint8_t *almacaddr)
{
struct bcnreq *br = NULL;
trace("%s: --->\n", __func__);
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)
{
struct node *n = NULL;
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 n;
}
}
}
return NULL;
}
#if (EASYMESH_VERSION >= 6)
bool cntlr_radio_support_ap_wifi7(struct controller *c,
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;
}
bool cntlr_radio_support_bsta_wifi7(struct controller *c,
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;
}
#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) {
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;
char mac_str[18] = {0};
struct node *n;
int ret;
if (!hwaddr_ntoa(almacaddr, mac_str))
return NULL;
n = cntlr_find_node(c, almacaddr);
if (!n) {
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);
#ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
if (!hwaddr_equal(c->almacaddr, almacaddr))
cntlr_sync_dyn_controller_config(c, almacaddr);
#endif
#if (EASYMESH_VERSION > 2)
if (c->cfg.qos.enabled) {
cntlr_qos_sync_node(c, n->almacaddr);
}
#endif
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);
return n;
}
static void cntlr_log_nodes(struct controller *c)
{
struct node *n = NULL;
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 *bh_topo_dev =
find_bh_topology_device(node->almacaddr);
struct netif_radio *radio = NULL;
if (!bh_topo_dev) {
err("%s: device not found in bh topo model, logical error.\n", __func__);
continue;
}
if (bh_topo_dev->bh_info.level_in_tree == UNKNOWN_TREE_LEVEL) {
warn("%s: Level in BH treee unknown for: " MACFMT "\n", __func__, MAC2STR(node->almacaddr));
continue;
}
if (c->cfg.max_node_bh_hops <= bh_topo_dev->bh_info.wifi_hops_from_root)
bh_control = BLOCK;
list_for_each_entry(radio, &node->radiolist, list) {
struct netif_iface *iface = NULL;
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),
bh_topo_dev->bh_info.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 node *n = s->fh->agent;
struct controller *c = n->cntlr;
cntlr_dbg(LOG_STA, "%s: No Beacon Report from STA " MACFMT" yet.\n",
__func__, MAC2STR(s->de_sta->macaddr));
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 {
/* pop and free matching request(s) for the STA */
cntlr_clean_bcnreqlist_sta(c, s);
s->bcn_response_tmo = false;
}
#if 0
list_for_each_entry_safe(b, tmp, &s->de_sta->meas_reportlist, list) {
dbg("bcn-report from " MACFMT "\n", MAC2STR(b->bssid));
/* Skip entry not in our network */
bss = cntlr_find_iface(c, b->bssid);
if (!bss) {
dbg("%s: Delete alien entry " MACFMT "\n",
__func__, MAC2STR(b->bssid));
list_del(&b->list);
free(b);
s->de_sta->num_meas_reports--;
continue;
}
}
#endif
dbg("%s exiting\n", __func__);
}
void node_add_sta(struct node *n, struct sta *s)
{
list_add(&s->list, &n->stalist);
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;
}
return NULL;
}
static void cntlr_remove_sta(struct controller *c, struct node *n, struct sta *s)
{
n->sta_count--;
timer_del(&s->bcn_metrics_timer);
sta_free_bcn_metrics(s);
cntlr_clean_bcnreqlist_sta(c, s);
list_del(&s->list);
free(s->de_sta);
sta_del(c->sta_table, s->macaddr);
}
struct cmdu_buff *_cntlr_gen_sta_metric_query(struct controller *c, struct sta *s)
{
struct cmdu_buff *cmdu = NULL;
if (!s->fh || !s->fh->agent || hwaddr_is_zero(s->fh->agent->almacaddr)) {
warn("%s: unable to determine target node almacaddr!\n", __func__);
return NULL;
}
if (!s->de_sta || hwaddr_is_zero(s->de_sta->macaddr)) {
warn("%s: unable to determine STA macaddr!\n", __func__);
return NULL;
}
cmdu = cntlr_gen_sta_metric_query(c, s->fh->agent->almacaddr, s->de_sta->macaddr);
if (!cmdu)
return NULL;
return cmdu;
}
static void cntlr_get_all_sta_metrics(struct controller *c)
{
struct node *n = NULL;
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_gen_sta_metric_query(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;
//INIT_LIST_HEAD(&bss->stalist);
memcpy(bss->bssid, macaddr, 6);
return bss;
}
struct netif_iface *cntlr_radio_add_iface(struct controller *c,
struct netif_radio *r,
uint8_t *macaddr)
{
struct netif_iface *n;
n = cntlr_find_iface_in_radio(c, r, macaddr);
if (n) {
n->bss->enabled = true;
return n;
}
n = calloc(1, sizeof(*n));
if (!n)
return NULL;
n->bss = cntlr_alloc_wifi_bss(c, macaddr);
if (!n->bss) {
free(n);
return NULL;
}
n->band = wifi_opclass_get_band(r->radio_el->cur_opclass.opclass[0].id);
if (n->band == BAND_UNKNOWN) {
int ret;
n->band = r->radio_el->band;
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,
0, 0, 0);
#endif
mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_FBSS, (void *)n);
return n;
}
static struct wifi_radio_element *cntlr_alloc_wifi_radio(struct controller *c)
{
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);
return radio_el;
}
struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n,
uint8_t *macaddr)
{
struct netif_radio *r;
struct radio_policy *p;
r = cntlr_find_radio_in_node(c, n, macaddr);
if (r)
return r;
r = calloc(1, sizeof(*r));
if (!r)
return NULL;
r->radio_el = cntlr_alloc_wifi_radio(c);
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, 0, 0, 0);
p = cntlr_get_radio_policy(&c->cfg, macaddr);
if (p)
r->radio_el->band = p->band;
return r;
}
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)
{
struct node *n;
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->stalist);
INIT_LIST_HEAD(&n->radiolist);
n->sta_count = 0;
list_add(&n->list, &c->nodelist);
c->num_nodes++;
mactable_add_entry(c->mac_table, almacaddr, MAC_ENTRY_ALID, (void *)n);
dbg("%s %d --------- " MACFMT "\n", __func__, __LINE__, MAC2STR(almacaddr));
return n;
}
void cntlr_clean_bcnreqlist(struct controller *c)
{
dbg("%s: --->\n", __func__);
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->de_sta->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_iface *ni = NULL, *tmp;
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);
free(ni->bss);
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);
}
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) {
radio_clean_iflist(c, r);
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) {
cntlr_remove_sta(c, n, s);
n->sta_count--;
}
if (n->sta_count != 0) {
err("%s: Misalligned station counter!\n", __func__);
n->sta_count = 0;
}
}
static void cntlr_clean_mac_hashtable(struct controller *c)
{
mactable_flush(c->mac_table);
}
static void cntlr_clean_nodelist(struct controller *c)
{
struct node *n = NULL, *tmp;
list_for_each_entry_safe(n, tmp, &c->nodelist, list) {
node_clean_stalist(c, n);
cntlr_clean_radiolist(c, n);
list_del(&n->list);
free(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)
goto out;
l->upstream = cntlr_find_iface(c, upstream);
if (!l->upstream)
goto out_metrics;
l->downstream = cntlr_find_iface(c, downstream);
if (!l->downstream)
goto out_metrics;
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);
return NULL;
}
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]);
if (tb[2])
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;
struct node *n = NULL;
/* TODO/CLEANUP:
* as of now, no information is being stored about
* the specific agent's radios & BSS,
* Also few information i.e. exclude stalist,
* rcpi/util threshold is sent along with
* 17.2.11 & 17.2.12 tlv.
* So for now, dummy radio id & bss id is being used for this
* purpose.
* ((later on radio id & bss id info for specific agent
* will be stored using in Topology Response CMDU.))
*/
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};
uint8_t radio_id[MAX_NUM_RADIO * 6] = {0};
struct netif_radio *r = NULL;
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],
r->radio_el->macaddr, 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; */
list_for_each_entry(p, &c->cfg.nodelist, list) {
p->is_policy_diff = false;
}
}
#if (EASYMESH_VERSION > 2)
if ((diff & CONFIG_DIFF_QOS) && c->cfg.qos.enabled) {
struct node *n = NULL;
trace("qos config changed\n");
/* send the policy config cmdu to the marked agent */
list_for_each_entry(n, &c->nodelist, list) {
cntlr_qos_sync_node(c, n->almacaddr);
}
}
#endif
#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);
}
#endif
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;
}
#endif
bool cntlr_resync_config(struct controller *c, bool reload)
{
uint32_t diff;
struct node_policy *np = NULL;
diff = cntlr_config_reload(&c->cfg);
list_for_each_entry(np, &c->cfg.nodelist, list) {
struct node *n;
n = cntlr_find_node(c, np->agent_id);
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);
#endif
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);
remove_bh_topology_device(n->almacaddr);
node_clean_stalist(c, n);
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);
/* For each radio */
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");
goto error;
}
radiolist = new_radiolist;
num_radio++;
radio_index = (num_radio - 1) * 6;
memcpy(radiolist + radio_index, r->radio_el->macaddr, 6);
/* For each bss in radio */
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 */
continue;
/* 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");
goto error;
}
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);
if (!n) {
cmdu_free(cmdu);
goto error;
}
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)
{
cntlr_log_nodes(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);
/* TODO: update only when a node is added or removed */
//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);
bool skip_dfs = false;
/* Run ACS recalc here */
dbg("acs timeout - run recalc\n");
cntlr_acs_recalc(c, skip_dfs);
if (c->cfg.acs_timeout)
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
}
static void cntlr_dfs_cleanup_run(atimer_t *t)
{
struct controller *c = container_of(t, struct controller, dfs_cleanup);
/* Run background CAC here */
dbg("dfs bgcac timeout - run cleanup\n");
cntlr_dfs_cleanup(c);
if (c->cfg.dfs_cleanup_timeout)
timer_set(&c->dfs_cleanup, c->cfg.dfs_cleanup_timeout * 1000);
}
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_free(cmdu);
cmdu = cntlr_gen_ap_autoconfig_search(c, 0x02, 0x01);
if (!cmdu)
return;
c->mid_5g = send_cmdu(c, cmdu);
cmdu_free(cmdu);
timer_set(t, 180 * 1000);
}
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);
free(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);
return 0;
}
static int cntlr_subscribe_for_cmdus(struct controller *c)
{
mapmodule_cmdu_mask_t cmdu_mask = {0};
uint32_t map_id;
int ret;
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_HIGHER_LAYER_DATA,
CMDU_BACKHAUL_STEER_RESPONSE,
#if (EASYMESH_VERSION > 2)
CMDU_PROXIED_ENCAP_DPP,
CMDU_DIRECT_ENCAP_DPP,
CMDU_BSS_CONFIG_REQUEST,
CMDU_BSS_CONFIG_RESULT,
CMDU_CHIRP_NOTIFICATION,
CMDU_DPP_BOOTSTRAPING_URI,
#endif
#if (EASYMESH_VERSION > 5)
CMDU_EARLY_AP_CAPABILITY_REPORT,
#endif
-1);
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));
}
return 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)
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);
}
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);
}
}
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_remove_stale_sta(struct controller *c)
{
struct node *n = NULL;
time_t curr_time;
if (!c)
return;
time(&curr_time);
list_for_each_entry(n, &c->nodelist, list) {
struct sta *s = NULL, *tmp;
list_for_each_entry_safe(s, tmp, &n->stalist, list) {
if (!s->associated && s->disassoc_time != 0) {
if ((curr_time - s->disassoc_time) >= c->cfg.stale_sta_timeout) {
dbg("|%s:%d| Removing station " MACFMT " after %d seconds of disassociation\n",
__func__, __LINE__,
MAC2STR(s->de_sta->macaddr), c->cfg.stale_sta_timeout);
cntlr_remove_sta(c, n, s);
}
}
}
}
}
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);
cntlr_ageout_nodes(c);
cntlr_remove_stale_sta(c);
timer_set(&c->heartbeat, 1 * 1000);
}
void run_controller(void *opts)
{
struct log_options *lopts = (struct log_options *)opts;
struct controller *c;
struct ubus_context *ctx;
/* struct ubus_event_handler *ev; */
sigset_t base_mask;
int ret;
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)
return;
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);
stop_logging();
return;
}
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_bh_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)
{
#ifdef USE_LIBDPP
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);
#endif
#if 0
dpp_cntlr_read_uris(c);
#endif
}
#endif
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;
{
/* TODO: diff always 1 after first round, will cause failures on
* first reload */
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;
}
}
//cntlr_register_events(c);
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->dfs_cleanup, cntlr_dfs_cleanup_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);
if (c->cfg.acs_timeout)
timer_set(&c->acs, c->cfg.acs_timeout * 1000);
if (c->cfg.dfs_cleanup_timeout)
timer_set(&c->dfs_cleanup, c->cfg.dfs_cleanup_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);
uloop_run();
out_exit:
#if (EASYMESH_VERSION > 2)
#ifdef USE_LIBDPP
err("free dpp!!\n");
dpp_free(c->dpp);
#endif
#endif
cntlr_unload_steer_modules(c);
map_unsubscribe(ctx, c->subscriber);
cntlr_clean_mac_hashtable(c);
cntlr_clean_bcnreqlist(c);
cntlr_clean_linklist(c);
cntlr_clean_nodelist(c);
free_bh_topology();
ubus_unregister_event_handler(ctx, &c->evh);
cntlr_remove_object(c);
//cntlr_remove_dbg_object(c); //TODO
cmdu_ackq_free(&c->cmdu_ack_q);
cntlr_config_clean(&c->cfg);
uloop_done();
stop_logging();
free(c);
}