
Don't determine a finished client scan based on whether the current node has seen a beacon or not. This works surprisingly bad on 5 GHz nodes, as clients may refuse to perform active scanning on this frequency band. In case the clients does refuse to scan the 5 GHz band but scans the 2.4 GHz band actively, we might have a good indication about a better node on this band at least. However, as the roam state-machine requires to have seen a probe request from the client to direct him to a better node, thi process will not continue, which either ends the node in being kicked due to exceeding the number of max tries or the scan cooldown to kick in. The solution to this is fairly simple: Don't track the roam_scan_done state but instead just determine a better candidate after the scan interval finished. To ensure the indicated signal levels are as recent as 2 * scan-interval, add a max_age parameter to find_better_candidate. Signed-off-by: David Bauer <mail@david-bauer.net>
230 lines
5.2 KiB
C
230 lines
5.2 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* Copyright (C) 2020 embedd.ch
|
|
* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
|
|
* Copyright (C) 2020 John Crispin <john@phrozen.org>
|
|
*/
|
|
|
|
#include "usteer.h"
|
|
|
|
static int
|
|
avl_macaddr_cmp(const void *k1, const void *k2, void *ptr)
|
|
{
|
|
return memcmp(k1, k2, 6);
|
|
}
|
|
|
|
AVL_TREE(stations, avl_macaddr_cmp, false, NULL);
|
|
static struct usteer_timeout_queue tq;
|
|
|
|
static void
|
|
usteer_sta_del(struct sta *sta)
|
|
{
|
|
MSG(DEBUG, "Delete station " MAC_ADDR_FMT "\n",
|
|
MAC_ADDR_DATA(sta->addr));
|
|
|
|
avl_delete(&stations, &sta->avl);
|
|
free(sta);
|
|
}
|
|
|
|
static void
|
|
usteer_sta_info_del(struct sta_info *si)
|
|
{
|
|
struct sta *sta = si->sta;
|
|
|
|
MSG(DEBUG, "Delete station " MAC_ADDR_FMT " entry for node %s\n",
|
|
MAC_ADDR_DATA(sta->addr), usteer_node_name(si->node));
|
|
|
|
usteer_timeout_cancel(&tq, &si->timeout);
|
|
list_del(&si->list);
|
|
list_del(&si->node_list);
|
|
free(si);
|
|
|
|
if (list_empty(&sta->nodes))
|
|
usteer_sta_del(sta);
|
|
}
|
|
|
|
void
|
|
usteer_sta_node_cleanup(struct usteer_node *node)
|
|
{
|
|
struct sta_info *si, *tmp;
|
|
|
|
free(node->rrm_nr);
|
|
node->rrm_nr = NULL;
|
|
|
|
list_for_each_entry_safe(si, tmp, &node->sta_info, node_list)
|
|
usteer_sta_info_del(si);
|
|
}
|
|
|
|
static void
|
|
usteer_sta_info_timeout(struct usteer_timeout_queue *q, struct usteer_timeout *t)
|
|
{
|
|
struct sta_info *si = container_of(t, struct sta_info, timeout);
|
|
|
|
usteer_sta_info_del(si);
|
|
}
|
|
|
|
struct sta_info *
|
|
usteer_sta_info_get(struct sta *sta, struct usteer_node *node, bool *create)
|
|
{
|
|
struct sta_info *si;
|
|
|
|
list_for_each_entry(si, &sta->nodes, list) {
|
|
if (si->node != node)
|
|
continue;
|
|
|
|
if (create)
|
|
*create = false;
|
|
|
|
return si;
|
|
}
|
|
|
|
if (!create)
|
|
return NULL;
|
|
|
|
MSG(DEBUG, "Create station " MAC_ADDR_FMT " entry for node %s\n",
|
|
MAC_ADDR_DATA(sta->addr), usteer_node_name(node));
|
|
|
|
si = calloc(1, sizeof(*si));
|
|
si->node = node;
|
|
si->sta = sta;
|
|
list_add(&si->list, &sta->nodes);
|
|
list_add(&si->node_list, &node->sta_info);
|
|
si->created = current_time;
|
|
*create = true;
|
|
|
|
/* Node is by default not connected. */
|
|
usteer_sta_disconnected(si);
|
|
|
|
return si;
|
|
}
|
|
|
|
|
|
void
|
|
usteer_sta_info_update_timeout(struct sta_info *si, int timeout)
|
|
{
|
|
if (si->connected == STA_CONNECTED)
|
|
usteer_timeout_cancel(&tq, &si->timeout);
|
|
else if (timeout > 0)
|
|
usteer_timeout_set(&tq, &si->timeout, timeout);
|
|
else
|
|
usteer_sta_info_del(si);
|
|
}
|
|
|
|
struct sta *
|
|
usteer_sta_get(const uint8_t *addr, bool create)
|
|
{
|
|
struct sta *sta;
|
|
|
|
sta = avl_find_element(&stations, addr, sta, avl);
|
|
if (sta)
|
|
return sta;
|
|
|
|
if (!create)
|
|
return NULL;
|
|
|
|
MSG(DEBUG, "Create station entry " MAC_ADDR_FMT "\n", MAC_ADDR_DATA(addr));
|
|
sta = calloc(1, sizeof(*sta));
|
|
memcpy(sta->addr, addr, sizeof(sta->addr));
|
|
sta->avl.key = sta->addr;
|
|
avl_insert(&stations, &sta->avl);
|
|
INIT_LIST_HEAD(&sta->nodes);
|
|
|
|
return sta;
|
|
}
|
|
|
|
void usteer_sta_disconnected(struct sta_info *si)
|
|
{
|
|
si->connected = STA_NOT_CONNECTED;
|
|
usteer_sta_info_update_timeout(si, config.local_sta_timeout);
|
|
}
|
|
|
|
void
|
|
usteer_sta_info_update(struct sta_info *si, int signal, bool avg)
|
|
{
|
|
/* ignore probe request signal when connected */
|
|
if (si->connected == STA_CONNECTED && si->signal != NO_SIGNAL && !avg)
|
|
signal = NO_SIGNAL;
|
|
|
|
if (signal != NO_SIGNAL)
|
|
si->signal = signal;
|
|
|
|
si->seen = current_time;
|
|
|
|
if (si->node->freq < 4000)
|
|
si->sta->seen_2ghz = 1;
|
|
else
|
|
si->sta->seen_5ghz = 1;
|
|
|
|
usteer_sta_info_update_timeout(si, config.local_sta_timeout);
|
|
}
|
|
|
|
bool
|
|
usteer_handle_sta_event(struct usteer_node *node, const uint8_t *addr,
|
|
enum usteer_event_type type, int freq, int signal)
|
|
{
|
|
struct sta *sta;
|
|
struct sta_info *si;
|
|
uint32_t diff;
|
|
bool ret;
|
|
bool create;
|
|
|
|
sta = usteer_sta_get(addr, true);
|
|
if (!sta)
|
|
return -1;
|
|
|
|
si = usteer_sta_info_get(sta, node, &create);
|
|
usteer_sta_info_update(si, signal, false);
|
|
si->stats[type].requests++;
|
|
|
|
diff = si->stats[type].blocked_last_time - current_time;
|
|
if (diff > config.sta_block_timeout)
|
|
si->stats[type].blocked_cur = 0;
|
|
|
|
ret = usteer_check_request(si, type);
|
|
if (!ret) {
|
|
si->stats[type].blocked_cur++;
|
|
si->stats[type].blocked_total++;
|
|
si->stats[type].blocked_last_time = current_time;
|
|
} else {
|
|
si->stats[type].blocked_cur = 0;
|
|
}
|
|
|
|
if (create)
|
|
usteer_send_sta_update(si);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool
|
|
usteer_sta_supports_beacon_measurement_mode(struct sta *sta, enum usteer_beacon_measurement_mode mode)
|
|
{
|
|
switch (mode) {
|
|
case BEACON_MEASUREMENT_PASSIVE:
|
|
return sta->rrm & (1 << 4);
|
|
case BEACON_MEASUREMENT_ACTIVE:
|
|
return sta->rrm & (1 << 5);
|
|
case BEACON_MEASUREMENT_TABLE:
|
|
return sta->rrm & (1 << 6);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void __usteer_init usteer_sta_init(void)
|
|
{
|
|
usteer_timeout_init(&tq);
|
|
tq.cb = usteer_sta_info_timeout;
|
|
}
|