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>
#include "dpp.h"
#endif
#include "wifi_dataelements.h"
#include "utils/debug.h"
#include "utils/utils.h"
#include "backhaul_topology.h"
#include "cntlr_ubus_dbg.h"
#include "cntlr_cmdu.h"
#include "steer_module.h"
#if (EASYMESH_VERSION > 2)
#include "cntlr_qos.h"
#endif
extern bool waitext;
static void cntlr_clean_stalist_from_bssid(struct controller *c, uint8_t *bssid);
/* 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;
}
struct netif_radio *find_radio_by_band(struct controller *c, struct node *n,
enum wifi_band band)
{
struct netif_radio *r = NULL;
list_for_each_entry(r, &n->radiolist, list) {
if (r->radio_el->band == band)
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_STA);
/* fall back to old find - TODO: deprecate */
res = _cntlr_find_sta(c, mac);
} else {
res = (struct sta*)entry->data;
}
if (res)
time(&res->lookup_time);
}
/* find bsta by macaddress */
struct sta *cntlr_find_bsta(struct controller *c, uint8_t *mac)
{
struct map_macaddr_entry *entry = NULL;
entry = allmac_lookup(&c->mac_table, mac, MAC_ENTRY_BSTA);
/* fall back to old find - TODO: deprecate */
res = _cntlr_find_sta(c, mac);
} else {
res = (struct sta*)entry->data;
}
if (res)
time(&res->lookup_time);
int cntlr_get_node_assoclist(struct controller *c, uint8_t *node_alid,
int *num_sta, uint8_t *sta_macaddrs)
{
struct sta *s = NULL;
int limit = *num_sta;
int ret = 0;
int i = 0;
*num_sta = 0;
list_for_each_entry(s, &c->stalist, list) {
if (s->fh && s->fh->agent && !memcmp(s->fh->agent->alid, node_alid, 6)) {
if (i < limit)
memcpy(&sta_macaddrs[i*6], s->de_sta->macaddr, 6);
else
ret = -E2BIG;
i++;
}
}
*num_sta = i;
return ret;
}
struct bcnreq *cntlr_find_bcnreq(struct controller *c, uint8_t *sta, uint8_t *alid)
{
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 = NULL;
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;
}
/* 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;
}
#if (EASYMESH_VERSION >= 6)
bool cntlr_radio_support_ap_wifi7(struct controller *c,
struct wifi7_radio_capabilities *wifi7_caps)
{
if (wifi7_caps->ap.str_support || wifi7_caps->ap.nstr_support ||
wifi7_caps->ap.emlsr_support || wifi7_caps->ap.emlmr_support)
return true;
return false;
}
bool cntlr_radio_support_bsta_wifi7(struct controller *c,
struct wifi7_radio_capabilities *wifi7_caps)
{
if (wifi7_caps->bsta.str_support || wifi7_caps->bsta.nstr_support ||
wifi7_caps->bsta.emlsr_support || wifi7_caps->bsta.emlmr_support)
return true;
return false;
}
#endif
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
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);
}
/* update the timestamp of the node */
if (n)
timestamp_update(&n->last_tsp_seen);
#ifdef CONTROLLER_SYNC_DYNAMIC_CNTLR_CONFIG
if (!hwaddr_equal(c->almac, almac))
cntlr_sync_dyn_controller_config(c, almac);
#endif
#if (EASYMESH_VERSION > 2)
if (c->cfg.qos.enabled) {
cntlr_qos_sync_node(c, n->alid);
}
#endif
int i = 0;
list_for_each_entry(n, &c->nodelist, list) {
cntlr_dbg(LOG_MISC,
" %d | agent = %p, hwaddr = '"MACFMT"')\n",
i++, n, MAC2STR(n->alid));
static int forall_node_update_neighbors(struct controller *c)
{
return 0;
}
void cntrl_send_max_wifi_bh_hops_policy(struct controller *c)
{
struct node *node = NULL;
if (c->cfg.max_node_bh_hops == 0)
return;
trace("%s: max_node_bh_hops %d\n",
__func__, c->cfg.max_node_bh_hops);
list_for_each_entry(node, &c->nodelist, list) {
enum bh_control {
BLOCK = 0x00,
UNBLOCK = 0x01
} bh_control = UNBLOCK;
struct bh_topology_dev *bh_topo_dev =
find_bh_topology_device(node->alid);
struct netif_radio *radio = NULL;
if (!bh_topo_dev) {
err("%s: device not found in bh topo model, logical error.\n", __func__);
continue;
}
if (bh_topo_dev->bh_info.level_in_tree == UNKNOWN_TREE_LEVEL) {
warn("%s: Level in BH treee unknown for: " MACFMT "\n", __func__, MAC2STR(node->alid));
continue;
}
if (c->cfg.max_node_bh_hops <= bh_topo_dev->bh_info.wifi_hops_from_root)
bh_control = BLOCK;
list_for_each_entry(radio, &node->radiolist, list) {
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
list_for_each_entry(iface, &radio->iflist, list) {
if (iface->bss->is_bbss) {
const uint8_t NO_VALIDITY_PERIOD = 0;
uint8_t ALL_BSTAS[6] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
uint16_t mid;
trace("%s: sending BH assoc_mode %s, node al_mac: " MACFMT
", wifi_hops_from_root: %d, bssid: " MACFMT"\n",
__func__,
bh_control ? "UNBLOCK" : "BLOCK",
MAC2STR(node->alid),
bh_topo_dev->bh_info.wifi_hops_from_root,
MAC2STR(iface->bss->bssid));
cntlr_send_client_assoc_ctrl_request(
c, node->alid, iface->bss->bssid,
bh_control, NO_VALIDITY_PERIOD, 1,
ALL_BSTAS, &mid);
}
}
}
}
}
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,
uint8_t dst_rcpi)
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);
time(&a->steer_time);
if (src_bssid)
memcpy(a->src_bssid, src_bssid, 6);
if (dst_bssid)
memcpy(a->dst_bssid, dst_bssid, 6);
a->trigger = trigger;
if (trigger == STEER_TRIGGER_LINK_QUALITY) {
a->src_rcpi = s->de_sta->rcpi;
a->dst_rcpi = dst_rcpi;
}
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;
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
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, int result)
{
char ev_data[1024] = {0};
snprintf(ev_data, sizeof(ev_data),
"{\"sta_mac\":\""MACFMT"\""
",\"status\":%d}",
MAC2STR(sta_mac), result);
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.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,
STEER_RESULT_FAIL_TIMEOUT);
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));
warn("%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.failed_steer_attempts++;
/* 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, to->rcpi);
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 = cntlr_get_radio_policy(&c->cfg, 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) {
cntlr_dbg(LOG_STEER,
"%s: cntlr_maybe_steer_sta() ret = %d\n",
__func__, ret);
scc = get_steer_control_config(c);
if (!scc)
return;
switch (candidate.verdict) {
case STEER_VERDICT_OK:
if (!timestamp_expired(&s->de_sta->mapsta.stats.last_attempt_tsp,
cntlr_warn(LOG_STEER,
"%s: last steer attempt < %us ago; skip steering\n",
__func__, scc->steer_retry_int / 1000);
if (!timestamp_expired(&s->de_sta->mapsta.stats.last_steer_tsp,
cntlr_warn(LOG_STEER,
"%s: last successful steer < %us ago; skip steering\n",
__func__, scc->steer_int / 1000);
return;
}
cntlr_warn(LOG_STEER,
"%s: client: " MACFMT ", src:" MACFMT ", dst:" MACFMT \
", reason: %s [src: rcpi %d, dst: rcpi %d]\n",
__func__, MAC2STR(s->de_sta->macaddr), MAC2STR(s->bssid),
MAC2STR(candidate.best->bssid),
candidate.reason == STEER_REASON_LOW_RCPI ? "link quality" : "unknown",
candidate.sta->rcpi, candidate.best->rcpi);
cntlr_steer_sta(c, s, candidate.best, candidate.mode, candidate.reason);
break;
case STEER_VERDICT_NOK:
return;
case STEER_VERDICT_MAYBE: