Skip to content
Snippets Groups Projects
cntlr.c 53.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 <map1905/map2.h>
    #include <map1905/maputils.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 "hlist.h"
    #include "allsta.h"
    #include "cntlr_ubus.h"
    
    #include "cntlr_map.h"
    
    #include "cntlr_cmdu_generator.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    static int update_fronthaul_bsslist(struct node *n, struct netif_fhbss *fh);
    static void node_getbssinfo(void *cntlr, void *resp, int len, void *cookie);
    static int enumerate_topology_indirect(struct controller *c);
    
    
    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);
    	ubus_unregister_event_handler(c->ubus_ctx, &c->ubusx_ev);
    	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);
    	exit_alloctrace();
    	stop_logging();
    	//unlink(pidfile);
    	exit(0);
    }
    
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    /* find node by macaddress */
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    static struct node *find_node_by_mac(struct controller *c,
    		const unsigned char *mac)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct node *p;
    
    	list_for_each_entry(p, &c->nodelist, list) {
    		if (!memcmp(p->hwaddr, mac, 6))
    			return p;
    	}
    
    	return NULL;
    }
    
    /* 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;
    }
    
    /* 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_fhbss *p;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		list_for_each_entry(p, &n->fronthaul_iflist, list) {
    			if (memcmp(bssid, p->bssid, 6))
    				continue;
    
    			return n;
    		}
    	}
    
    	return NULL;
    }
    
    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 |  & = %p ,  agent = %p,  prefix = '%s')\n",
    						i++, n, n->uobj[4],
    						inet_ntoa(n->ipaddr));
    	}
    }
    
    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);
    		cntlr_dbg("Sending DEL_NEIGHBOR Async to node (%p:%p)---->\n",
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    				n, n->uobj[4]);
    
    		CMD(c->comm, c, (void *)(uintptr_t)n->uobj[4],
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				CMD_DEL_NEIGHBOR,
    				nbrstr, sizeof(nbrstr),
    				NULL, NULL);
    		return 0;
    	}
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		list_for_each_entry(p, &c->nodelist, list) {
    			struct netif_fhbss *fh;
    
    			if (n == p)
    				continue;
    
    			list_for_each_entry(fh, &p->fronthaul_iflist, list) {
    				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);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    				cntlr_dbg("Sending ADD_NEIGHBOR to node " \
    						" (%p:%p)---->\n",
    						n, n->uobj[4]);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				CMD(c->comm, c, (void *)n->uobj[4],
    						CMD_ADD_NEIGHBOR,
    						&nbrstr, 2 * sizeof(nbr) + 1,
    						NULL, NULL);
    			}
    		}
    	}
    
    	return 0;
    }
    
    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;
    
    		cntlr_dbg("Send ASSOC_CONTROL to node (%p:%p)---->\n",
    						n, n->uobj[4]);
    		CMD(c->comm, c, (void *)n->uobj[4],
    				CMD_ASSOC_CONTROL,
    				&sccmd_str,
    				sizeof(sccmd_str),
    				NULL, NULL);
    	}
    }
    
    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->fronthaul_iflist))
    			continue;
    
    		cntlr_dbg("Send MONITOR_STA to node (%p:%p)---->\n",
    						n, n->uobj[4]);
    		CMD(c->comm, c, (void *)n->uobj[4],
    				CMD_MONITOR_STA,
    				&sta_macstr, sizeof(sta_macstr),
    				NULL, NULL);
    	}
    
    	return 0;
    }
    
    static void node_get_monitor_sta(void *cntlr, void *resp, int len, void *cookie)
    {
    	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);
    	}
    }
    
    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("Send GET_MONITOR_STA to node (%p:%p)---->\n",
    						n, n->uobj[4]);
    		CMD(c->comm, c, (void *)n->uobj[4],
    				CMD_GET_MONITOR_STA,
    				&sta_macstr, sizeof(sta_macstr),
    				node_get_monitor_sta, n);
    	}
    
    	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");
    		cntlr_dbg("Send STEER to node (%p:%p)---->\n",
    						sta_n, sta_n->uobj[4]);
    		CMD(c->comm, c, (void *)sta_n->uobj[4],
    				CMD_STEER, &sscmd_str, sizeof(sscmd_str),
    				NULL, NULL);
    	}
    
    	return 0;
    }
    
    static bool endpoint_hwaddr(struct controller *c, unsigned char *sta)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (!memcmp(n->hwaddr, sta, 6))
    			return false;
    	}
    
    	return true;
    }
    
    static void cntlr_handle_assoclist_resp(void *cntlr,
    					void *resp,
    					int len,
    					void *cookie)
    {
    	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->fronthaul_iflist, list) {
    			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);
    		}
    	}
    }
    
    /* XXX: now doing for only the first fh-iface of a node */
    static void forall_node_get_assoclist(struct controller *c)
    {
    	struct node *n;
    	int ret;
    
    	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->fronthaul_iflist))
    			continue;
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		p = list_first_entry(&n->fronthaul_iflist,
    				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);
    
    		ret = CMD(c->comm, c, (void *)n->uobj[4],
    				CMD_GET_ASSOCLIST,
    				reqbuf, sizeof(reqbuf),
    				cntlr_handle_assoclist_resp,
    				p);
    
    		if (ret)
    			cntlr_dbg("Failed to get assoclist (ret = %d)\n", ret);
    	}
    }
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    int invoke_disconnect_sta(struct node *n, struct netif_fhbss *p,
    		unsigned char *hwaddr)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct cmd_disconnect_sta ds = {0};
    	char s[2*sizeof(struct cmd_disconnect_sta) + 1] = {0};
    	int ret;
    
    	memcpy(ds.hwaddr, hwaddr, sizeof(ds.hwaddr));
    	strncpy(ds.iface, p->ifname, 16);
    
    	btostr((unsigned char *)&ds, sizeof(struct cmd_disconnect_sta), s);
    
    	ret = CMD(n->cntlr->comm, n->cntlr, (void *)n->uobj[4],
    			CMD_DISCONNECT_STA, s, sizeof(s), NULL, NULL);
    	return ret;
    }
    
    int invoke_disconnect_sta_by_bssid(struct controller *c, unsigned char *bssid,
    						unsigned char *hwaddr)
    {
    	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->fronthaul_iflist, list) {
    		if (memcmp(bssid, p->bssid, 6))
    			continue;
    
    		invoke_disconnect_sta(n, p, hwaddr);
    
    		break;
    	}
    
    	return ret;
    }
    
    
    static void disconnect_repeated_macs(struct controller *c, struct node *n,
    							unsigned char *sta)
    {
    	struct active_sta *as;
    	int hidx = as_hash(sta);
    
    	hlist_for_each_entry(as, &c->as_table[hidx], hlist) {
    		if (as->hwaddr[3] ^ sta[3] ||
    				as->hwaddr[4] ^ sta[4] ||
    				as->hwaddr[5] ^ sta[5])
    			continue;
    
    		if (as->hwaddr[1] != n->mobility_domain[0] ||
    				as->hwaddr[2] != n->mobility_domain[1])
    			continue;
    
    		if (!match_oui0((unsigned char *)n->oui0, as->hwaddr, n->ouis))
    			continue;
    
    		if (memcmp(sta, as->hwaddr, 6) == 0)
    			continue;
    
    		dbg("Disconnecting redundant repeated client " MACFMT "\n",
    				MAC2STR(as->hwaddr));
    
    		invoke_disconnect_sta_by_bssid(c, as->bssid, as->hwaddr);
    		invoke_disconnect_sta_by_bssid(c, as->old_bssid, as->hwaddr);
    	}
    }
    
    static int parse_sta_event(struct controller *c, const char *evtype,
    						struct blob_attr *msg)
    {
    	struct netif_fhbss *p;
    	char macaddr[18] = {0}, action[64] = {0}, vif[16] = {0};
    	unsigned char sta_macaddr[6], bssid[6] = {0};
    	bool found = false;
    	struct active_sta *as;
    	uint32_t lowrssi = 0;
    	int rv;
    	enum {
    		MACADDR,
    		ACTION,
    		VIF,
    		LOWRSSI,
    		__POLICY_MAX
    	};
    	const struct blobmsg_policy sta_policy[__POLICY_MAX] = {
    		[MACADDR] = {.name = "macaddr", .type = BLOBMSG_TYPE_STRING},
    		[ACTION] = {.name = "action", .type = BLOBMSG_TYPE_STRING},
    		[VIF] = {.name = "vif", .type = BLOBMSG_TYPE_STRING},
    		[LOWRSSI] = {.name = "lowrssi", .type = BLOBMSG_TYPE_INT32},
    	};
    	struct blob_attr *tb[__POLICY_MAX];
    
    	char sta_node[128] = {0};
    	char *sta_p;
    	struct in_addr ipn;
    	struct node *n;
    
    	sta_p = strstr(evtype, "wifi.sta");
    	snprintf(sta_node, sta_p - evtype, "%s", evtype);
    	if (strlen(sta_node) && !inet_aton(sta_node, &ipn)) {
    		warn("Invalid 'wifi.sta' prefix: %s\n", sta_node);
    		return -1;
    	}
    
    	if (strlen(sta_node))
    		n = find_node_by_ip(c, sta_node);
    	else
    		n = find_node_by_ip(c, "127.0.0.1");
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	rv = blobmsg_parse(sta_policy, __POLICY_MAX, tb,
    			blobmsg_data(msg), blobmsg_len(msg));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (rv < 0) {
    		cntlr_dbg("Error parsing wifi.sta events!\n");
    		goto fail;
    	}
    
    	if (!tb[MACADDR] || !tb[VIF] || !tb[ACTION])
    		goto fail;
    
    	strncpy(macaddr, blobmsg_get_string(tb[MACADDR]), 17);
    	if (hwaddr_aton(macaddr, sta_macaddr) == NULL)
    		goto fail;
    
    	strncpy(vif, blobmsg_get_string(tb[VIF]), 15);
    
    	if (tb[LOWRSSI]) {
    		lowrssi = blobmsg_get_u32(tb[LOWRSSI]);
    		if (lowrssi != 0) {
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    			cntlr_dbg("Received wifi.sta 'lowrssi' (%d) event " \
    					"+++++++++++++>\n", lowrssi);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			return cntlr_handle_sta_rssi_low(c, n, sta_macaddr, lowrssi);
    		}
    	}
    
    	strncpy(action, blobmsg_get_string(tb[ACTION]), 63);
    
    	/* At this instant when the associated STA is checked to see if it is
    	 * backhaul wifi-iface of a node, the nodelist does not include this
    	 * STA.
    	 * We check again for pure STA from wifi.agent objects' appear/disappear
    	 * event handler.
    	 */
    	if (!endpoint_hwaddr(c, sta_macaddr)) {
    		cntlr_dbg("STA %s is NOT a pure STA!\n", macaddr);
    		goto fail;
    	}
    
    	/* TODO: wifi.sta events should contain bssid too */
    	list_for_each_entry(p, &n->fronthaul_iflist, list) {
    		if (strncmp(p->ifname, vif, 16) != 0)
    			continue;
    
    		memcpy(bssid, p->bssid, 6);
    		found = true;
    		break;
    	}
    
    	if (!found)
    		goto fail;
    
    	if (strncmp(action, "disassociated", strlen("disassociated") + 1) == 0) {
    		as = as_lookup(c->as_table, sta_macaddr);
    
    		/* if disassociate event came from most recently recorded bssid,
    		 * we must have missed the disassociate event from previous one
    		 */
    		if (as && memcmp(as->bssid, bssid, 6) == 0)
    			invoke_disconnect_sta_by_bssid(n->cntlr,
    							as->old_bssid,
    							as->hwaddr);
    
    		as_clean_entry(c->as_table, sta_macaddr, bssid);
    	} else if (strncmp(action, "associated", strlen("associated") + 1) == 0) {
    		as = as_lookup(c->as_table, sta_macaddr);
    		if (as) {
    			if (memcmp(as->bssid, bssid, 6) != 0) {
    				struct node *prev_ap;
    				char hwaddrstr[12 + 1] = {0};
    
    				/* if entry already exists, it must not have
    				 * receieved dissassoc event;
    				 * issue disconnect_sta to previous bssid.
    				 */
    				invoke_disconnect_sta_by_bssid(n->cntlr,
    								as->bssid,
    								as->hwaddr);
    
    				/* flush from arp table of previous bssid */
    				prev_ap = get_node_by_bssid(c, as->bssid);
    				if (!prev_ap)
    					return -1;
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    				btostr(as->hwaddr, sizeof(as->hwaddr),
    						hwaddrstr);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				CMD(prev_ap->cntlr->comm, prev_ap->cntlr,
    					(void *)prev_ap->uobj[4],
    					CMD_FLUSH_ARP,
    					hwaddrstr, sizeof(hwaddrstr),
    					NULL, NULL);
    			}
    		}
    
    		/* disconnect redundant repeated addresses if real mac is seen
    		 * locally, or a repeated mac is seen
    		 */
    		/* TODO TODO: check this logic.. again */
    		if ((sta_macaddr[1] != n->mobility_domain[0] ||
    			sta_macaddr[2] != n->mobility_domain[1]) ||
    			(sta_macaddr[1] == n->mobility_domain[0] &&
    			 sta_macaddr[2] == n->mobility_domain[1] &&
    			match_oui0(n->oui0, sta_macaddr, n->ouis))) {
    
    			disconnect_repeated_macs(c, n, sta_macaddr);
    		}
    
    		as = as_insert(c->as_table, sta_macaddr, bssid);
    
    		/* instruct agents to start monitoring this sta */
    		cntlr_dbg("Start Monitoring for STA " MACFMT " ....\n",
    				MAC2STR(sta_macaddr));
    		cntlr_monitor_sta(c, sta_macaddr, n);
    	}
    
    	as_print(c->as_table);
    	return 0;
    fail:
    	return -1;
    }
    
    static void node_event_handler(struct ubus_context *ctx,
    					struct ubus_event_handler *ev,
    					const char *type,
    					struct blob_attr *msg)
    {
    	struct node *n = container_of(ev, struct node, evh);
    	char *str;
    
    	str = blobmsg_format_json(msg, true);
    	if (!str)
    		return;
    
    	cntlr_dbg("[ &node = %p ] Received event = '%s': %s\n", n, type, str);
    	if (strstr(type, "wifi.sta"))
    		parse_sta_event(n->cntlr, type, msg);
    
    	/* handle other events as needed */
    
    	free(str);
    }
    
    static void system_info_resp_handler(struct ubus_request *req, int type,
    						struct blob_attr *msg)
    {
    	struct node *n = (struct node *)req->priv;
    	struct json_object *json_msg;
    	char *json_str;
    
    	loud("Response from peer: 0x%x\n", req->peer);
    	json_str = blobmsg_format_json(msg, true);
    	if (!json_str)
    		return;
    
    	json_msg = json_tokener_parse(json_str);
    	if (!json_msg)
    		goto out_str;
    
    	if (!json_object_is_type(json_msg, json_type_object))
    		goto out_json;
    
    	if (n->uobj[0] == req->peer) {
    		/* handle resp for system info */
    		json_object_object_foreach(json_msg, key, val) {
    			if (!strncmp(key, "system", 6)) {
    				struct json_object *uptime_obj;
    
    				json_object_object_get_ex(val, "uptime", &uptime_obj);
    				if (!uptime_obj &&
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    					!json_object_is_type(uptime_obj,
    							json_type_int)) {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    					continue;
    				}
    				/* TODO - in node data struct */
    				/* sys.attr.uptime = json_object_get_int(uptime_obj); */
    			} else if (!strncmp(key, "memoryKB", 8)) {
    				struct json_object *mtotal_obj, *mfree_obj;
    				int mtotal = 0, mfree = 0;
    
    				json_object_object_get_ex(val, "total", &mtotal_obj);
    				json_object_object_get_ex(val, "free", &mfree_obj);
    				mtotal = json_object_get_int(mtotal_obj);
    				mfree = json_object_get_int(mfree_obj);
    				/* sys.attr.memfree = mfree * 100 / mtotal; */
    			}
    		}
    	} else if (n->uobj[1] == req->peer) {
    		/* handle resp for system load */
    		json_object_object_foreach(json_msg, key, val) {
    			if (!strncmp(key, "load", 4)) {
    				struct json_object *l1_obj, *l5_obj, *l15_obj;
    
    				json_object_object_get_ex(val, "min_1", &l1_obj);
    				json_object_object_get_ex(val, "min_5", &l5_obj);
    				json_object_object_get_ex(val, "min_15", &l15_obj);
    				/* sys.attr.avgcpu_1 = json_object_get_int(l1_obj); */
    				/* sys.attr.avgcpu_5 = json_object_get_int(l5_obj); */
    				/* sys.attr.avgcpu_15 = json_object_get_int(l15_obj); */
    			}
    		}
    	}
    
    out_json:
    	json_object_put(json_msg);
    out_str:
    	free(json_str);
    
    }
    
    static int get_node_system_info(struct controller *c, struct node *n)
    {
    	struct blob_buf bb = {};
    	int ret;
    
    	blob_buf_init(&bb, 0);
    	loud("Request to peer: 0x%x\n", n->uobj[0]);
    	ret = ubus_invoke(c->ubus_ctx, n->uobj[0], "info", bb.head,
    			system_info_resp_handler, n, 2 * 1000);
    	if (ret)
    		cntlr_dbg("Failed to get node's sys data1 (ret = %d)\n", ret);
    	blob_buf_free(&bb);
    
    	blob_buf_init(&bb, 0);
    	loud("Request to peer: 0x%x\n", n->uobj[1]);
    	ret = ubus_invoke(c->ubus_ctx, n->uobj[1], "load", bb.head,
    			system_info_resp_handler, n, 2 * 1000);
    	if (ret)
    		cntlr_dbg("Failed to get node's sys data2 (ret = %d)\n", ret);
    	blob_buf_free(&bb);
    
    	return ret;
    }
    
    static void node_getoui(void *cntlr, void *resp, int len, void *cookie)
    {
    	unsigned char rbytes[sizeof(struct cmd_get_oui)] = {0};
    	struct node *n = (struct node *)cookie;
    	struct cmd_get_oui *oui;
    	int i;
    
    	strtob(resp, len, rbytes);
    	oui = (struct cmd_get_oui *) rbytes;
    	memcpy(n->oui0, oui->oui0, sizeof(n->oui0));
    	n->ouis = oui->ouis;
    
    	if (n->ouis)
    		dbg("Received %d different oui0s\n", n->ouis);
    	for (i = 0; i < n->ouis; i++)
    		dbg("oui0[%d] = %d\n", i, n->oui0[i]);
    }
    
    static void node_getmobid(void *cntlr, void *resp, int len, void *cookie)
    {
    	struct node *n = (struct node *)cookie;
    
    	strtob(resp, len, n->mobility_domain);
    	dbg("Received mobility domain = %02x%02x\n",
    			n->mobility_domain[0], n->mobility_domain[1]);
    }
    
    static int get_node_oui(struct controller *c, struct in_addr *ipn)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (memcmp(ipn, &n->ipaddr, sizeof(struct in_addr)))
    			continue;
    
    		CMD(c->comm, c, (void *)n->uobj[4], CMD_GET_OUI, NULL, 0,
    				node_getoui, n);
    	}
    
    	return 0;
    }
    
    static int get_node_mobid(struct controller *c, struct in_addr *ipn)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (memcmp(ipn, &n->ipaddr, sizeof(struct in_addr)))
    			continue;
    
    		CMD(c->comm, c, (void *)n->uobj[4], CMD_GET_MOBID, NULL, 0,
    				node_getmobid, n);
    	}
    
    	return 0;
    }
    
    static void forall_node_get_repeater_hwmasks(struct controller *c)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		CMD_ASYNC(c->comm, c, (void *)n->uobj[4], CMD_GET_MOBID, NULL,
    				0, node_getmobid, n);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    		CMD_ASYNC(c->comm, c, (void *)n->uobj[4], CMD_GET_OUI, NULL, 0,
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    				node_getoui, n);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    }
    
    static void forall_node_get_fhinfo(struct controller *c)
    {
    	struct node *n;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		cntlr_dbg("CMD GET_BSSINFO to agent @ (%p) " MACFMT "\n",
    				(void *)n->uobj[4], MAC2STR(n->hwaddr));
    
    		CMD_ASYNC(c->comm, c, (void *)n->uobj[4], CMD_GET_BSS_INFO,
    					NULL, 0, node_getbssinfo, n);
    	}
    }
    
    
    #if 0
    static void refresh_node_info(struct uloop_timeout *t)
    {
    	struct node *n = container_of(t, struct node, refresh_timer);
    	struct controller *c = n->cntlr;
    
    	/* get_node_system_info(c, n); */
    
    	/* get_node_wifi_info(c, n); */
    
    	CMD(c->comm, c, (void *)n->uobj[4], CMD_GET_BSS_INFO, NULL, 0,
    						node_getbssinfo, n);
    
    	uloop_timeout_set(&n->refresh_timer, NODE_STATS_INTERVAL);
    }
    #endif
    
    static int update_node(struct controller *c, struct node *n)
    {
    	char *uobj[] = {
    		"router.system", /* info */
    		"router.graph",  /* load */
    		"router.network", /* clients */
    		"wifix", /* radios, status, assoclist, stas, list_neighbor */
    		"wifi.agent", /* cmd, ... */
    		NULL };
    	int j;
    	const char *ipstr;
    
    	ipstr = inet_ntoa(n->ipaddr);
    	/* update refs to node's (ubus-x) objects */
    	for (j = 0; j < MAX_UOBJECTS && uobj[j]; j++) {
    		char uobjpath[128] = {0};
    		int ret;
    
    		snprintf(uobjpath, 128, "%s%s%s", ipstr,
    					strlen(ipstr) ? "/" : "", uobj[j]);
    		ret = ubus_lookup_id(c->ubus_ctx, uobjpath, &n->uobj[j]);
    		if (ret != UBUS_STATUS_OK) {
    
    			n->uobj[j] = OBJECT_INVALID;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			cntlr_dbg("object '%s' not present!\n", uobjpath);
    			continue;
    		}
    	}
    	return 0;
    }
    
    static struct node *alloc_node_init(struct controller *c, char *ipstr)
    {
    	struct node *n;
    	char *uobj[] = {
    		"router.system", /* info */
    		"router.graph",  /* load */
    		"router.network", /* clients */
    		"wifix", /* radios, status, assoclist, stas, list_neighbor */
    		"wifi.agent", /* cmd, ... */
    		NULL };
    	char *evmask[] = { "wifi*", "client*", NULL };
    	char ev[128] = {0};
    	int j;
    
    
    	n = calloc(1, sizeof(struct node));
    	if (!n) {
    		warn("OOM: node malloc failed!\n");
    		return NULL;
    	}
    	n->cntlr = c;
    	/* n->refresh_timer.cb = refresh_node_info; */
    	INIT_LIST_HEAD(&n->stalist);
    	INIT_LIST_HEAD(&n->fronthaul_iflist);
    	n->depth = -1;
    	if (ipstr && strlen(ipstr))
    		inet_pton(AF_INET, ipstr, &n->ipaddr);
    	else
    		inet_pton(AF_INET, "127.0.0.1", &n->ipaddr);
    
    	n->scan_supported = true;
    	/* get refs to node (ubus-x) objects we are interested in */
    	for (j = 0; j < MAX_UOBJECTS && uobj[j]; j++) {
    		char uobjpath[128] = {0};
    		int ret;
    
    		snprintf(uobjpath, 128, "%s%s%s", ipstr,
    					strlen(ipstr) ? "/" : "", uobj[j]);
    		ret = ubus_lookup_id(c->ubus_ctx, uobjpath, &n->uobj[j]);
    		if (ret != UBUS_STATUS_OK) {
    
    			n->uobj[j] = OBJECT_INVALID;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			cntlr_dbg("object '%s' not present!\n", uobjpath);
    			continue;