station: Move scanning code from device

This commit is contained in:
Denis Kenzior 2018-09-01 16:21:28 -05:00
parent 1b22af2052
commit 1378a3c945
3 changed files with 239 additions and 229 deletions

View File

@ -51,11 +51,7 @@
struct device {
uint32_t index;
enum device_state state;
struct l_queue *bss_list;
struct l_queue *old_bss_list;
struct l_dbus_message *scan_pending;
struct l_hashmap *networks;
struct l_queue *networks_sorted;
struct scan_bss *connected_bss;
struct network *connected_network;
struct l_dbus_message *connect_pending;
@ -77,7 +73,6 @@ struct device {
bool signal_low : 1;
bool roam_no_orig_ap : 1;
bool ap_directed_roaming : 1;
bool seen_hidden_networks : 1;
uint32_t ap_roam_watch;
};
@ -108,25 +103,6 @@ void __iwd_device_foreach(iwd_device_foreach_func func, void *user_data)
}
}
static const char *iwd_network_get_path(struct device *device,
const char *ssid,
enum security security)
{
static char path[256];
unsigned int pos, i;
pos = snprintf(path, sizeof(path), "%s/", device_get_path(device));
for (i = 0; ssid[i] && pos < sizeof(path); i++)
pos += snprintf(path + pos, sizeof(path) - pos, "%02x",
ssid[i]);
snprintf(path + pos, sizeof(path) - pos, "_%s",
security_to_str(security));
return path;
}
static const char *device_state_to_string(enum device_state state)
{
switch (state) {
@ -149,173 +125,10 @@ static const char *device_state_to_string(enum device_state state)
return "invalid";
}
static void bss_free(void *data)
{
struct scan_bss *bss = data;
const char *addr;
addr = util_address_to_string(bss->addr);
l_debug("Freeing BSS %s", addr);
scan_bss_free(bss);
}
static void network_free(void *data)
{
struct network *network = data;
network_remove(network, -ESHUTDOWN);
}
static bool process_network(const void *key, void *data, void *user_data)
{
struct network *network = data;
struct device *device = user_data;
if (!network_bss_list_isempty(network)) {
/* Build the network list ordered by rank */
network_rank_update(network);
l_queue_insert(device->networks_sorted, network,
network_rank_compare, NULL);
return false;
}
/* Drop networks that have no more BSSs in range */
l_debug("No remaining BSSs for SSID: %s -- Removing network",
network_get_ssid(network));
network_remove(network, -ERANGE);
return true;
}
/*
* Returns the network object the BSS was added to or NULL if ignored.
*/
static struct network *add_seen_bss(struct device *device, struct scan_bss *bss)
{
struct network *network;
struct ie_rsn_info info;
int r;
enum security security;
const char *path;
char ssid[33];
l_debug("Found BSS '%s' with SSID: %s, freq: %u, rank: %u, "
"strength: %i",
util_address_to_string(bss->addr),
util_ssid_to_utf8(bss->ssid_len, bss->ssid),
bss->frequency, bss->rank, bss->signal_strength);
if (util_ssid_is_hidden(bss->ssid_len, bss->ssid)) {
l_debug("Ignoring BSS with hidden SSID");
device->seen_hidden_networks = true;
return NULL;
}
if (!util_ssid_is_utf8(bss->ssid_len, bss->ssid)) {
l_debug("Ignoring BSS with non-UTF8 SSID");
return NULL;
}
memcpy(ssid, bss->ssid, bss->ssid_len);
ssid[bss->ssid_len] = '\0';
memset(&info, 0, sizeof(info));
r = scan_bss_get_rsn_info(bss, &info);
if (r < 0) {
if (r != -ENOENT)
return NULL;
security = security_determine(bss->capability, NULL);
} else
security = security_determine(bss->capability, &info);
path = iwd_network_get_path(device, ssid, security);
network = l_hashmap_lookup(device->networks, path);
if (!network) {
network = network_create(device, ssid, security);
if (!network_register(network, path)) {
network_remove(network, -EINVAL);
return NULL;
}
l_hashmap_insert(device->networks,
network_get_path(network), network);
l_debug("Added new Network \"%s\" security %s",
network_get_ssid(network), security_to_str(security));
}
network_bss_add(network, bss);
return network;
}
static bool bss_match(const void *a, const void *b)
{
const struct scan_bss *bss_a = a;
const struct scan_bss *bss_b = b;
return !memcmp(bss_a->addr, bss_b->addr, sizeof(bss_a->addr));
}
/*
* Used when scan results were obtained; either from passive scan running
* inside device.c or active scans running in other state machines, e.g. wsc.c
*/
void device_set_scan_results(struct device *device, struct l_queue *bss_list,
bool add_to_autoconnect)
{
struct network *network;
const struct l_queue_entry *bss_entry;
device->old_bss_list = device->bss_list;
device->bss_list = bss_list;
device->seen_hidden_networks = false;
while ((network = l_queue_pop_head(device->networks_sorted)))
network_bss_list_clear(network);
l_queue_destroy(device->station->autoconnect_list, l_free);
device->station->autoconnect_list = l_queue_new();
for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
bss_entry = bss_entry->next) {
struct scan_bss *bss = bss_entry->data;
struct network *network = add_seen_bss(device, bss);
if (network && add_to_autoconnect)
station_add_autoconnect_bss(device->station,
network, bss);
}
if (device->connected_bss) {
struct scan_bss *bss;
bss = l_queue_find(device->bss_list, bss_match,
device->connected_bss);
if (!bss) {
l_warn("Connected BSS not in scan results!");
device->connected_bss->rank = 0;
l_queue_push_tail(device->bss_list,
device->connected_bss);
network_bss_add(device->connected_network,
device->connected_bss);
l_queue_remove(device->old_bss_list,
device->connected_bss);
} else
device->connected_bss = bss;
}
l_hashmap_foreach_remove(device->networks, process_network, device);
l_queue_destroy(device->old_bss_list, bss_free);
device->old_bss_list = NULL;
station_set_scan_results(device->station, bss_list, add_to_autoconnect);
}
static bool new_scan_results(uint32_t wiphy_id, uint32_t ifindex, int err,
@ -419,9 +232,7 @@ bool device_remove_state_watch(struct device *device, uint32_t id)
struct network *device_network_find(struct device *device, const char *ssid,
enum security security)
{
const char *path = iwd_network_get_path(device, ssid, security);
return l_hashmap_lookup(device->networks, path);
return station_network_find(device->station, ssid, security);
}
static void device_enter_state(struct device *device, enum device_state state)
@ -492,6 +303,7 @@ static void device_reset_connection_state(struct device *device)
scan_cancel(device->index, device->roam_scan_id);
device->connected_bss = NULL;
station->connected_bss = NULL;
device->connected_network = NULL;
station->connected_network = NULL;
@ -605,6 +417,8 @@ static void device_transition_reassociate(struct device *device,
struct scan_bss *bss,
struct handshake_state *new_hs)
{
struct station *station = device->station;
if (netdev_reassociate(device->netdev, bss, device->connected_bss,
new_hs, device_netdev_event,
device_reassociate_cb, device) < 0) {
@ -615,6 +429,7 @@ static void device_transition_reassociate(struct device *device,
}
device->connected_bss = bss;
station->connected_bss = bss;
device->preparing_roam = false;
device_enter_state(device, DEVICE_STATE_ROAMING);
}
@ -642,7 +457,7 @@ static void device_preauthenticate_cb(struct netdev *netdev,
if (!device->preparing_roam || result == NETDEV_RESULT_ABORTED)
return;
bss = l_queue_find(device->bss_list, bss_match_bssid,
bss = l_queue_find(device->station->bss_list, bss_match_bssid,
device->preauth_bssid);
if (!bss) {
l_error("Roam target BSS not found");
@ -732,6 +547,7 @@ static void device_transition_start(struct device *device, struct scan_bss *bss)
}
device->connected_bss = bss;
station->connected_bss = bss;
device->preparing_roam = false;
device_enter_state(device, DEVICE_STATE_ROAMING);
@ -895,7 +711,7 @@ next:
goto fail_free_bss;
/* See if we have anywhere to roam to */
if (!best_bss || bss_match(best_bss, device->connected_bss))
if (!best_bss || scan_bss_addr_eq(best_bss, device->connected_bss))
goto fail_free_bss;
bss = network_bss_find_by_addr(network, best_bss->addr);
@ -904,7 +720,7 @@ next:
best_bss = bss;
} else {
network_bss_add(network, best_bss);
l_queue_push_tail(device->bss_list, best_bss);
l_queue_push_tail(device->station->bss_list, best_bss);
}
device_transition_start(device, best_bss);
@ -1433,6 +1249,7 @@ int __device_connect_network(struct device *device, struct network *network,
}
device->connected_bss = bss;
station->connected_bss = bss;
device->connected_network = network;
station->connected_network = network;
@ -1516,7 +1333,7 @@ static struct l_dbus_message *device_scan(struct l_dbus *dbus,
* use passive scanning to hide our MAC address
*/
if (!device->connected_bss &&
!(device->seen_hidden_networks &&
!(device->station->seen_hidden_networks &&
known_networks_has_hidden())) {
if (!scan_passive(device->index, device_scan_triggered,
new_scan_results, device, NULL))
@ -1625,6 +1442,7 @@ static struct l_dbus_message *device_get_networks(struct l_dbus *dbus,
struct device *device = user_data;
struct l_dbus_message *reply;
struct l_dbus_message_builder *builder;
struct l_queue *sorted = device->station->networks_sorted;
const struct l_queue_entry *entry;
reply = l_dbus_message_new_method_return(message);
@ -1632,8 +1450,7 @@ static struct l_dbus_message *device_get_networks(struct l_dbus *dbus,
l_dbus_message_builder_enter_array(builder, "(osns)");
for (entry = l_queue_get_entries(device->networks_sorted); entry;
entry = entry->next) {
for (entry = l_queue_get_entries(sorted); entry; entry = entry->next) {
const struct network *network = entry->data;
enum security security = network_get_security(network);
int16_t signal_strength = network_get_signal_strength(network);
@ -1736,15 +1553,6 @@ static struct l_dbus_message *device_signal_agent_register(struct l_dbus *dbus,
return l_dbus_message_new_method_return(message);
}
static bool device_remove_network(const void *key, void *data, void *user_data)
{
struct network *network = data;
network_remove(network, -ESHUTDOWN);
return true;
}
static struct l_dbus_message *device_signal_agent_unregister(
struct l_dbus *dbus,
struct l_dbus_message *message,
@ -1826,8 +1634,8 @@ static bool device_hidden_network_scan_results(uint32_t wiphy_id,
memcmp(bss->ssid, ssid, ssid_len))
goto next;
if (add_seen_bss(device, bss)) {
l_queue_push_tail(device->bss_list, bss);
if (station_add_seen_bss(device->station, bss)) {
l_queue_push_tail(device->station->bss_list, bss);
continue;
}
@ -2324,15 +2132,6 @@ static void device_netdev_notify(struct netdev *netdev,
device_reset_connection_state(device);
l_hashmap_foreach_remove(device->networks,
device_remove_network, device);
l_queue_destroy(device->bss_list, bss_free);
device->bss_list = l_queue_new();
l_queue_destroy(device->networks_sorted, NULL);
device->networks_sorted = l_queue_new();
l_dbus_property_changed(dbus, device_get_path(device),
IWD_DEVICE_INTERFACE, "Powered");
break;
@ -2357,13 +2156,7 @@ struct device *device_create(struct wiphy *wiphy, struct netdev *netdev)
const uint8_t action_ap_roam_prefix[2] = { 0x0a, 0x07 };
device = l_new(struct device, 1);
device->bss_list = l_queue_new();
device->networks = l_hashmap_new();
watchlist_init(&device->state_watches, NULL);
l_hashmap_set_hash_function(device->networks, l_str_hash);
l_hashmap_set_compare_function(device->networks,
(l_hashmap_compare_func_t) strcmp);
device->networks_sorted = l_queue_new();
device->index = ifindex;
device->wiphy = wiphy;
device->netdev = netdev;
@ -2425,12 +2218,6 @@ static void device_free(void *user)
dbus = dbus_get_bus();
l_dbus_unregister_object(dbus, device_get_path(device));
l_queue_destroy(device->networks_sorted, NULL);
l_hashmap_destroy(device->networks, network_free);
l_queue_destroy(device->bss_list, bss_free);
l_queue_destroy(device->old_bss_list, bss_free);
l_timeout_remove(device->roam_trigger_timeout);
scan_ifindex_remove(device->index);

View File

@ -24,6 +24,7 @@
#include <config.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <ell/ell.h>
@ -96,6 +97,203 @@ void station_add_autoconnect_bss(struct station *station,
autoconnect_rank_compare, NULL);
}
static void bss_free(void *data)
{
struct scan_bss *bss = data;
const char *addr;
addr = util_address_to_string(bss->addr);
l_debug("Freeing BSS %s", addr);
scan_bss_free(bss);
}
static void network_free(void *data)
{
struct network *network = data;
network_remove(network, -ESHUTDOWN);
}
static bool process_network(const void *key, void *data, void *user_data)
{
struct network *network = data;
struct station *station = user_data;
if (!network_bss_list_isempty(network)) {
/* Build the network list ordered by rank */
network_rank_update(network);
l_queue_insert(station->networks_sorted, network,
network_rank_compare, NULL);
return false;
}
/* Drop networks that have no more BSSs in range */
l_debug("No remaining BSSs for SSID: %s -- Removing network",
network_get_ssid(network));
network_remove(network, -ERANGE);
return true;
}
static const char *iwd_network_get_path(struct station *station,
const char *ssid,
enum security security)
{
static char path[256];
unsigned int pos, i;
pos = snprintf(path, sizeof(path), "%s/",
netdev_get_path(station->netdev));
for (i = 0; ssid[i] && pos < sizeof(path); i++)
pos += snprintf(path + pos, sizeof(path) - pos, "%02x",
ssid[i]);
snprintf(path + pos, sizeof(path) - pos, "_%s",
security_to_str(security));
return path;
}
struct network *station_network_find(struct station *station, const char *ssid,
enum security security)
{
const char *path = iwd_network_get_path(station, ssid, security);
return l_hashmap_lookup(station->networks, path);
}
/*
* Returns the network object the BSS was added to or NULL if ignored.
*/
struct network *station_add_seen_bss(struct station *station,
struct scan_bss *bss)
{
struct device *device = netdev_get_device(station->netdev);
struct network *network;
struct ie_rsn_info info;
int r;
enum security security;
const char *path;
char ssid[33];
l_debug("Found BSS '%s' with SSID: %s, freq: %u, rank: %u, "
"strength: %i",
util_address_to_string(bss->addr),
util_ssid_to_utf8(bss->ssid_len, bss->ssid),
bss->frequency, bss->rank, bss->signal_strength);
if (util_ssid_is_hidden(bss->ssid_len, bss->ssid)) {
l_debug("Ignoring BSS with hidden SSID");
station->seen_hidden_networks = true;
return NULL;
}
if (!util_ssid_is_utf8(bss->ssid_len, bss->ssid)) {
l_debug("Ignoring BSS with non-UTF8 SSID");
return NULL;
}
memcpy(ssid, bss->ssid, bss->ssid_len);
ssid[bss->ssid_len] = '\0';
memset(&info, 0, sizeof(info));
r = scan_bss_get_rsn_info(bss, &info);
if (r < 0) {
if (r != -ENOENT)
return NULL;
security = security_determine(bss->capability, NULL);
} else
security = security_determine(bss->capability, &info);
path = iwd_network_get_path(station, ssid, security);
network = l_hashmap_lookup(station->networks, path);
if (!network) {
network = network_create(device, ssid, security);
if (!network_register(network, path)) {
network_remove(network, -EINVAL);
return NULL;
}
l_hashmap_insert(station->networks,
network_get_path(network), network);
l_debug("Added new Network \"%s\" security %s",
network_get_ssid(network), security_to_str(security));
}
network_bss_add(network, bss);
return network;
}
static bool bss_match(const void *a, const void *b)
{
const struct scan_bss *bss_a = a;
const struct scan_bss *bss_b = b;
return !memcmp(bss_a->addr, bss_b->addr, sizeof(bss_a->addr));
}
/*
* Used when scan results were obtained; either from passive scan running
* inside station module or active scans running in other state machines, e.g.
* wsc
*/
void station_set_scan_results(struct station *station, struct l_queue *bss_list,
bool add_to_autoconnect)
{
struct l_queue *old_bss_list = station->bss_list;
struct network *network;
const struct l_queue_entry *bss_entry;
station->bss_list = bss_list;
station->seen_hidden_networks = false;
while ((network = l_queue_pop_head(station->networks_sorted)))
network_bss_list_clear(network);
l_queue_destroy(station->autoconnect_list, l_free);
station->autoconnect_list = l_queue_new();
for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
bss_entry = bss_entry->next) {
struct scan_bss *bss = bss_entry->data;
struct network *network = station_add_seen_bss(station, bss);
if (network && add_to_autoconnect)
station_add_autoconnect_bss(station, network, bss);
}
if (station->connected_bss) {
struct scan_bss *bss;
bss = l_queue_find(station->bss_list, bss_match,
station->connected_bss);
if (!bss) {
l_warn("Connected BSS not in scan results!");
station->connected_bss->rank = 0;
l_queue_push_tail(station->bss_list,
station->connected_bss);
network_bss_add(station->connected_network,
station->connected_bss);
l_queue_remove(old_bss_list, station->connected_bss);
} else
station->connected_bss = bss;
}
l_hashmap_foreach_remove(station->networks, process_network, station);
l_queue_destroy(old_bss_list, bss_free);
}
static enum ie_rsn_akm_suite select_akm_suite(struct network *network,
struct scan_bss *bss,
struct ie_rsn_info *info)
@ -304,6 +502,13 @@ struct station *station_create(struct wiphy *wiphy, struct netdev *netdev)
station = l_new(struct station, 1);
station->bss_list = l_queue_new();
station->networks = l_hashmap_new();
l_hashmap_set_hash_function(station->networks, l_str_hash);
l_hashmap_set_compare_function(station->networks,
(l_hashmap_compare_func_t) strcmp);
station->networks_sorted = l_queue_new();
station->wiphy = wiphy;
station->netdev = netdev;
@ -319,6 +524,9 @@ void station_free(struct station *station)
if (!l_queue_remove(station_list, station))
return;
l_queue_destroy(station->networks_sorted, NULL);
l_hashmap_destroy(station->networks, network_free);
l_queue_destroy(station->bss_list, bss_free);
l_queue_destroy(station->autoconnect_list, l_free);
l_free(station);

View File

@ -29,11 +29,17 @@ struct scan_bss;
struct network;
struct station {
struct scan_bss *connected_bss;
struct network *connected_network;
struct l_queue *autoconnect_list;
struct l_queue *bss_list;
struct l_hashmap *networks;
struct l_queue *networks_sorted;
struct wiphy *wiphy;
struct netdev *netdev;
bool seen_hidden_networks : 1;
};
void station_autoconnect_next(struct station *station);
@ -41,6 +47,15 @@ void station_add_autoconnect_bss(struct station *station,
struct network *network,
struct scan_bss *bss);
struct network *station_network_find(struct station *station, const char *ssid,
enum security security);
struct network *station_add_seen_bss(struct station *station,
struct scan_bss *bss);
void station_set_scan_results(struct station *station, struct l_queue *bss_list,
bool add_to_autoconnect);
struct handshake_state *station_handshake_setup(struct station *station,
struct network *network,
struct scan_bss *bss);