diff --git a/src/network.c b/src/network.c index 09099fac..8c16d9a6 100644 --- a/src/network.c +++ b/src/network.c @@ -75,6 +75,7 @@ struct network { struct l_ecc_point *sae_pt_20; /* SAE PT for Group 20 */ unsigned int agent_request; struct l_queue *bss_list; + struct l_queue *old_bss_list; struct l_settings *settings; struct l_queue *secrets; struct l_queue *blacklist; /* temporary blacklist for BSS's */ @@ -1129,12 +1130,107 @@ bool network_update_known_frequencies(struct network *network) return true; } +const char *network_bss_get_path(const struct network *network, + const struct scan_bss *bss) +{ + static char path[256]; + + snprintf(path, sizeof(path), "%s/%02x%02x%02x%02x%02x%02x", + network->object_path, MAC_STR(bss->addr)); + + return path; +} + +static bool network_unregister_bss(void *a, void *user_data) +{ + struct scan_bss *bss = a; + struct network *network = user_data; + + l_dbus_unregister_object(dbus_get_bus(), + network_bss_get_path(network, bss)); + + return true; +} + +static bool network_register_bss(struct network *network, struct scan_bss *bss) +{ + const char *path = network_bss_get_path(network, bss); + struct scan_bss *old; + + /* + * If we find this path in the object tree update the data to the new + * scan_bss pointer, as this one will be freed soon. + */ + old = l_dbus_object_get_data(dbus_get_bus(), path, IWD_BSS_INTERFACE); + if (old) + return l_dbus_object_set_data(dbus_get_bus(), path, + IWD_BSS_INTERFACE, bss); + + if (!l_dbus_object_add_interface(dbus_get_bus(), path, + IWD_BSS_INTERFACE, bss)) + return false; + + if (!l_dbus_object_add_interface(dbus_get_bus(), path, + L_DBUS_INTERFACE_PROPERTIES, bss)) + return false; + + return true; +} + +void network_bss_start_update(struct network *network) +{ + network->old_bss_list = network->bss_list; + network->bss_list = l_queue_new(); +} + +void network_bss_stop_update(struct network *network, + const struct scan_freq_set *limit_freqs) +{ + const struct l_queue_entry *e; + + /* + * Update has finished, clean up any BSS's from the old list that have + * been registered on DBus. + */ + for (e = l_queue_get_entries(network->old_bss_list); e; e = e->next) { + const struct scan_bss *old = e->data; + const struct scan_bss *bss; + const char *path = network_bss_get_path(network, old); + + bss = l_dbus_object_get_data(dbus_get_bus(), path, + IWD_BSS_INTERFACE); + + /* + * Don't remove BSS's who's frequencies were not in the set. + * This happens due to scan subsets (i.e. from station) so some + * BSS's won't be seen since the frequency is not being scanned. + * These BSS's will get cleared out eventually if they have + * actually disappeared. + */ + if (!scan_freq_set_contains(limit_freqs, bss->frequency)) + continue; + + /* + * The lookup matched the user data of an old BSS. This should + * be unregistered from DBus + */ + if (bss && bss == old) + l_dbus_object_remove_interface(dbus_get_bus(), path, + IWD_BSS_INTERFACE); + } + + l_queue_destroy(network->old_bss_list, NULL); + network->old_bss_list = NULL; +} + bool network_bss_add(struct network *network, struct scan_bss *bss) { if (!l_queue_insert(network->bss_list, bss, scan_bss_rank_compare, NULL)) return false; + network_register_bss(network, bss); + /* Done if BSS is not HS20 or we already have network_info set */ if (!bss->hs20_capable) return true; @@ -1168,6 +1264,8 @@ bool network_bss_update(struct network *network, struct scan_bss *bss) l_queue_insert(network->bss_list, bss, scan_bss_rank_compare, NULL); + network_register_bss(network, bss); + /* Sync frequency for already known networks */ if (network->info) { known_network_add_frequency(network->info, bss->frequency); @@ -1190,7 +1288,12 @@ void network_bss_list_clear(struct network *network) struct scan_bss *network_bss_list_pop(struct network *network) { - return l_queue_pop_head(network->bss_list); + struct scan_bss *bss = l_queue_pop_head(network->bss_list); + + if (bss) + network_unregister_bss(bss, network); + + return bss; } struct scan_bss *network_bss_find_by_addr(struct network *network, @@ -1899,6 +2002,9 @@ static void network_unregister(struct network *network, int reason) void network_remove(struct network *network, int reason) { + l_queue_foreach_remove(network->bss_list, + network_unregister_bss, network); + if (network->object_path) network_unregister(network, reason); @@ -2155,6 +2261,24 @@ static void setup_network_interface(struct l_dbus_interface *interface) network_property_get_known_network, NULL); } +static bool network_bss_property_get_address(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + struct scan_bss *bss = user_data; + + l_dbus_message_builder_append_basic(builder, 's', + util_address_to_string(bss->addr)); + return true; +} + +static void setup_bss_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_property(interface, "Address", 0, "s", + network_bss_property_get_address, NULL); +} + static int network_init(void) { if (!l_dbus_register_interface(dbus_get_bus(), IWD_NETWORK_INTERFACE, @@ -2162,6 +2286,11 @@ static int network_init(void) l_error("Unable to register %s interface", IWD_NETWORK_INTERFACE); + if (!l_dbus_register_interface(dbus_get_bus(), IWD_BSS_INTERFACE, + setup_bss_interface, NULL, false)) + l_error("Unable to register %s interface", + IWD_BSS_INTERFACE); + known_networks_watch = known_networks_watch_add(known_networks_changed, NULL, NULL); @@ -2179,6 +2308,7 @@ static void network_exit(void) event_watch = 0; l_dbus_unregister_interface(dbus_get_bus(), IWD_NETWORK_INTERFACE); + l_dbus_unregister_interface(dbus_get_bus(), IWD_BSS_INTERFACE); } IWD_MODULE(network, network_init, network_exit) diff --git a/src/network.h b/src/network.h index 17dfcca8..b27158ee 100644 --- a/src/network.h +++ b/src/network.h @@ -30,6 +30,7 @@ struct network; struct scan_bss; struct handshake_state; struct erp_cache_entry; +struct scan_freq_set; void network_connected(struct network *network); void network_disconnected(struct network *network); @@ -67,8 +68,13 @@ int network_can_connect_bss(struct network *network, const struct scan_bss *bss); int network_autoconnect(struct network *network, struct scan_bss *bss); void network_connect_failed(struct network *network, bool in_handshake); +void network_bss_start_update(struct network *network); +void network_bss_stop_update(struct network *network, + const struct scan_freq_set *freqs); bool network_bss_add(struct network *network, struct scan_bss *bss); bool network_bss_update(struct network *network, struct scan_bss *bss); +const char *network_bss_get_path(const struct network *network, + const struct scan_bss *bss); bool network_bss_list_isempty(struct network *network); void network_bss_list_clear(struct network *network); struct scan_bss *network_bss_list_pop(struct network *network);