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.
This commit is contained in:
James Prestwood 2019-01-30 15:13:57 -08:00 committed by Denis Kenzior
parent 2522216379
commit 025ca0d4d3
4 changed files with 40 additions and 31 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;