Skip to content
Snippets Groups Projects
cntlr.c 48.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Anjan Chanda's avatar
    Anjan Chanda committed
    /*
     * cntlr.c
     * wifi controller core
     *
    
     * Copyright (C) 2020 IOPSYS Software Solutions AB. All rights reserved.
    
    Anjan Chanda's avatar
    Anjan Chanda committed
     *
     * Author: anjan.chanda@iopsys.eu
     *
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #ifndef _GNU_SOURCE
    #define _GNU_SOURCE
    #endif
    
    #include <json-c/json.h>
    #include <libubox/blobmsg.h>
    #include <libubox/blobmsg_json.h>
    #include <libubox/uloop.h>
    #include <libubox/ustream.h>
    #include <libubox/utils.h>
    #include <libubus.h>
    
    #include <easy/easy.h>
    
    #include <wifi.h>	// FIXME: should not be included
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    #include <timer_impl.h>
    #include <cmdu.h>
    #include <1905_tlvs.h>
    
    #include <map2.h>
    #include <map_module.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "debug.h"
    #include "utils.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "cntlr.h"
    #include "comm.h"
    #include "allsta.h"
    #include "cntlr_ubus.h"
    
    #include "cntlr_map.h"
    
    #include "cntlr_cmdu_generator.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    #define map_plugin	"ieee1905.map"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    static int update_fronthaul_bsslist(struct node *n, struct netif_iface *fh);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static void node_getbssinfo(void *cntlr, void *resp, int len, void *cookie);
    static int enumerate_topology_indirect(struct controller *c);
    
    static void mapclient_subscribe_for_cmdus(struct controller *c);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    void stop_cntlr(struct controller *c)
    {
    
    	if (!c) {
    		warn("%s: (cntlr = NULL)\n", __func__);
    		exit(0);
    	}
    
    	stop_test_logging();
    	ubus_unregister_event_handler(c->ubus_ctx, &c->ieee1905_evh);
    	cntlr_remove_object(c);
    	uloop_done();
    	cntlr_config_clean(&c->cfg);
    	comm_destroy(c->comm);
    	ubus_free(c->ubus_ctx);
    	free(c);
    }
    
    static void cntlr_terminate(struct controller *c)
    {
    	dbg("%s: called.\n", __func__);
    	stop_cntlr(c);
    
    	stop_logging();
    	//unlink(pidfile);
    	exit(0);
    }
    
    
    /* find node by macaddress */
    struct netif_iface *find_interface_by_mac(struct controller *c,
    		struct netif_radio *r, uint8_t *hwaddr)
    {
    	struct netif_iface *p;
    
    	list_for_each_entry(p, &r->iflist, list) {
    		if (!memcmp(p->bssid, hwaddr, 6))
    			return p;
    	}
    
    	return NULL;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    /* find node by macaddress */
    
    struct netif_radio *find_radio_by_node(struct controller *c, struct node *n,
    		uint8_t *radio)
    {
    	struct netif_radio *p;
    
    	list_for_each_entry(p, &n->radiolist, list) {
    		if (!memcmp(p->macaddr, radio, 6))
    			return p;
    	}
    
    	return NULL;
    }
    
    /* find node by macaddress */
    struct node *find_node_by_mac(struct controller *c, uint8_t *mac)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct node *p;
    
    	list_for_each_entry(p, &c->nodelist, list) {
    
    		if (!memcmp(p->alid, mac, 6))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			return p;
    	}
    
    	return NULL;
    }
    
    
    /* find node by macaddress */
    struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac)
    {
    	struct sta *s;
    
    	list_for_each_entry(s, &c->stalist, list) {
    		if (!memcmp(s->macaddr, mac, 6))
    			return s;
    	}
    
    	return NULL;
    }
    
    #if 0
    /* find node by macaddress */
    struct netif_iface *cntlr_get_fbss_by_mac(struct controller *c, struct node *n,
    		uint8_t *mac)
    {
    	struct netif_iface *p;
    
    	list_for_each_entry(p, &n->iflist, list) {
    		if (!memcmp(p->bssid, mac, 6))
    			return p;
    	}
    
    	return NULL;
    }
    #endif
    /* find fbss based on macaddr */
    struct netif_iface *cntlr_iterate_fbss(struct controller *c, uint8_t *mac)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		struct netif_radio *r;
    
    		list_for_each_entry(r, &n->radiolist, list) {
    			struct netif_iface *p;
    
    			list_for_each_entry(p, &r->iflist, list) {
    				if (!memcmp(p->bssid, mac, 6))
    					return p;
    			}
    		}
    	}
    
    	return NULL;
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    /* find node by ip address */
    static struct node *find_node_by_ip(struct controller *c, const char *ip)
    {
    	struct node *p;
    	struct in_addr ipn;
    
    	if (ip && strlen(ip) && !inet_aton(ip, &ipn)) {
    		warn("Invalid ipaddr: %s\n", ip);
    		return NULL;
    	}
    
    	list_for_each_entry(p, &c->nodelist, list) {
    		if (!memcmp(&p->ipaddr, &ipn, sizeof(struct in_addr)))
    			return p;
    	}
    
    	return NULL;
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    /* find node by any of its fh-bssid */
    struct node *get_node_by_bssid(struct controller *c, unsigned char *bssid)
    {
    	struct node *n;
    
    	struct netif_iface *p;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	list_for_each_entry(n, &c->nodelist, list) {
    
    		list_for_each_entry(p, &n->iflist, list) {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			if (memcmp(bssid, p->bssid, 6))
    				continue;
    
    			return n;
    		}
    	}
    
    	return NULL;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    static void cntlr_log_nodes(struct controller *c)
    {
    	struct node *n;
    	int i = 0;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    
    		cntlr_dbg("  %d | agent = %p,  hwaddr = '"MACFMT"')\n",
    				i++, n, MAC2STR(n->alid));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static int forall_node_update_neighbors(struct controller *c)
    {
    	struct node *n, *p;
    
    	if (c->num_nodes == 0)
    		return 0;
    
    	cntlr_dbg("num nodes = %d\n", c->num_nodes);
    
    	/* For single node, there will be no neighbors. So, delete
    	 * neighbors from it, if any.
    	 * A broadcast neighbor address passed to CMD_DEL_NEIGHBOR
    	 * command will delete all neighbors from a node.
    	 */
    	if (c->num_nodes == 1) {
    		char nbrstr[12 + 1] = {0};
    		unsigned char nbrmac[6] = "\xff\xff\xff\xff\xff\xff";
    
    		n = list_first_entry(&c->nodelist, struct node, list);
    		if (!n)
    			return -1;
    
    		btostr((unsigned char *)nbrmac, sizeof(nbrmac), nbrstr);
    		return 0;
    	}
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		list_for_each_entry(p, &c->nodelist, list) {
    
    			struct netif_iface *fh;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    			if (n == p)
    				continue;
    
    
    			list_for_each_entry(fh, &p->iflist, list) {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				struct nbr nbr;
    				char nbrstr[2*sizeof(struct nbr) + 1] = {0};
    
    				memset(&nbr, 0, sizeof(nbr));
    				memcpy(nbr.bssid, fh->bssid, 6);
    				nbr.bssid_info = htonl(fh->bssid_info);
    				nbr.channel = fh->channel;
    				nbr.reg = fh->reg;
    				nbr.phy = fh->phy;
    
    				btostr((unsigned char *)&nbr, sizeof(nbr), nbrstr);
    			}
    		}
    	}
    
    	return 0;
    }
    
    #endif
    
    static void cntlr_bcn_metrics_parse(struct uloop_timeout *t)
    {
    	trace("%s %d\n", __func__, __LINE__);
    	struct sta *s = container_of(t, struct sta, bcn_metrics_timer);
    	struct bcn_metrics *best = NULL, *b;
    	struct node *n = s->fh->agent;
    	struct controller *c = n->cntlr;
    	int i;
    
    	dbg("%s %d for "MACFMT" attached to bssid " MACFMT "\n", __func__, __LINE__, MAC2STR(s->macaddr), MAC2STR(s->bssid));
    	dbg("%s %d node = "MACFMT"\n", __func__, __LINE__, MAC2STR(n->alid));
    
    	//for (i = 0; i < s->num_bcn_metrics; i++) {
    	list_for_each_entry(b, &s->bcnlist, list) {
    		dbg("%s %d bcn "MACFMT" \n", __func__, __LINE__,
    				MAC2STR(b->bssid));
    		if (!best) {
    			best = b;
    			continue;
    		}
    
    		dbg("%s %d best rcpi %u this rcpi %u\n", __func__, __LINE__,
    				best->rcpi, b->rcpi);
    
    		if ((best->rcpi - b->rcpi) > 10) {
    			dbg("%s %d new best bcn "MACFMT" with rcpi %d\n",
    					__func__, __LINE__,
    					MAC2STR(b->bssid),
    					b->rcpi);
    			best = b;
    		}
    	}
    
    	if (!best) {
    		dbg("%s %d\n", __func__, __LINE__);
    		return;
    	}
    
    	if (!hwaddr_is_zero(best->bssid) &&  memcmp(best->bssid, s->bssid, 6)) {
    		struct cmdu_buff *cmdu;
    		uint8_t stas[1][6];
    
    		if (!c->cfg.enable_sta_steer) {
    			trace("|%s:%d| better bssid found, but will not steer "MACFMT",\
    			       because the 'enable_sta_steer' is not set!\n",
    			       __func__, __LINE__, MAC2STR(s->macaddr));
    			return;
    		}
    
    		dbg("%s %d new bssid, wow! try to steer "MACFMT " " MACFMT " " MACFMT "\n", __func__,
    				__LINE__, MAC2STR(s->macaddr), MAC2STR(best->bssid), MAC2STR(s->bssid));
    
    		memcpy(stas[0], s->macaddr, 6);
    		cmdu = cntlr_gen_client_steer_request(c, s->fh->agent->alid,
    				s->bssid, 0, 1,	s->macaddr, 1, best->bssid, 1);
    		if (cmdu) {
    			send_cmdu(c, cmdu);
    			cmdu_free(cmdu);
    		}
    	}
    	dbg("%s %d\n", __func__, __LINE__);
    }
    
    
    struct sta *cntlr_add_sta(struct controller *c, uint8_t *macaddr)
    {
    	struct sta *s;
    
    	s = cntlr_find_sta(c, macaddr);
    	if (s)
    		return s;
    
    	s = calloc(1, sizeof(struct sta));
    	if (!s)
    		return NULL;
    
    	INIT_LIST_HEAD(&s->bcnlist);
    	memcpy(s->macaddr, macaddr, 6);
    	list_add(&s->list, &c->stalist);
    	s->bcn_metrics_timer.cb = cntlr_bcn_metrics_parse;
    	return s;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    static void forall_but_node_restrict_sta(struct controller *c, struct node *ex,
    							uint8_t *sta)
    {
    #define sc_sz	(sizeof(struct cmd_assoc_control))
    	unsigned char sccmd[sc_sz] = {0};
    	struct cmd_assoc_control *asc = (struct cmd_assoc_control *)sccmd;
    	char sccmd_str[2 * sc_sz + 1] = {0};
    	struct node *n;
    
    	asc->len = htonl(sizeof(asc));
    	asc->enable = htonl(1);
    	asc->duration = htonl(0);   /* use fh-iface's assoc_ctrl_time */
    	asc->num = htonl(1);
    	memcpy(asc->stas, sta, 6);
    	btostr(sccmd, sc_sz, sccmd_str);
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (n == ex)
    			continue;
    	}
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static int cntlr_monitor_sta(struct controller *c, uint8_t *sta,
    						struct node *sta_node)
    {
    	char sta_macstr[12 + 1] = {0};
    	struct node *n;
    
    	btostr((unsigned char *)sta, 6, sta_macstr);
    	list_for_each_entry(n, &c->nodelist, list) {
    		/* skip monitoring on sta's current node */
    		if (n == sta_node)
    			continue;
    
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;
    
    	}
    
    	return 0;
    }
    
    #endif
    
    static void forall_node_get_bcn_metrics(struct controller *c)
    {
    	struct sta *s;
    
    	list_for_each_entry(s, &c->stalist, list) {
    		struct cmdu_buff *cmdu;
    
    		cmdu = cntlr_gen_sta_metric_query(c, s->fh->agent->alid, s->macaddr);
    		if (!cmdu)
    			continue;
    
    		send_cmdu(c, cmdu);
    		cmdu_free(cmdu);
    	}
    }
    
    static void forall_node_get_fhinfo(struct controller *c)
    {
    	struct node *n;
    
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    static void node_get_monitor_sta(void *cntlr, void *resp, int len, void *cookie)
    {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	struct controller *c = (struct controller *)cntlr;
    	/* struct node *n = (struct node *)cookie; */
    	struct cmd_monitor_sta *info;
    #define SZ sizeof(struct cmd_monitor_sta)
    	unsigned char rbytes[SZ] = {0};
    	struct active_sta *entry;
    	int nbr_rssi = 0;
    
    	cntlr_dbg("%s(): received: %s\n", __func__, (char *)resp);
    	strtob(resp, len, rbytes);
    	info = (struct cmd_monitor_sta *)rbytes;
    	/* TODO: info->rssi[0..3] */
    	nbr_rssi = (info->rssi[0] > 0) ? info->rssi[0] - 256 : info->rssi[0];
    	info->seen = ntohl(info->seen);
    
    	if (hwaddr_is_zero(info->addr) || nbr_rssi == 0) {
    		cntlr_dbg("Invalid GET_MONITOR resp!!!\n");
    		return;
    	}
    
    	entry = as_lookup(c->as_table, info->addr);
    	if (!entry)
    		return;
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	cntlr_dbg("STA " MACFMT " old best nbr-rssi = %d  nbr-rssi = %d " \
    			"seen = %d secs ago\n",
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			MAC2STR(entry->hwaddr), entry->best_nbr_rssi,
    			nbr_rssi, info->seen);
    
    	if (entry->best_nbr_rssi < nbr_rssi &&
    			(info->seen >= 0 && info->seen < 30)) {
    
    		entry->best_nbr_rssi = nbr_rssi;
    		memcpy(entry->best_nbr, info->bss, 6);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		cntlr_dbg("STA " MACFMT " better neighbor is " MACFMT \
    				" with rssi %d\n",
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				MAC2STR(info->addr),
    				MAC2STR(entry->best_nbr),
    				entry->best_nbr_rssi);
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static int cntlr_handle_sta_rssi_low(struct controller *c, struct node *sta_n,
    					uint8_t *sta, int lowrssi)
    {
    	struct node *n;
    	char sta_macstr[12 + 1] = {0};
    	struct active_sta *s_entry;
    
    	if (hwaddr_is_zero(sta))
    		return 0;
    
    	s_entry = as_lookup(c->as_table, sta);
    	if (!s_entry)
    		return -1;
    
    	/* reset lowrssi and best_nbr */
    	s_entry->lowrssi = lowrssi;
    	s_entry->best_nbr_rssi = -127;
    	memset(s_entry->best_nbr, 0, 6);
    
    	/* get this sta's monitored data from other nodes,
    	 * if any available.
    	 */
    	btostr((unsigned char *)sta, 6, sta_macstr);
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (n == sta_n)
    			continue;   /* skip sta's currently assoc'd node */
    
    	}
    
    	cntlr_dbg("%s: After doing get monitor sta .......\n", __func__);
    	if (hwaddr_is_zero(s_entry->best_nbr))
    		return 0;
    
    #define lowrssi_thresh	-65
    #define diffrssi	9
    
    	if (lowrssi < lowrssi_thresh &&
    		(s_entry->best_nbr_rssi != -127 &&
    		 s_entry->best_nbr_rssi - lowrssi >= diffrssi)) {
    
    		/* found a better neighbor for this lowrssi sta */
    #define ss_sz	(sizeof(struct cmd_steer_sta) + 6)
    		unsigned char sscmd[ss_sz] = {0};
    		struct cmd_steer_sta *ss = (struct cmd_steer_sta *)sscmd;
    		char sscmd_str[2 * ss_sz + 1] = {0};
    		struct node *best_nbr_node;
    
    
    		/* 1. assoc_control STA on all nodes but the best_nbr node */
    		best_nbr_node = get_node_by_bssid(c, s_entry->best_nbr);
    		if (best_nbr_node)
    			forall_but_node_restrict_sta(c, best_nbr_node, sta);
    
    
    		/* 2. send steer mandate */
    		memcpy(ss->sta, sta, 6);
    		ss->num = htonl(1);
    		memcpy(ss->nbrs, s_entry->best_nbr, 6);
    		btostr(sscmd, ss_sz, sscmd_str);
    
    		cntlr_dbg("CMD_STEER in agent with best nbr +++++++++++>\n");
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    	return 0;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static bool endpoint_hwaddr(struct controller *c, unsigned char *sta)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    
    		if (!memcmp(n->alid, sta, 6))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			return false;
    	}
    
    	return true;
    }
    
    static void cntlr_handle_assoclist_resp(void *cntlr,
    					void *resp,
    					int len,
    					void *cookie)
    {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	struct controller *c = (struct controller *)cntlr;
    	struct netif_fhbss *p, *fh = (struct netif_fhbss *)cookie;
    	unsigned char rbytes[sizeof(struct cmd_get_assoclist)] = {0};
    	unsigned char *bssid = fh->bssid;
    	struct cmd_get_assoclist *info;
    	bool found = false;
    	int i, num_sta;
    	struct node *n;
    
    	/* cntlr_dbg("%s(): received: %s\n", __func__, (char *)resp); */
    	strtob(resp, len, rbytes);
    	info = (struct cmd_get_assoclist *)rbytes;
    	num_sta = ntohl(info->num);
    
    	/* TODO: include node pointer in netif_fhbss, so that
    	 * we don't have to parse for node from fh.
    	 */
    	list_for_each_entry(n, &c->nodelist, list) {
    
    		list_for_each_entry(p, &n->iflist, list) {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			if (p == fh) {
    				found = true;
    				break;
    			}
    		}
    	}
    
    	if (!found)
    		n = NULL;
    
    	for (i = 0; i < num_sta; i++) {
    		unsigned char *macaddr = &info->sta[6 * i];
    
    		if (!endpoint_hwaddr(c, macaddr))
    			continue;
    
    		as_insert(c->as_table, macaddr, bssid);
    
    		if (n) {
    			cntlr_dbg("Start Monitoring for STA " MACFMT " ....\n",
    						MAC2STR(macaddr));
    			cntlr_monitor_sta(c, macaddr, n);
    		}
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    /* XXX: now doing for only the first fh-iface of a node */
    static void forall_node_get_assoclist(struct controller *c)
    {
    	struct node *n;
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	list_for_each_entry(n, &c->nodelist, list) {
    		struct netif_fhbss *p;
    		struct cmd_get_assoclist req;
    		char reqbuf[2 * sizeof(struct cmd_get_assoclist) + 1] = {0};
    
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;
    
    
    		p = list_first_entry(&n->iflist,
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    				struct netif_fhbss, list);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		if (!p)
    			continue;
    
    		memset(&req, 0, sizeof(req));
    		strncpy(req.ifname, p->ifname, 16);
    		btostr((unsigned char *)&req, sizeof(req), reqbuf);
    
    
    		if (ret)
    			cntlr_dbg("Failed to get assoclist (ret = %d)\n", ret);
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    int invoke_disconnect_sta(struct node *n, struct netif_iface *p,
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		unsigned char *hwaddr)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    int invoke_disconnect_sta_by_bssid(struct controller *c, unsigned char *bssid,
    						unsigned char *hwaddr)
    {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	int ret = 0;
    	struct node *n;
    	struct netif_fhbss *p;
    
    	n = get_node_by_bssid(c, bssid);
    	if (!n)
    		return -1;
    
    
    	list_for_each_entry(p, &n->iflist, list) {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		if (memcmp(bssid, p->bssid, 6))
    			continue;
    
    		invoke_disconnect_sta(n, p, hwaddr);
    
    		break;
    	}
    
    	return ret;
    
    struct netif_iface *cntlr_radio_add_interface(struct controller *c,
    		struct netif_radio *r, uint8_t *hwaddr)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct netif_iface *n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	n = find_interface_by_mac(c, r, hwaddr);
    	if (n) {
    		n->active = true;
    		n->band = r->band;
    		return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    
    	n = calloc(1, sizeof(*n));
    	if (!n)
    		return NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	memcpy(n->bssid, hwaddr, 6);
    	n->type = NETIF_FHBSS;
    	n->active = true;
    	list_add(&n->list, &r->iflist);
    	n->agent = r->agent;
    	n->band = r->band;
    	return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    struct netif_radio *cntlr_node_add_radio(struct controller *c, struct node *n,
    		uint8_t *radio)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct netif_radio *r;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	r = find_radio_by_node(c, n, radio);
    	if (r)
    		return r;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	r = calloc(1, sizeof(*r));
    	if (!r)
    		return NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	memcpy(r->macaddr, radio, 6);
    	INIT_LIST_HEAD(&r->iflist);
    	list_add(&r->list, &n->radiolist);
    	r->agent = n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    struct node *alloc_node_init(struct controller *c, uint8_t *hwaddr)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct node *n;
    
    	struct cmdu_buff *cmdu;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	n = find_node_by_mac(c, hwaddr);
    	if (n)
    		return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	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->ap = c->cfg.apolicy;
    
    	memcpy(n->alid, hwaddr, 6);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	//INIT_LIST_HEAD(&n->stalist);
    	INIT_LIST_HEAD(&n->radiolist);
    	list_add(&n->list, &c->nodelist);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	dbg("%s %d --------------------------- " MACFMT "\n", __func__, __LINE__, MAC2STR(hwaddr));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	cmdu = cntlr_gen_bk_caps_query(c, n->alid);
    	if (!cmdu)
    		return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	send_cmdu(c, cmdu);
    	cmdu_free(cmdu);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	cmdu = cntlr_gen_ap_capability_query(c, n->alid);
    	if (!cmdu)
    		return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	send_cmdu(c, cmdu);
    	cmdu_free(cmdu);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    void free_bcn_metrics(struct controller *c, struct sta *s)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct bcn_metrics *b, *tmp;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	list_for_each_entry_safe(b, tmp, &s->bcnlist, list) {
    		list_del(&b->list);
    		free(b);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    }
    
    void free_watchnode_cleanup(struct controller *c, struct watchnode *wn)
    {
    	uloop_timeout_cancel(&wn->scan_timer);
    	uloop_timeout_cancel(&wn->scanres_timer);
    	list_del(&wn->list);
    	free(wn);
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    void free_node_cleanup(struct controller *c, struct node *n)
    {
    #if 0
    	uloop_timeout_cancel(&n->refresh_timer);
    #endif
    
    	ubus_unregister_event_handler(c->ubus_ctx, &n->evh);
    
    	list_flush(&n->iflist, struct netif_iface, list);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	free(n);
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static int topology_add_node(struct controller *c, char *prefix)
    {
    	struct node *n;
    	struct in_addr ipn;
    
    	if (!prefix) {
    		cntlr_dbg("%s: prefix = NULL!\n", __func__);
    		return -1;
    	}
    
    	if (strlen(prefix) && !inet_aton(prefix, &ipn)) {
    		cntlr_dbg("Invalid prefix: %s\n", prefix);
    		return -1;
    	}
    
    	if (strlen(prefix))
    		n = find_node_by_ip(c, prefix);
    	else
    		n = find_node_by_ip(c, "127.0.0.1");
    
    	if (n) {
    		/* This node is already in our list.
    		 * If the comm path to it is still
    		 * alive, then we can discard add.
    		 */
    		/* TODO: check if uobj[4] is valid? */
    		return 0;
    	}
    
    	info("Alloc new node ### for ipaddr '%s'\n",
    				strlen(prefix) ? prefix : "127.0.0.1");
    	n = alloc_node_init(c, prefix);
    	if (!n)
    		return -1;
    
    	if (strlen(prefix)) {
    		struct active_sta *as;
    
    
    		hwaddr_from_ip(c->cfg.al_bridge, prefix, n->hwaddr);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		info("%s: " MACFMT "\n", __func__, MAC2STR(n->hwaddr));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    		/* TODO
    		 * if (node_uplink_wifi(c, n->hwaddr))
    		 *	n->ul_type = UL_WIFI;
    		 */
    
    		/* Remove this backhaul iface macaddr from active STA list */
    		as = as_lookup(c->as_table, n->hwaddr);
    		if (as)
    			as_clean_entry(c->as_table, n->hwaddr, as->bssid);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	} else
    		info("%s: (local) " MACFMT "\n", __func__, MAC2STR(n->hwaddr));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	list_add(&n->list, &c->nodelist);
    	c->num_nodes++;
    
    	return 0;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    static int cntlr_cmd_set_channel(struct controller *c,
    					void *agent,
    					uint32_t ch,
    					uint32_t bw)
    {
    #define sz  (sizeof(struct cmd_param) + 4 + 4)
    	unsigned char chbuf[sz] = {0};
    	char chbufstr[2 * sz + 1] = {0};
    	struct cmd_param_channel {
    		struct cmd_param p;
    		uint32_t ch;
    		uint32_t bw;
    	} *pch = (struct cmd_param_channel *)chbuf;
    
    	sprintf(pch->p.type, "%s", "channel");
    	pch->p.len = htonl(8);   /* channel, bw */
    	pch->ch = htonl(ch);
    	pch->bw = htonl(bw);
    	btostr(chbuf, sz, chbufstr);
    	dbg("cmd_set_param: %s\n", chbufstr);
    	CMD(c->comm, c, agent, CMD_SET_PARAM,
    			chbufstr, sizeof(chbufstr), NULL, NULL);
    
    	return 0;
    }
    
    static void cntlr_radar_exit(struct uloop_timeout *t)
    {
    	/*TODO: before change channel due to radar, save old chandef.
    	 * Restore that chandef upon exit from radar nop.
    	 */
    
    #if 0 /* Not setting chandef to auto due to reason above */
    	struct controller *c = container_of(t, struct controller, radar_timer);
    	struct node *n;
    
    
    	list_for_each_entry(n, &c->nodelist, list) {
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;
    
    		cntlr_dbg("Sending SET_CHANNEL to node (%p:%p)---->\n",
    						n, n->uobj[4]);
    		/* auto channel */
    		cntlr_cmd_set_channel(c, (void *)n->uobj[4], 0, 80);
    	}
    #endif
    }
    
    static int cntlr_handle_radar_alert(struct controller *c, struct watchnode *wn)
    {
    	struct node *n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	list_for_each_entry(n, &c->nodelist, list) {
    		/* skip watchnode as CMD for it will fail */
    		if (!memcmp(n->hwaddr, wn->hwaddr, 6))	/* TODO: verify this */
    			continue;
    
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;
    
    		cntlr_dbg("Sending SET_CHANNEL to node (%p:%p)---->\n",
    						n, n->uobj[4]);
    		/* switch to a fixed non-dfs channel */
    		cntlr_cmd_set_channel(c, (void *)n->uobj[4], 36, 80);
    	}
    
    	uloop_timeout_set(&c->radar_timer, 3600 * 1000);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	return 0;
    }
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    static void watchlist_scanres_node_dump(void *cntlr, void *resp, int len,
    								void *cookie)
    {
    	struct controller *c = (struct controller *)cntlr;
    	struct watchnode *wn = (struct watchnode *)cookie;
    	unsigned char rbytes[sizeof(struct wifi_bss_detail)] = {0};
    	struct wifi_bss_detail *bx;
    	unsigned char radar_ie[9] = "\xdd\x0b\x00\x22\x07\xdd\x06\xaa\x04";
    	int ret;
    
    	/* cntlr_dbg("%s(): received: %s\n", __func__, (char *)resp); */
    	strtob(resp, len, rbytes);
    	bx = (struct wifi_bss_detail *)rbytes;
    	bx->ielen = ntohl(bx->ielen);
    	if (!memmem(bx->ie, bx->ielen, radar_ie, sizeof(radar_ie))) {
    		cntlr_dbg("radar ie not present\n");
    		if (++wn->scan_retry > WATCHNODE_SCAN_RETRY_MAX) {
    			free_watchnode_cleanup(c, wn);
    			return;
    		}
    
    		uloop_timeout_set(&wn->scan_timer, 30 * 1000);
    		return;
    	}
    
    	info("Node " MACFMT " has detected radar\n", MAC2STR(wn->hwaddr));
    	ret = cntlr_handle_radar_alert(c, wn);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	if (!ret)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		free_watchnode_cleanup(c, wn);
    }
    
    static void watchlist_scanres_node(struct uloop_timeout *t)
    {
    	struct watchnode *wn = container_of(t, struct watchnode, scanres_timer);
    	struct controller *c = wn->cntlr;
    	char bssidstr[12 + 1] = {0};
    	struct node *n;
    	int ret = 0;
    
    	btostr(wn->fh_bssid, sizeof(wn->fh_bssid), bssidstr);
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		/* skip watchnode as CMD for it will fail */
    		if (!memcmp(n->hwaddr, wn->hwaddr, 6))	/* TODO: verify this */
    			continue;
    
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;
    
    	}
    
    	if (ret && ++wn->scan_retry < WATCHNODE_SCAN_RETRY_MAX) {
    		cntlr_dbg("Will retry-%d watchnode scan\n", wn->scan_retry);
    		uloop_timeout_set(&wn->scan_timer, 20 * 1000);
    	}
    }
    
    static void watchlist_scan_node(struct uloop_timeout *t)
    {
    	struct watchnode *wn = container_of(t, struct watchnode, scan_timer);
    	struct controller *c = wn->cntlr;
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		struct scan_param sp;
    		char spbuf[2*sizeof(struct scan_param) + 1] = {0};
    
    		/* skip watchnode as CMD for it will fail */
    		if (!memcmp(n->hwaddr, wn->hwaddr, 6))
    			continue;
    
    
    		if (list_empty(&n->iflist))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			continue;