mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-08-27 09:07:28 +02:00

The initial pass of this feature only envisioned BSS transition management frames as the trigger to "roam blacklist" a BSS, hence the original name. But some APs actually utilize status codes that also indicate to the stations that they are busy, or not able to handle more connections. This directly aligns with the original motivation of the "roam blacklist" series and these events should also trigger this type of blacklist. First, since we will be applying this blacklist to cases other than being told to roam, rename this reason code internally to BLACKLIST_REASON_AP_BUSY. The config option is also being renamed to [Blacklist].InitialAccessPointBusyTimeout while also supporting the old config option, but warning that it is deprecated.
270 lines
6.6 KiB
C
270 lines
6.6 KiB
C
/*
|
|
*
|
|
* Wireless daemon for Linux
|
|
*
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "src/blacklist.h"
|
|
#include "src/util.h"
|
|
#include "src/iwd.h"
|
|
#include "src/module.h"
|
|
|
|
/*
|
|
* The current timeout is multiplied by this value after an entry is blacklisted
|
|
* more than once.
|
|
*/
|
|
#define BLACKLIST_DEFAULT_MULTIPLIER 30
|
|
|
|
/* Initial timeout for a new blacklist entry */
|
|
#define BLACKLIST_DEFAULT_TIMEOUT 60
|
|
|
|
/* The maximum amount of time a BSS can be blacklisted for */
|
|
#define BLACKLIST_DEFAULT_MAX_TIMEOUT 86400
|
|
|
|
static uint64_t blacklist_multiplier;
|
|
static uint64_t blacklist_initial_timeout;
|
|
static uint64_t blacklist_ap_busy_initial_timeout;
|
|
static uint64_t blacklist_max_timeout;
|
|
|
|
struct blacklist_entry {
|
|
uint8_t addr[6];
|
|
uint64_t added_time;
|
|
uint64_t expire_time;
|
|
enum blacklist_reason reason;
|
|
};
|
|
|
|
struct blacklist_search {
|
|
const uint8_t *addr;
|
|
enum blacklist_reason reason;
|
|
};
|
|
|
|
static struct l_queue *blacklist;
|
|
|
|
static uint64_t get_reason_timeout(enum blacklist_reason reason)
|
|
{
|
|
switch (reason) {
|
|
case BLACKLIST_REASON_CONNECT_FAILED:
|
|
return blacklist_initial_timeout;
|
|
case BLACKLIST_REASON_AP_BUSY:
|
|
return blacklist_ap_busy_initial_timeout;
|
|
default:
|
|
l_warn("Unhandled blacklist reason: %u", reason);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool check_if_expired(void *data, void *user_data)
|
|
{
|
|
struct blacklist_entry *entry = data;
|
|
uint64_t now = l_get_u64(user_data);
|
|
|
|
if (l_time_after(now, entry->expire_time)) {
|
|
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
|
|
l_free(entry);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void blacklist_prune(void)
|
|
{
|
|
uint64_t now = l_time_now();
|
|
|
|
l_queue_foreach_remove(blacklist, check_if_expired, &now);
|
|
}
|
|
|
|
static bool match_addr(const void *a, const void *b)
|
|
{
|
|
const struct blacklist_entry *entry = a;
|
|
const uint8_t *addr = b;
|
|
|
|
if (!memcmp(entry->addr, addr, 6))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool match_addr_and_reason(const void *a, const void *b)
|
|
{
|
|
const struct blacklist_entry *entry = a;
|
|
const struct blacklist_search *search = b;
|
|
|
|
if (entry->reason != search->reason)
|
|
return false;
|
|
|
|
if (!memcmp(entry->addr, search->addr, 6))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason)
|
|
{
|
|
struct blacklist_entry *entry;
|
|
uint64_t timeout;
|
|
|
|
blacklist_prune();
|
|
|
|
timeout = get_reason_timeout(reason);
|
|
if (!timeout)
|
|
return;
|
|
|
|
entry = l_queue_find(blacklist, match_addr, addr);
|
|
|
|
if (entry) {
|
|
uint64_t offset;
|
|
|
|
if (reason < entry->reason) {
|
|
l_debug("Promoting "MAC" blacklist to reason %u",
|
|
MAC_STR(addr), reason);
|
|
/* Reset this to the new timeout and reason */
|
|
entry->reason = reason;
|
|
entry->added_time = l_time_now();
|
|
entry->expire_time = l_time_offset(entry->added_time,
|
|
timeout);
|
|
return;
|
|
} else if (reason > entry->reason) {
|
|
l_debug("Ignoring blacklist extension of "MAC", "
|
|
"current blacklist status is more severe!",
|
|
MAC_STR(addr));
|
|
return;
|
|
}
|
|
|
|
offset = l_time_diff(entry->added_time, entry->expire_time);
|
|
|
|
offset *= blacklist_multiplier;
|
|
|
|
if (offset > blacklist_max_timeout)
|
|
offset = blacklist_max_timeout;
|
|
|
|
entry->expire_time = l_time_offset(entry->added_time, offset);
|
|
|
|
return;
|
|
}
|
|
|
|
entry = l_new(struct blacklist_entry, 1);
|
|
|
|
entry->added_time = l_time_now();
|
|
entry->expire_time = l_time_offset(entry->added_time, timeout);
|
|
entry->reason = reason;
|
|
memcpy(entry->addr, addr, 6);
|
|
|
|
l_queue_push_tail(blacklist, entry);
|
|
}
|
|
|
|
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason)
|
|
{
|
|
struct blacklist_search search = {
|
|
.addr = addr,
|
|
.reason = reason
|
|
};
|
|
|
|
blacklist_prune();
|
|
|
|
return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
|
|
}
|
|
|
|
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason)
|
|
{
|
|
struct blacklist_entry *entry;
|
|
struct blacklist_search search = {
|
|
.addr = addr,
|
|
.reason = reason
|
|
};
|
|
|
|
blacklist_prune();
|
|
|
|
entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
|
|
|
|
if (!entry)
|
|
return;
|
|
|
|
l_free(entry);
|
|
}
|
|
|
|
static int blacklist_init(void)
|
|
{
|
|
const struct l_settings *config = iwd_get_config();
|
|
|
|
if (!l_settings_get_uint64(config, "Blacklist", "InitialTimeout",
|
|
&blacklist_initial_timeout))
|
|
blacklist_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
|
|
|
/* For easier user configuration the timeout values are in seconds */
|
|
blacklist_initial_timeout *= L_USEC_PER_SEC;
|
|
|
|
if (!l_settings_get_uint64(config, "Blacklist",
|
|
"InitialRoamRequestedTimeout",
|
|
&blacklist_ap_busy_initial_timeout))
|
|
blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
|
else
|
|
l_warn("[Blacklist].InitialRoamRequestedTimeout is deprecated, "
|
|
"use [Blacklist].InitialAccessPointBusyTimeout");
|
|
|
|
if (!l_settings_get_uint64(config, "Blacklist",
|
|
"InitialAccessPointBusyTimeout",
|
|
&blacklist_ap_busy_initial_timeout))
|
|
blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
|
|
|
/* For easier user configuration the timeout values are in seconds */
|
|
blacklist_ap_busy_initial_timeout *= L_USEC_PER_SEC;
|
|
|
|
if (!l_settings_get_uint64(config, "Blacklist",
|
|
"Multiplier",
|
|
&blacklist_multiplier))
|
|
blacklist_multiplier = BLACKLIST_DEFAULT_MULTIPLIER;
|
|
|
|
if (blacklist_multiplier == 0) {
|
|
l_warn("[Blacklist].Multiplier cannot be zero, setting to 1");
|
|
blacklist_multiplier = 1;
|
|
}
|
|
|
|
if (!l_settings_get_uint64(config, "Blacklist",
|
|
"MaximumTimeout",
|
|
&blacklist_max_timeout))
|
|
blacklist_max_timeout = BLACKLIST_DEFAULT_MAX_TIMEOUT;
|
|
|
|
blacklist_max_timeout *= L_USEC_PER_SEC;
|
|
|
|
if (blacklist_initial_timeout > blacklist_max_timeout)
|
|
l_warn("[Blacklist].InitialTimeout exceeded "
|
|
"[Blacklist].MaximumTimeout!");
|
|
|
|
if (!blacklist_initial_timeout)
|
|
l_debug("initial timeout was zero, blacklist will be disabled");
|
|
|
|
blacklist = l_queue_new();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void blacklist_exit(void)
|
|
{
|
|
l_queue_destroy(blacklist, l_free);
|
|
}
|
|
|
|
IWD_MODULE(blacklist, blacklist_init, blacklist_exit)
|