3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2025-06-08 14:57:25 +02:00
iwd/src/blacklist.c
James Prestwood 258482d509 blacklist: add new blacklist reason, ROAM_REQUESTED
This adds a new (less severe) blacklist reason as well as an option
to configure the timeout. This blacklist reason will be used in cases
where a BSS has requested IWD roam elsewhere. At that time a new
blacklist entry will be added which will be used along with some
other criteria to determine if IWD should connect/roam to that BSS
again.

Now that we have multiple blacklist reasons there may be situations
where a blacklist entry already exists but with a different reason.
This is going to be handled by the reason severity. Since we have
just two reasons we will treat a connection failure as most severe
and a roam requested as less severe. This leaves us with two
possible situations:

1. BSS is roam blacklisted, then gets connection blacklisted:
   The reason will be "promoted" to connection blacklisted.

2. BSS is connection blacklisted, then gets roam blacklisted:
   The blacklist request will be ignored
2025-04-01 10:54:04 -05:00

262 lines
6.3 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_roam_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_ROAM_REQUESTED:
return blacklist_roam_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_roam_initial_timeout))
blacklist_roam_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
/* For easier user configuration the timeout values are in seconds */
blacklist_roam_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)