/* * 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 "cntlr.h" #include <1905_tlvs.h> #include <cmdu.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 <map_module.h> #include <signal.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <wifidefs.h> #include "timer.h" #if (EASYMESH_VERSION >= 3) #ifdef USE_LIBDPP #include <dpp_api.h> #endif #endif #include "acs.h" #include "cntlr_cmdu.h" #include "cntlr_map.h" #include "cntlr_ubus.h" #include "config.h" #if (EASYMESH_VERSION >= 3) #include "dpp.h" #endif #include "libubox/blobmsg.h" #include "libubox/list.h" #include "mactable.h" #include "sta.h" #include "stdbool.h" #include "timer_impl.h" #include "topology.h" #include "utils/debug.h" #include "utils/utils.h" #include "wifi_dataelements.h" #include "wifi_opclass.h" #include "steer.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_iface *cntlr_find_iface_type(struct controller *c, uint8_t *macaddr, uint32_t type) { 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); if (WARN_ON(!entry)) 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; } /* 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->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; } /* 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) 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; } 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; } bool cntlr_node_support_ap_wifi7(struct node *n) { struct netif_radio *r = NULL; list_for_each_entry(r, &n->radiolist, list) { if (cntlr_radio_support_ap_wifi7(&r->wifi7_caps)) return true; } return false; } #endif int cntlr_update_txlink_metric_data(struct controller *c, struct tlv_tx_linkmetric *txl, int len) { int i = 0; int size = 0; struct tx_link_info *txlinfo; struct netif_link *txlink; struct link_metrics *metrics; /* For each tx link in the mesg */ size = len - (sizeof(struct tlv_tx_linkmetric)); size = size / (sizeof(struct tx_link_info)); for (i = 0; i < size; i++) { txlinfo = (struct tx_link_info *)&txl->link[i]; /* Find or alloc the backhaul link to the controller */ txlink = cntlr_alloc_link(c, txlinfo->local_macaddr, txlinfo->neighbor_macaddr); if (!txlink) { 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 *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)); continue; } 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) { 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), 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", __func__, MAC2STR(s->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 { cntlr_del_bcnreq(c, s->macaddr, s->agent_almacaddr); s->bcn_response_tmo = false; } } 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)); return -1; } memcpy(s->agent_almacaddr, n->almacaddr, 6); list_add(&s->list, &n->stalist); n->sta_count++; sta_inc_ref(s); return 0; } 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)); return -1; } list_del(&s->list); n->sta_count--; sta_dec_ref(s); return 0; } 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--; sta_dec_ref(s); } } } 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; } 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 NULL; } return cntlr_gen_sta_metric_query(c, s->agent_almacaddr, s->macaddr); } 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_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, uint8_t *macaddr) { struct netif_iface *n; n = cntlr_find_iface(c, 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, NULL); #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 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); 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(c, macaddr); if (r) return r; 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; 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; } 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; 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) { 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->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); } 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) { 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) { node_del_sta(n, s); } if (WARN_ON(n->sta_count != 0)) { err("%s: Misalligned station counter (cnt=%d)!\n", __func__, n->sta_count); n->sta_count = 0; } } 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) { 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; 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; cntlr_dbg(LOG_QOS, "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); topology_remove_device(&c->topology, 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); /* Run ACS recalc here */ dbg("acs timeout - run recalc\n"); cntlr_acs_recalc(c); if (c->cfg.acs && c->cfg.acs_timeout) 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_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_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); } void run_controller(void *opts) { struct log_options *lopts = (struct log_options *)opts; struct controller *c; struct ubus_context *ctx; 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_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) { #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; { /* 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); if (c->cfg.acs && c->cfg.acs_timeout) 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); 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); cntlr_clean_all_sta(c); free_topology(&c->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); }