Skip to content
Snippets Groups Projects
cntlr.c 45.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Anjan Chanda's avatar
    Anjan Chanda committed
    /*
    
     * cntlr.c - Multi-AP controller
    
    Anjan Chanda's avatar
    Anjan Chanda committed
     *
    
     * Copyright (C) 2020-2022 IOPSYS Software Solutions AB. All rights reserved.
    
    Anjan Chanda's avatar
    Anjan Chanda committed
     *
    
     * See LICENSE file for source code license information.
    
    Anjan Chanda's avatar
    Anjan Chanda committed
     *
     */
    
    #include "cntlr.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    
    #include <1905_tlvs.h>
    
    #include <cmdu.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 "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"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    #define map_plugin	"ieee1905.map"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    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 (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 (struct node *)entry->data;
    
    /* finds radio struct from interface macaddr */
    
    struct netif_radio *cntlr_find_radio_with_bssid(struct controller *c, uint8_t *bssid)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	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))
    
    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)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	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))
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    /* 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;
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    	struct node *n;
    
    	int limit = *num_sta;
    	int ret = 0;
    	int i = 0;
    
    	*num_sta = 0;
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    
    	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);
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    		else
    			ret = -E2BIG;
    
    		i++;
    
    struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta, uint8_t *almacaddr)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	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))
    
    /* find node based on bssid */
    
    struct node *cntlr_find_node_with_bssid(struct controller *c, uint8_t *bssid)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	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) {
    
    
    			list_for_each_entry(p, &r->iflist, list) {
    
    				if (!memcmp(p->bss->bssid, bssid, 6))
    
    /* 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;
    }
    
    
    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;
    }
    
    
    
    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;
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	char mac_str[18] = {0};
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	int ret;
    
    
    	if (!hwaddr_ntoa(almacaddr, mac_str))
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		return NULL;
    
    
    	n = cntlr_find_node(c, almacaddr);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	if (!n) {
    
    		n = cntlr_alloc_node(c, almacaddr);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		if (!n) {
    			err("|%s:%d| failed to allocate node "MACFMT"\n",
    
    			    __func__, __LINE__, MAC2STR(almacaddr));
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    			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);
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    #ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
    
    	if (!hwaddr_equal(c->almacaddr, almacaddr))
    		cntlr_sync_dyn_controller_config(c, almacaddr);
    
    #if (EASYMESH_VERSION > 2)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	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);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static void cntlr_log_nodes(struct controller *c)
    {
    
    	struct node *n = NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	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;
    
    			err("%s: device not found in bh topo model, logical error.\n", __func__);
    			continue;
    		}
    
    
    		if (topo_dev->bh.depth == UNKNOWN_TREE_LEVEL) {
    
    			warn("%s: Level in BH treee unknown for: " MACFMT "\n", __func__, MAC2STR(node->almacaddr));
    
    		if (c->cfg.max_node_bh_hops <= topo_dev->bh.wifi_hops_from_root)
    
    			bh_control = BLOCK;
    
    		list_for_each_entry(radio, &node->radiolist, list) {
    
    			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);
    
    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;
    	}
    
    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)
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    	struct node *n = NULL;
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    	list_for_each_entry(n, &c->nodelist, list) {
    		struct sta *s = NULL;
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    		list_for_each_entry(s, &n->stalist, list) {
    			struct cmdu_buff *cmdu;
    
    			cmdu = cntlr_query_sta_metric(c, s);
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    			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;
    
    struct netif_iface *cntlr_radio_add_iface(struct controller *c,
    
    					      struct netif_radio *r,
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct netif_iface *n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    		n->bss->enabled = true;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    
    	n = calloc(1, sizeof(*n));
    	if (!n)
    		return NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	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;
    
    
    		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);
    
    	mactable_add_entry(c->mac_table, macaddr, MAC_ENTRY_FBSS, (void *)n);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    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)
    
    	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)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct netif_radio *r;
    
    	struct radio_policy *p;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	r = calloc(1, sizeof(*r));
    	if (!r)
    		return NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	r->radio_el = cntlr_alloc_wifi_radio(c, n, r);
    
    
    	INIT_LIST_HEAD(&r->iflist);
    	list_add(&r->list, &n->radiolist);
    	r->agent = n;
    
    
    	memcpy(r->radio_el->macaddr, macaddr, 6);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	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;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    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)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct node *n;
    
    
    	n = calloc(1, sizeof(struct node));
    	if (!n) {
    		warn("OOM: node malloc failed!\n");
    		return NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    
    	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;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    	INIT_LIST_HEAD(&n->stalist);
    
    	INIT_LIST_HEAD(&n->radiolist);
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    	n->sta_count = 0;
    
    	list_add(&n->list, &c->nodelist);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	mactable_add_entry(c->mac_table, almacaddr, MAC_ENTRY_ALID, (void *)n);
    
    	dbg("%s %d --------- " MACFMT "\n", __func__, __LINE__, MAC2STR(almacaddr));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    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)
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    {
    	struct netif_iface *ni = NULL, *tmp;
    
    	struct netif_link *l = NULL, *temp;
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    
    	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);
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    		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;
    
    static void cntlr_clean_radiolist(struct controller *c, struct node *n)
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    {
    	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);
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    		list_del(&r->list);
    		free(r);
    	}
    }
    
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    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);
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    		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;
    }
    
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    static void cntlr_clean_nodelist(struct controller *c)
    {
    	struct node *n = NULL, *tmp;
    
    	list_for_each_entry_safe(n, tmp, &c->nodelist, list) {
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    		node_clean_stalist(c, n);
    
    		cntlr_clean_radiolist(c, n);
    
    		list_del(&n->list);
    
    struct netif_link *cntlr_alloc_link(struct controller *c,
    
    		uint8_t *upstream, uint8_t *downstream)
    {