Skip to content
Snippets Groups Projects
cntlr.c 49.3 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 <stdio.h>
    #include <stdlib.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include <sys/types.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include <unistd.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    
    #include <libubox/uloop.h>
    #include <libubus.h>
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #ifndef _GNU_SOURCE
    #define _GNU_SOURCE
    #endif
    
    #include <easy/easy.h>
    
    
    #include <cmdu.h>
    #include <1905_tlvs.h>
    
    #include <i1905_wsc.h>
    
    #include <map_module.h>
    
    #include <wifidefs.h>
    
    
    #include "timer.h"
    
    #if (EASYMESH_VERSION > 2)
    #include "dpp.h"
    #endif
    
    #include "wifi_dataelements.h"
    
    
    #include "utils/debug.h"
    #include "utils/utils.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "cntlr.h"
    #include "allsta.h"
    
    #include "allmac.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "cntlr_ubus.h"
    
    #include "cntlr_ubus_dbg.h"
    
    #include "cntlr_map.h"
    
    #include "cntlr_cmdu.h"
    
    #include "steer_module.h"
    
    #include "cntlr_acs.h"
    
    #include "wifi_opclass.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    #define map_plugin	"ieee1905.map"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    /* deprecated */
    struct netif_iface *_find_interface_by_mac(struct controller *c,
    		struct netif_radio *r, uint8_t *hwaddr)
    
    	struct netif_iface *p = NULL;
    
    	list_for_each_entry(p, &r->iflist, list) {
    		if (!memcmp(p->bss->bssid, hwaddr, 6))
    			return p;
    
    	}
    
    	return NULL;
    }
    
    /* find interface by macaddress */
    
    struct netif_iface *find_interface_by_mac(struct controller *c,
    		struct netif_radio *r, uint8_t *hwaddr)
    {
    
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, hwaddr, MAC_ENTRY_FBSS);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _find_interface_by_mac(c, r, hwaddr);
    
    	return (struct netif_iface*)entry->data;
    
    /* find radio by ssid, node known */
    struct netif_radio *_find_radio_by_ssid(struct node *n, char *ssid)
    
    	struct netif_iface *p = NULL;
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct netif_radio *r = NULL;
    
    	if (!n)
    		return NULL;
    
    	list_for_each_entry(r, &n->radiolist, list) {
    
    		list_for_each_entry(p, &r->iflist, list) {
    
    			if (!memcmp(p->bss->ssid, ssid, 33))
    
    /* find radio by ssid */
    struct netif_radio *find_radio_by_ssid(struct controller *c,
    		struct node *n, char *ssid)
    {
    	struct netif_radio *r = NULL;
    
    	if (n)
    		return _find_radio_by_ssid(n, ssid);
    
    	list_for_each_entry(n, &c->nodelist, list)
    		r = _find_radio_by_ssid(n, ssid);
    
    	return r;
    }
    
    
    /* find netif by ssid */
    struct netif_iface *find_interface_by_ssid(struct controller *c,
    		struct node *n, char *ssid)
    {
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct netif_radio *r = NULL;
    
    
    	list_for_each_entry(r, &n->radiolist, list) {
    
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    		struct netif_iface *p = NULL;
    
    
    		list_for_each_entry(p, &r->iflist, list) {
    
    			if (!memcmp(p->bss->ssid, ssid, 33))
    
    /* deprecated */
    struct netif_radio *_find_radio_by_node(struct controller *c, struct node *n,
    
    		uint8_t *radio_mac)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct netif_radio *p = NULL;
    
    
    	list_for_each_entry(p, &n->radiolist, list) {
    
    		if (!memcmp(p->radio_el->macaddr, radio_mac, 6))
    
    /* find radio by node */
    struct netif_radio *find_radio_by_node(struct controller *c, struct node *n,
    		uint8_t *radio_mac)
    {
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, radio_mac, MAC_ENTRY_RADIO);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _find_radio_by_node(c, n, radio_mac);
    
    	return (struct netif_radio*)entry->data;
    }
    
    /* deprecated */
    struct netif_radio *_find_radio_by_mac(struct controller *c, uint8_t *mac)
    
    {
    	struct node *n = NULL;
    	struct netif_radio *r = NULL;
    
    	list_for_each_entry(n, &c->nodelist, list) {
    		r = find_radio_by_node(c, n, mac);
    		if (r)
    			return r;
    	}
    
    	return NULL;
    }
    
    
    /* find radio by macaddress, search all nodes */
    struct netif_radio *find_radio_by_mac(struct controller *c, uint8_t *mac)
    {
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, mac, MAC_ENTRY_RADIO);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _find_radio_by_mac(c, mac);
    
    	return (struct netif_radio*)entry->data;
    }
    
    
    /* finds radio struct from interface macaddr */
    struct netif_radio *find_radio_by_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))
    
    /* find link by macaddress */
    struct netif_link *find_link_by_mac(struct controller *c, uint8_t *upstream, uint8_t *downstream)
    {
    
    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, upstream, 6)
    				&& !memcmp(l->downstream->bss->bssid, downstream, 6))
    
    /* deprecated */
    struct node *_cntlr_find_node(struct controller *c, uint8_t *almac)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	struct node *n = NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	list_for_each_entry(n, &c->nodelist, list) {
    		if (!memcmp(n->alid, almac, 6))
    			return n;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    	return NULL;
    }
    
    
    /* find node by macaddress */
    struct node *cntlr_find_node(struct controller *c, uint8_t *mac)
    {
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, mac, MAC_ENTRY_ALID);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _cntlr_find_node(c, mac);
    
    	return (struct node*)entry->data;
    }
    
    /* deprecated */
    struct sta *_cntlr_find_sta(struct controller *c, uint8_t *mac)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct sta *s = NULL;
    
    
    	list_for_each_entry(s, &c->stalist, list) {
    
    		if (!memcmp(s->de_sta->macaddr, mac, 6))
    
    /* find sta by macaddress */
    struct sta *cntlr_find_sta(struct controller *c, uint8_t *mac)
    {
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, mac, MAC_ENTRY_ALID);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _cntlr_find_sta(c, mac);
    
    	return (struct sta*)entry->data;
    }
    
    
    struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta, uint8_t *alid)
    {
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct bcnreq *br = NULL;
    
    
    	dbg("%s: --->\n", __func__);
    
    	list_for_each_entry(br, &c->bcnreqlist, list) {
    		if (!memcmp(br->sta_mac, sta, 6) && !memcmp(br->agent_mac, alid, 6))
    			return br;
    	}
    
    	return NULL;
    }
    
    
    /* deprecated */
    struct netif_iface *_cntlr_iterate_fbss(struct controller *c, uint8_t *mac)
    
    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, mac, 6))
    
    /* find fbss based on macaddr */
    struct netif_iface *cntlr_iterate_fbss(struct controller *c, uint8_t *mac)
    {
    	struct map_macaddr_entry *entry = NULL;
    
    	entry = allmac_lookup(&c->mac_table, mac, MAC_ENTRY_FBSS);
    
    	if (WARN_ON(!entry))
    		/* fall back to old find - TODO: deprecate */
    		return _cntlr_iterate_fbss(c, mac);
    
    	return (struct netif_iface*)entry->data;
    }
    
    
    /* find node based on bssid */
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    struct node *cntlr_find_node_by_iface(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;
    
    		list_for_each_entry(r, &n->radiolist, list) {
    
    
    			list_for_each_entry(p, &r->iflist, list) {
    
    				if (!memcmp(p->bss->bssid, bssid, 6))
    
    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
    
    
    struct node_policy *agent_find_policy(struct controller *c, uint8_t *agent)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct node_policy *a = NULL;
    
    	list_for_each_entry(a, &c->cfg.nodelist, list) {
    		if (!memcmp(agent, a->agent_id, 6))
    			return a;
    	}
    
    	return NULL;
    }
    
    struct radio_policy *agent_find_radio_policy(struct controller *c, uint8_t *radio_mac)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct node_policy *a = NULL;
    
    
    	list_for_each_entry(a, &c->cfg.nodelist, list) {
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    		struct radio_policy *r = NULL;
    
    
    		list_for_each_entry(r, &a->radiolist, list) {
    
    			if (!memcmp(radio_mac, r->macaddr, 6))
    
    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) {
    
    			if (memcmp(bssid, p->bss->bssid, 6))
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				continue;
    
    			return n;
    		}
    	}
    
    	return NULL;
    }
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    struct node *cntlr_add_node(struct controller *c, uint8_t *almac)
    {
    	struct node *n;
    	char mac_str[18] = {0};
    	int ret;
    
    	if (!hwaddr_ntoa(almac, mac_str))
    		return NULL;
    
    	n = cntlr_find_node(c, almac);
    	if (!n) {
    		n = cntlr_alloc_node(c, almac);
    		if (!n) {
    			err("|%s:%d| failed to allocate node "MACFMT"\n",
    			    __func__, __LINE__, MAC2STR(almac));
    			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);
    	}
    
    #ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
    	if (!hwaddr_equal(c->almac, almac))
    		cntlr_sync_dyn_controller_config(c, almac);
    #endif
    
    	return n;
    }
    
    
    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)
    {
    	return 0;
    }
    
    void cntlr_update_sta_steer_counters(struct controller *c,
    				     uint8_t *sta_mac,
    				     uint8_t *src_bssid,
    				     uint8_t *dst_bssid,
    
    				     enum steer_trigger trigger)
    
    	trace("%s:--->\n", __func__);
    
    
    	struct wifi_apsta_steer_history *a;
    
    	struct sta *s = cntlr_find_sta(c, sta_mac);
    
    	struct wifi_multiap_sta *mapsta;
    
    
    	if (!s) {
    		dbg("|%s:%d| Unrecognized STA "MACFMT", skip!\n",
    		    __func__, __LINE__, MAC2STR(sta_mac));
    		return;
    	}
    
    	mapsta = &s->de_sta->mapsta;
    	if (mapsta->num_steer_hist >= MAX_STEER_HISTORY) {
    
    		for (i = 0; i < MAX_STEER_HISTORY - 1; i++) {
    			memcpy(&mapsta->steer_history[i], &mapsta->steer_history[i+1],
    					sizeof(struct wifi_apsta_steer_history));
    
    		a = &mapsta->steer_history[MAX_STEER_HISTORY - 1];
    
    		a = &mapsta->steer_history[mapsta->num_steer_hist];
    
    	}
    
    	/* Update SteeringHistory */
    	timestamp_update(&a->time);
    
    
    	if (src_bssid)
    		memcpy(a->src_bssid, src_bssid, 6);
    
    	if (dst_bssid)
    		memcpy(a->dst_bssid, dst_bssid, 6);
    
    
    	switch (mode) {
    	case STEER_MODE_ASSOC_CTL:
    		a->method = STEER_METHOD_ASSOC_CTL;
    		break;
    	case STEER_MODE_BTM_REQ:
    		a->method = STEER_METHOD_BTM_REQ;
    
    		/* Update SteeringSummaryStats - per STA & per Network */
    		s->de_sta->mapsta.stats.btm_attempt_cnt++;
    		c->dlem.network.steer_summary.btm_attempt_cnt++;
    
    		break;
    	case STEER_MODE_OPPORTUNITY:
    		a->method = STEER_METHOD_ASYNC_BTM;
    
    		/*TODO: add counter for opportunity (incl blacklis count) */
    
    		break;
    	default:
    		a->method = STEER_METHOD_UNKNOWN;
    		break;
    	}
    
    	a->duration = 0;
    
    
    	/* Record tsp for most recent steer attempt */
    
    	timestamp_update(&s->de_sta->mapsta.stats.last_attempt_tsp);
    
    	mapsta->num_steer_hist += 1;
    
    void cntlr_notify_client_steer_req_evt(struct controller *c,
    			uint8_t *bssid, uint32_t sta_nr, uint8_t stas[][6],
    			uint32_t bssid_nr, uint8_t target_bssid[][6])
    {
    	char ev_data[1024] = {0};
    
    	snprintf(ev_data, sizeof(ev_data),
    			"{\"bssid\":\""MACFMT"\"",
    			MAC2STR(bssid));
    
    	if (sta_nr) {
    		char mac[64] = {0};
    
    		strncat(ev_data, ",\"sta_mac\":", sizeof(ev_data) - strlen(ev_data));
    		snprintf(mac, sizeof(mac), "\""MACFMT"\"", MAC2STR(stas[0]));
    		strncat(ev_data, mac, sizeof(ev_data) - strlen(ev_data));
    
    		// TODO: use blob_buf directly to provide further STA MACs
    	}
    
    	if (bssid_nr) {
    		char mac[64] = {0};
    
    		strncat(ev_data, ",\"target_bssid\":", sizeof(ev_data) - strlen(ev_data));
    		snprintf(mac, sizeof(mac), "\""MACFMT"\"", MAC2STR(target_bssid[0]));
    		strncat(ev_data, mac, sizeof(ev_data) - strlen(ev_data));
    
    		// TODO: use blob_buf directly to provide further target MACs
    	}
    
    	strncat(ev_data, "}", sizeof(ev_data) - strlen(ev_data));
    
    	cntlr_notify_event(c, "client_steer_request", ev_data);
    }
    
    
    void cntlr_notify_client_steer_result(struct controller *c,
    		uint8_t *sta_mac, bool steer_success)
    {
    	char ev_data[1024] = {0};
    
    	snprintf(ev_data, sizeof(ev_data),
    	         "{\"sta_mac\":\""MACFMT"\""
    	         ",\"status\":%s}",
    	         MAC2STR(sta_mac),
    	         steer_success ? "SUCCESS" : "FAIL");
    	cntlr_notify_event(c, "client_steer_result", ev_data);
    }
    
    
    #if 0
    static int invoke_disconnect_sta(struct node *n, struct netif_iface *p,
    		uint8_t *sta_mac)
    {
    	/* TODO implement */
    	return 0;
    }
    #endif
    
    static int invoke_disconnect_sta_by_bssid(struct controller *c, unsigned char *bssid,
    						uint8_t *sta_mac)
    {
    	/* TODO implement */
    
    	/* The intention of this function is to force disconnect of the STA with given MAC
    	 * address by means other than BTM steering. I.e. find out the way to inform agent
    	 * on the necessity to deatuthenticate / disassociate the STA connected to one of
    	 * its BSSes immediately. This is because association control only disallows new
    	 * STAs from connecting to given BSS, but - according to spec - is not meant to
    	 * cause disconnection of already connected STA from that BSS (error scenario).
    	 */
    
    	return 0;
    }
    
    
    static void cntlr_btm_req_timer_cb(atimer_t *t)
    {
    	trace("%s:--->\n", __func__);
    
    	struct sta *s = container_of(t, struct sta, btm_req_timer);
    	struct node *n = s->fh->agent;
    	struct controller *c = n->cntlr;
    
    	if (s->de_sta->mapsta.pending_btm_resp_num > 0) {
    		s->de_sta->mapsta.stats.failed_steer_attempts +=
    				s->de_sta->mapsta.pending_btm_resp_num;
    		s->de_sta->mapsta.stats.btm_failure_cnt +=
    				s->de_sta->mapsta.pending_btm_resp_num;
    		c->dlem.network.steer_summary.btm_failure_cnt +=
    				s->de_sta->mapsta.pending_btm_resp_num;
    		s->de_sta->mapsta.pending_btm_resp_num = 0;
    
    
    		cntlr_notify_client_steer_result(c, s->de_sta->macaddr, false);
    
    static int cntlr_steer_sta(struct controller *c, struct sta *s,
    
    			   struct wifi_sta_meas_report *to, uint32_t mode,
    
    	uint16_t mid;
    
    	trace("%s:--->\n", __func__);
    
    
    	if (!to || hwaddr_is_zero(to->bssid)) {
    
    		dbg("%s: steer verdict = OK, but target AP = NULL!\n", __func__);
    
    		return 0;
    	}
    
    	if (!memcmp(to->bssid, s->bssid, 6)) {
    
    		s->de_sta->mapsta.stats.no_candidate_cnt++;
    		c->dlem.network.steer_summary.no_candidate_cnt++;
    
    		dbg("%s: " MACFMT " connected to best AP! No steer needed.\n",
    
    		    __func__, MAC2STR(s->de_sta->macaddr));
    
    		return 0;
    	}
    
    	dbg("%s: Try to steer " MACFMT " from " MACFMT " to " MACFMT "\n",
    
    	     __func__, MAC2STR(s->de_sta->macaddr), MAC2STR(s->bssid), MAC2STR(to->bssid));
    
    	switch (mode) {
    	case STEER_MODE_ASSOC_CTL:
    
    		/* Issue client assoc control */
    
    		invoke_disconnect_sta_by_bssid(c, s->bssid, s->de_sta->macaddr);
    		ret = cntlr_send_client_assoc_ctrl_request(c, s->fh->agent->alid,
    					s->bssid, 0, 10, /* block bssid for 10 sec */
    
    					1, s->de_sta->macaddr, &mid);
    
    		if (ret) {
    			warn("%s: Failed to send cmdu for assoc control!\n", __func__);
    			//s->de_sta->mapsta.stats.failed_steer_attempts++;
    			return ret;
    		}
    
    		/* Keep mid & check assoc control succesful in ACK msg */
    
    		s->latest_assoc_cntrl_mid = mid;
    
    		dbg("%s: cmdu->cdata->hdr.mid %u\n", __func__, mid);
    
    		break;
    	case STEER_MODE_BTM_REQ:
    	case STEER_MODE_OPPORTUNITY:
    
    		ret = cntlr_send_client_steer_request(c, s->fh->agent->alid,
    
    					1, (uint8_t (*)[6])s->de_sta->macaddr,
    
    					1, (uint8_t (*)[6])to->bssid,
    
    		if (ret) {
    			warn("%s: Failed to send cmdu for steering sta!\n", __func__);
    			return ret;
    
    
    		/* Expect btm-resp from STA forwarded to cntlr */
    		s->de_sta->mapsta.pending_btm_resp_num++;
    		timer_set(&s->btm_req_timer, BTM_RESP_EXP_TIMEOUT * 1000);
    
    
    		break;
    	case STEER_MODE_UNDEFINED:
    	default:
    		dbg("%s: steer mode is undefined\n", __func__);
    		return 0;
    
    	cntlr_update_sta_steer_counters(c, s->de_sta->macaddr, s->bssid, to->bssid,
    
    				mode, STEER_TRIGGER_LINK_QUALITY);
    
    /* returns steer_control_config for current steer_control */
    struct steer_control_config *get_steer_control_config(struct controller *c)
    {
    	struct steer_control_config *e = NULL;
    
    	struct steer_control *sc;
    
    	if (!c)
    		return NULL;
    
    	sc = cntlr_get_steer_control(c);
    
    	if (!sc)
    		/* steer plugin is not loaded yet */
    		return NULL;
    
    
    	list_for_each_entry(e, &c->cfg.scclist, list) {
    		if (!strncmp(e->name, sc->name, 63))
    			return e;
    	}
    
    	return NULL;
    }
    
    static void cntlr_configure_steer(struct controller *c, struct sta *s,
    			struct steer_control_config *e)
    
    	struct netif_radio *r;
    	struct radio_policy *rp = NULL;
    	struct steer_config scfg = {};
    
    	r = find_radio_by_bssid(c, s->bssid);
    	if (!r)
    		return;
    
    
    	/* Ensure band is set in interface data struct */
    
    	s->fh->band = wifi_opclass_get_band(r->radio_el->cur_opclass.opclass[0].id);
    
    	rp = agent_find_radio_policy(c, r->radio_el->macaddr);
    	if (!rp)
    		return;
    
    
    	/* RCPI threshold */
    	if (rp->rcpi_threshold > 0)
    
    		scfg.rcpi_threshold = rp->rcpi_threshold; /* band dependent */
    
    	else {
    		switch (rp->band) {
    			case BAND_5:
    				scfg.rcpi_threshold = CONFIG_DEFAULT_RCPI_TH_5G;
    				break;
    			case BAND_6:
    				scfg.rcpi_threshold = CONFIG_DEFAULT_RCPI_TH_6G;
    				break;
    			case BAND_DUAL:
    			case BAND_2:
    			default:
    				scfg.rcpi_threshold = CONFIG_DEFAULT_RCPI_TH_2G;
    		}
    	}
    
    	scfg.rcpi_hysteresis = 5; /* TODO: unused */
    
    	if (e->diffsnr > 0)
    		scfg.rcpi_diffsnr = e->diffsnr;
    
    		scfg.rcpi_diffsnr = 8; /* default diffsnr */
    
    	scfg.bandsteer = e->bandsteer;
    
    	/* maximum number of btm tries before assoc control */
    	/* TODO: use c->cfg */
    	scfg.max_btm_attempt = DEFAULT_MAX_BTM_ATTEMPT;
    
    
    	cntlr_configure_steer_module(c, &scfg);
    }
    
    static void cntlr_try_steer_sta(struct controller *c, struct sta *s)
    {
    	trace("%s:--->\n", __func__);
    
    	struct steer_sta candidate = {
    
    		.meas_reportlist = &s->de_sta->meas_reportlist,
    
    		.band = s->fh->band,
    
    	memcpy(candidate.bssid, s->bssid, 6);
    
    
    	/* check if sta should be steered */
    
    	ret = cntlr_maybe_steer_sta(c, &candidate);
    	if (ret) {
    		dbg("cntlr_maybe_steer_sta() ret = %d\n", ret);
    		return;
    	}
    
    	switch (candidate.verdict) {
    	case STEER_VERDICT_OK:
    
    		if (!timestamp_expired(&s->de_sta->mapsta.stats.last_attempt_tsp,
    					STEER_ATTEMPT_MIN_ITV)) {
    
    			dbg("%s: last steer attempt < %us ago; skip steering\n",
    
    			    __func__, STEER_ATTEMPT_MIN_ITV / 1000);
    
    		if (!timestamp_expired(&s->de_sta->mapsta.stats.last_steer_tsp,
    					STEER_SUCCESS_MIN_ITV)) {
    			dbg("%s: last successful steer < %us ago; skip steering\n",
    			    __func__, STEER_SUCCESS_MIN_ITV / 1000);
    			return;
    		}
    
    		cntlr_steer_sta(c, s, candidate.best, candidate.mode, candidate.reason);
    		break;
    	case STEER_VERDICT_NOK:
    		return;
    	case STEER_VERDICT_MAYBE:
    		/* TODO: check next steer-control ? */
    		break;
    	case STEER_VERDICT_EXCLUDE:
    
    		/* STA excluded from subsequent steer attempts */
    		dbg("%s: sticky STA excluded from steering, elapsed %us of %us\n", __func__,
    
    		    timestamp_elapsed_sec(&s->de_sta->mapsta.stats.last_attempt_tsp),
    		    STEER_ATTEMPT_STICKY_ITV / 1000);
    		if (timestamp_expired(&s->de_sta->mapsta.stats.last_attempt_tsp,
    					STEER_ATTEMPT_STICKY_ITV))
    
    			/* time up, allow steering again */
    			s->de_sta->mapsta.stats.failed_steer_attempts = 0;
    
    		/* TODO: consider update of BTM steering disallowed STA list in agent */
    
    static void cntlr_bcn_metrics_parse(atimer_t *t)
    
    {
    	trace("%s:--->\n", __func__);
    
    	struct sta *s = container_of(t, struct sta, bcn_metrics_timer);
    	struct node *n = s->fh->agent;
    	struct controller *c = n->cntlr;
    	struct netif_iface *bss = NULL;
    	struct wifi_sta_meas_report *b = NULL, *tmp;
    
    	struct steer_control_config *scc;
    
    
    	dbg("%s: STA " MACFMT" connected to " MACFMT " in Node " MACFMT"\n",
    	    __func__, MAC2STR(s->de_sta->macaddr), MAC2STR(s->bssid), MAC2STR(n->alid));
    
    	list_for_each_entry_safe(b, tmp, &s->de_sta->meas_reportlist, list) {
    
    		dbg("bcn-report from " MACFMT " %s\n",
    		    MAC2STR(b->bssid), b->stale ? "(stale)":"");
    
    
    		/* Skip entry not in our network */
    		bss = cntlr_iterate_fbss(c, b->bssid);
    		if (!bss) {
    
    			dbg("%s: Delete alien entry " MACFMT "\n",
    			    __func__, MAC2STR(b->bssid));
    
    			list_del(&b->list);
    			free(b);
    
    			s->de_sta->num_meas_reports--;
    
    
    		if (stale && !b->stale)
    			/* at least one fresh metric found */
    			stale = false;
    	}
    
    	if (stale) {
    		/* only stale metrics, reschedule parse of the results */
    		timer_set(&s->bcn_metrics_timer,
    				s->bcn_report_wait_time * 1000);
    		return;
    
    	scc = get_steer_control_config(c);
    	if (!scc)
    		return;
    
    	if (scc->enable_sta_steer) {
    
    		/* configure individually for each STA */
    
    		cntlr_configure_steer(c, s, scc);
    
    	dbg("%s exiting\n", __func__);
    
    /* TODO: deprecate after assoc control steering added */
    
    static void cntlr_init_sta_steer_counters(struct sta *s)
    
    	if (!s || !s->de_sta)
    		return;
    
    	s->de_sta->mapsta.stats = (struct wifi_steer_summary){0};
    
    
    	/* TODO: implement stats marked as NO_DATA */
    
    	s->de_sta->mapsta.stats.blacklist_attempt_cnt = STEER_STATS_NO_DATA;
    	s->de_sta->mapsta.stats.blacklist_success_cnt = STEER_STATS_NO_DATA;
    	s->de_sta->mapsta.stats.blacklist_failure_cnt = STEER_STATS_NO_DATA;
    }
    
    
    struct wifi_sta_element *cntlr_wifi_alloc_sta(struct controller *c,
    
    		uint8_t *macaddr)
    {
    	struct wifi_sta_element *wse = NULL;
    
    	wse = calloc(1, sizeof(struct wifi_sta_element));
    	if (!wse)
    		return NULL;
    
    	INIT_LIST_HEAD(&wse->meas_reportlist);
    
    	wse->num_meas_reports = 0;
    
    	memcpy(wse->macaddr, macaddr, 6);
    
    	wse->mapsta.pending_btm_resp_num = 0;
    
    
    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->unassoclist);
    
    	list_add(&s->list, &c->stalist);
    
    	timer_init(&s->bcn_metrics_timer, cntlr_bcn_metrics_parse);
    
    	timer_init(&s->btm_req_timer, cntlr_btm_req_timer_cb);
    
    	s->de_sta = cntlr_wifi_alloc_sta(c, macaddr);
    
    	if (!s->de_sta) {
    		free(s);
    		return NULL;
    	}
    
    
    	allmac_insert(&c->mac_table, macaddr, MAC_ENTRY_BSTA, (void *)s);
    
    
    	cntlr_init_sta_steer_counters(s);
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    static void forall_node_get_sta_metrics(struct controller *c)
    
    Saurabh Verma's avatar
    Saurabh Verma committed
    	struct sta *s = NULL;
    
    
    	list_for_each_entry(s, &c->stalist, list) {
    		struct cmdu_buff *cmdu;
    
    
    		cmdu = cntlr_gen_sta_metric_query(c, s->fh->agent->alid, s->de_sta->macaddr);
    
    		if (!cmdu)
    			continue;
    
    		send_cmdu(c, cmdu);
    		cmdu_free(cmdu);
    	}
    }
    
    
    struct wifi_bss_element *cntlr_wifi_bss(struct controller *c,
    					      uint8_t *hwaddr)
    {
    	struct wifi_bss_element *bss = NULL;
    
    	bss = calloc(1, sizeof(struct wifi_bss_element));
    	if (!bss)
    		return NULL;
    
    	//INIT_LIST_HEAD(&bss->stalist);
    	memcpy(bss->bssid, hwaddr, 6);
    
    	return bss;
    }
    
    
    struct netif_iface *cntlr_radio_add_interface(struct controller *c,