Newer
Older
* cntlr.c - Multi-AP controller
* Copyright (C) 2020-2022 IOPSYS Software Solutions AB. All rights reserved.
* See LICENSE file for source code license information.
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libubox/uloop.h>
#include <libubus.h>
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <easy/easy.h>
#include <cmdu.h>
#include <1905_tlvs.h>
#include <i1905_wsc.h>
#include <easymesh.h>
#if (EASYMESH_VERSION > 2)
#include "dpp.h"
#endif
#include "wifi_dataelements.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "cntlr_ubus_dbg.h"
#include "cntlr_cmdu.h"
#include "steer_module.h"
extern bool waitext;
/* 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;
list_for_each_entry(r, &n->radiolist, list) {
list_for_each_entry(p, &r->iflist, list) {
if (!memcmp(p->bss->ssid, ssid, 33))
return r;
}
}
return NULL;
}
/* 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)
{
list_for_each_entry(r, &n->radiolist, list) {
list_for_each_entry(p, &r->iflist, list) {
if (!memcmp(p->bss->ssid, ssid, 33))
return p;
}
}
return NULL;
}
/* deprecated */
struct netif_radio *_find_radio_by_node(struct controller *c, struct node *n,
list_for_each_entry(p, &n->radiolist, list) {
if (!memcmp(p->radio_el->macaddr, radio_mac, 6))
return p;
}
return NULL;
}
/* 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)
{
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))
return r;
}
}
}
return NULL;
}
/* find link by macaddress */
struct netif_link *find_link_by_mac(struct controller *c, uint8_t *upstream, uint8_t *downstream)
{
list_for_each_entry(l, &c->linklist, list) {
if (!memcmp(l->upstream->bss->bssid, upstream, 6)
&& !memcmp(l->downstream->bss->bssid, downstream, 6))
return l;
}
return NULL;
}
/* deprecated */
struct node *_cntlr_find_node(struct controller *c, uint8_t *almac)
list_for_each_entry(n, &c->nodelist, list) {
if (!memcmp(n->alid, almac, 6))
return n;
/* 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)
list_for_each_entry(s, &c->stalist, list) {
if (!memcmp(s->de_sta->macaddr, mac, 6))
return s;
}
return NULL;
}
/* 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)
{
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)
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))
return p;
}
}
}
return NULL;
}
/* 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 */
struct node *cntlr_find_node_by_iface(struct controller *c, uint8_t *bssid)
list_for_each_entry(n, &c->nodelist, list) {
struct netif_radio *r;
list_for_each_entry(r, &n->radiolist, list) {
struct netif_iface *p = NULL;
list_for_each_entry(p, &r->iflist, list) {
if (!memcmp(p->bss->bssid, bssid, 6))
}
}
}
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;
}
struct node_policy *agent_find_policy(struct controller *c, uint8_t *agent)
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)
list_for_each_entry(a, &c->cfg.nodelist, list) {
list_for_each_entry(r, &a->radiolist, list) {
if (!memcmp(radio_mac, r->macaddr, 6))
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;
list_for_each_entry(p, &n->iflist, list) {
if (memcmp(bssid, p->bss->bssid, 6))
continue;
return n;
}
}
return NULL;
}
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
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;
}
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));
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)
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);
a->trigger = trigger;
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;
}
/* Record tsp for most recent steer attempt */
timestamp_update(&s->de_sta->mapsta.stats.last_attempt_tsp);
mapsta->num_steer_hist += 1;
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
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,
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));
UNUSED(reason);
switch (mode) {
case STEER_MODE_ASSOC_CTL:
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 */
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);
return 0;
}
/* 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;
if (!e)
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 */
/* diffsnr */
if (e->diffsnr > 0)
scfg.rcpi_diffsnr = e->diffsnr;
scfg.rcpi_diffsnr = 8; /* default diffsnr */
/* bandsteer */
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,
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 */
break;
default:
break;
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);
cntlr_try_steer_sta(c, s);
}
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);
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;
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);
static void forall_node_get_sta_metrics(struct controller *c)
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,