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 <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 "dpp.h"
#endif
#include "wifi_dataelements.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "mactable.h"
#include "backhaul_topology.h"
#include "cntlr_ubus_dbg.h"
#include "cntlr_cmdu.h"
#if (EASYMESH_VERSION > 2)
#include "cntlr_qos.h"
#endif
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;
}
#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;
list_for_each_entry(r, &n->radiolist, list) {
if (cntlr_radio_support_ap_wifi7(&r->wifi7_caps))
return true;
}
return false;
}
#endif
273
274
275
276
277
278
279
280
281
282
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
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);
#if (EASYMESH_VERSION > 2)
if (c->cfg.qos.enabled) {
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 *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) {
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 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;
}
void node_add_sta(struct node *n, struct sta *s)
list_add(&s->list, &n->stalist);
n->sta_count++;
void node_del_sta(struct node *n, struct sta *s)
{
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;
}
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;
}
static void cntlr_freeze_sta(struct controller *c, struct sta *s)
timer_del(&s->bcn_metrics_timer);
sta_free_bcn_metrics(s);
cntlr_clean_bcnreqlist_sta(c, s);
static void cntlr_remove_sta(struct controller *c, struct node *n, struct sta *s)
cntlr_freeze_sta(c, s);
node_del_sta(n, s);
cntlr_del_sta(c->sta_table, s->macaddr);
}
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);
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,
0, 0, 0);
#endif
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 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);
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);
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;
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));
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);
}
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) {
cntlr_remove_sta(c, n, s);
}
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) {
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;
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);
list_for_each_entry(n, &c->nodelist, list) {