From 025ca0d4d32abefe79868e6010b6ba57d6fc9bfa Mon Sep 17 00:00:00 2001 From: James Prestwood Date: Wed, 30 Jan 2019 15:13:57 -0800 Subject: [PATCH] network: allow network_bss_select to skip blacklist If we have a BSS list where all BSS's have been blacklisted we still need a way to force a connection to that network, instead of having to wait for the blacklist entry to expire. network_bss_select now takes a boolean 'fallback_to_blacklist' which causes the selection to still return a connectable BSS even if the entire list was blacklisted. In most cases this is set to true, as these cases are initiated by DBus calls. The only case where this is not true is inside station_try_next_bss, where we do want to honor the blacklist. This both prevents an explicit connect call (where all BSS's are blacklisted) from trying all the blacklisted BSS's, as well as the autoconnect case where we simply should not try to connect if all the BSS's are blacklisted. There are is some implied behavior here that may not be obvious: On an explicit DBus connect call IWD will attempt to connect to any non-blacklisted BSS found under the network. If unsuccessful, the current BSS will be blacklisted and IWD will try the next in the list. This will repeat until all BSS's are blacklisted, and in this case the connect call will fail. If a connect is tried again when all BSS's are blacklisted IWD will attempt to connect to the first connectable blacklisted BSS, and if this fails the connect call will fail. No more connection attempts will happen until the next DBus call. --- src/network.c | 64 +++++++++++++++++++++++++++++---------------------- src/network.h | 3 ++- src/station.c | 2 +- src/wsc.c | 2 +- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/src/network.c b/src/network.c index 11c252db..9f537052 100644 --- a/src/network.c +++ b/src/network.c @@ -649,42 +649,50 @@ struct scan_bss *network_bss_find_by_addr(struct network *network, return NULL; } -/* Selects what we think is the best BSS to connect to */ -struct scan_bss *network_bss_select(struct network *network) +struct scan_bss *network_bss_select(struct network *network, + bool fallback_to_blacklist) { struct l_queue *bss_list = network->bss_list; struct wiphy *wiphy = station_get_wiphy(network->station); const struct l_queue_entry *bss_entry; + struct scan_bss *candidate = NULL; - switch (network_get_security(network)) { - case SECURITY_NONE: - /* Pick the first bss (strongest signal) */ - return l_queue_peek_head(bss_list); - - case SECURITY_PSK: - case SECURITY_8021X: - /* - * Pick the first bss that advertises ciphers compatible with - * the wiphy. - */ - for (bss_entry = l_queue_get_entries(bss_list); bss_entry; - bss_entry = bss_entry->next) { - struct scan_bss *bss = bss_entry->data; + for (bss_entry = l_queue_get_entries(bss_list); bss_entry; + bss_entry = bss_entry->next) { + struct scan_bss *bss = bss_entry->data; + switch (network_get_security(network)) { + case SECURITY_PSK: + case SECURITY_8021X: if (!wiphy_can_connect(wiphy, bss)) continue; - - if (blacklist_contains_bss(bss->addr)) - continue; - - return bss; + /* fall through */ + case SECURITY_NONE: + break; + default: + return NULL; } - return NULL; + /* + * We only want to record the first (best) candidate. In case + * all our BSS's are blacklisted but we still want to connect + * we want to hold only this first candidate + */ + if (!candidate) + candidate = bss; - default: - return NULL; + if (!blacklist_contains_bss(bss->addr)) + return bss; } + + /* + * No BSS was found, but if we are falling back to blacklisted BSS's we + * can just use the first connectable candidate found above. + */ + if (fallback_to_blacklist) + return candidate; + + return NULL; } static void passphrase_callback(enum agent_result result, @@ -713,7 +721,7 @@ static void passphrase_callback(enum agent_result result, goto err; } - bss = network_bss_select(network); + bss = network_bss_select(network, true); /* Did all good BSSes go away while we waited */ if (!bss) { @@ -946,7 +954,7 @@ static void eap_secret_done(enum agent_result result, goto err; } - bss = network_bss_select(network); + bss = network_bss_select(network, true); /* Did all good BSSes go away while we waited */ if (!bss) { @@ -1068,7 +1076,7 @@ static struct l_dbus_message *network_connect(struct l_dbus *dbus, * agent this may not be the final choice because BSS visibility can * change while we wait for the agent. */ - bss = network_bss_select(network); + bss = network_bss_select(network, true); /* None of the BSSes is compatible with our stack */ if (!bss) @@ -1106,7 +1114,7 @@ void network_connect_new_hidden_network(struct network *network, */ network->info->is_hidden = true; - bss = network_bss_select(network); + bss = network_bss_select(network, true); if (!bss) { /* This should never happened for the hidden networks. */ error = dbus_error_not_supported(message); diff --git a/src/network.h b/src/network.h index 51d842c6..6dd073c0 100644 --- a/src/network.h +++ b/src/network.h @@ -55,7 +55,8 @@ bool network_bss_list_isempty(struct network *network); void network_bss_list_clear(struct network *network); struct scan_bss *network_bss_find_by_addr(struct network *network, const uint8_t *addr); -struct scan_bss *network_bss_select(struct network *network); +struct scan_bss *network_bss_select(struct network *network, + bool fallback_to_blacklist); bool network_register(struct network *network, const char *path); diff --git a/src/station.c b/src/station.c index c0314621..95a313cb 100644 --- a/src/station.c +++ b/src/station.c @@ -1617,7 +1617,7 @@ static bool station_try_next_bss(struct station *station) blacklist_add_bss(station->connected_bss->addr); - next = network_bss_select(station->connected_network); + next = network_bss_select(station->connected_network, false); if (!next) return false; diff --git a/src/wsc.c b/src/wsc.c index 3fc1bcc4..3ed910c5 100644 --- a/src/wsc.c +++ b/src/wsc.c @@ -126,7 +126,7 @@ static void wsc_try_credentials(struct wsc *wsc) bss = network_bss_find_by_addr(network, wsc->creds[i].addr); if (!bss) - bss = network_bss_select(network); + bss = network_bss_select(network, true); if (!bss) continue;