Skip to content
Snippets Groups Projects
cntlr.c 45.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	struct netif_link *l;
    
    
    	l = cntlr_find_link(c, upstream, downstream);
    
    	if (l)
    		return l;
    
    	l = calloc(1, sizeof(struct netif_link));
    	if (!l)
    		return NULL;
    
    	l->metrics = calloc(1, sizeof(struct link_metrics));
    	if (!l->metrics)
    
    	l->upstream = cntlr_find_iface(c, upstream);
    
    	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);
    
    static void cntlr_radar_exit(atimer_t *t)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	/*TODO: before change channel due to radar, save old chandef.
    	 * Restore that chandef upon exit from radar nop.
    	 */
    }
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    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];
    
    	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))
    
    		mid = (uint16_t)blobmsg_get_u16(tb[1]);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	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;
    
    		strncpy(tlvstr, (blobmsg_data(tb[5]) + 16), len);
    
    
    		strtob(tlvstr, len, tlv);
    		free(tlvstr);
    
    	cntlr_handle_map_event(c, type, mid, in_ifname, srcmac, origin, tlv, len);
    
    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);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    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;
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    		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;
    
    
    				       r->radio_el->macaddr, 6);
    
    
    				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; */
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    		list_for_each_entry(p, &c->cfg.nodelist, list) {
    
    #if (EASYMESH_VERSION > 2)
    	if ((diff & CONFIG_DIFF_QOS) && c->cfg.qos.enabled) {
    		struct node *n = NULL;
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		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);
    
    #if (EASYMESH_VERSION >= 6)
    
    	if ((diff & CONFIG_DIFF_AP_MLD) || (diff & CONFIG_DIFF_PUNCT_BITMAP)) {
    
    		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);
    	}
    
    #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;
    }
    
    bool cntlr_resync_config(struct controller *c, bool reload)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct node_policy *np = NULL;
    
    
    	diff = cntlr_config_reload(&c->cfg);
    
    
    	list_for_each_entry(np, &c->cfg.nodelist, list) {
    		struct node *n;
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		n = cntlr_find_node(c, np->agent_id);
    
    	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);
    
    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__);
    
    	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);
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    			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) {
    
    		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;
    
    			/* 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) {
    
    				if (!bss->bss->is_fbss && !bss->bss->is_bbss)
    					/* if bss is a bsta */
    
    				/* 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");
    
    		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);
    
    		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)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	cntlr_log_nodes(c);
    
    
    	//forall_node_get_fhinfo(c);   /* replaced from per-node refresh bss */
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	cntlr_get_all_sta_metrics(c);
    
    	/* TODO: */
    	//forall_node_get_usta_metrics(c);
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	/* TODO: update only when a node is added or removed */
    
    	//forall_node_update_neighbors(c);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	/* 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 = cntlr_gen_ap_autoconfig_search(c, 0x02, 0x01);
    
    	if (!cmdu)
    		return;
    
    	c->mid_5g = send_cmdu(c, 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;
    
    	str = blobmsg_format_json(msg, true);
    	trace("Received notification '%s'\n", str);
    
    	cntlr_ieee1905_cmdu_event_handler(priv, msg);
    
    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);
    
    static int cntlr_subscribe_for_cmdus(struct controller *c)
    
    	mapmodule_cmdu_mask_t cmdu_mask = {0};
    
    
    	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,
    
    Lejla Murselovic's avatar
    Lejla Murselovic committed
    			CMDU_AP_CAPABILITY_QUERY,
    			CMDU_AP_CAPABILITY_REPORT,
    
    			CMDU_CLIENT_CAPABILITY_REPORT,
    
    			CMDU_HIGHER_LAYER_DATA,
    
    			CMDU_BACKHAUL_STEER_RESPONSE,
    
    			CMDU_PROXIED_ENCAP_DPP,
    
    Filip Matusiak's avatar
    Filip Matusiak committed
    			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,
    
    	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));
    
    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)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct log_options *lopts = (struct log_options *)opts;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	struct controller *c;
    	struct ubus_context *ctx;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	sigemptyset(&base_mask);
    	sigaddset(&base_mask, SIGHUP);
    
    	sigprocmask(SIG_SETMASK, &base_mask, NULL);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	set_sighandler(SIGPIPE, SIG_IGN);
    
    	c = calloc(1, sizeof(struct controller));
    	if (!c)
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    		return;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	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);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	uloop_init();
    	ctx = ubus_connect(ubus_socket);
    	if (!ctx) {
    		err("Failed to connect to ubus\n");
    		free(c);
    
    		stop_logging();
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    		return;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    	c->ubus_ctx = ctx;
    	INIT_LIST_HEAD(&c->nodelist);
    
    	INIT_LIST_HEAD(&c->bcnreqlist);
    	c->num_nodes = 0;
    
    	INIT_LIST_HEAD(&c->linklist);
    
    	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;
    
    
    #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);
    
    
    		dpp_cntlr_read_uris(c);
    
    	cntlr_config_defaults(c, &c->cfg);
    
    
    	ret = cntlr_get_ieee1905_almac(c, c->almacaddr);
    
    	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) {
    
    			n = cntlr_add_node(c, np->agent_id);
    
    	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);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	uloop_run();
    
    #if (EASYMESH_VERSION > 2)
    #ifdef USE_LIBDPP
    	err("free dpp!!\n");
    	dpp_free(c->dpp);
    #endif
    #endif
    
    	cntlr_unload_steer_modules(c);
    
    Stanislaw Gruszka's avatar
    Stanislaw Gruszka committed
    	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);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	uloop_done();
    
    	stop_logging();
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }