diff mbox series

[3/5] dpp: fix extra settings not being used when connecting

Message ID 20231214180110.130991-3-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [1/5] network: remove 'path' from settings_load_pt_ecc | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-gitlint success GitLint

Commit Message

James Prestwood Dec. 14, 2023, 6:01 p.m. UTC
After DPP completes all settings are written and synced to disk as
well as credentials set into the network object itself. The way
network/knownnetworks worked at the time did not actually re-load
these settings before the connection attempt was made which means
that extra settings not set into the network object were not used,
i.e. Hidden/Sendhostname. The connection itself always succeeded
because the network object was given the credentials directly via
setters.

Now network and knownnetworks support updating on the directory
watch callback and ADDED/UPDATED known network events. Take advantage
of this and if the network object already exists after DPP (from a
prior scan) wait unil known networks adds/updates the network and
issue the connection after that.
---
 src/dpp.c | 124 ++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 93 insertions(+), 31 deletions(-)
diff mbox series

Patch

diff --git a/src/dpp.c b/src/dpp.c
index 1ff4b99e..dca40660 100644
--- a/src/dpp.c
+++ b/src/dpp.c
@@ -53,6 +53,7 @@ 
 #include "src/network.h"
 #include "src/handshake.h"
 #include "src/nl80211util.h"
+#include "src/knownnetworks.h"
 
 #define DPP_FRAME_MAX_RETRIES 5
 #define DPP_FRAME_RETRY_TIMEOUT 1
@@ -101,6 +102,7 @@  struct dpp_sm {
 	uint8_t role;
 	int refcount;
 	uint32_t station_watch;
+	uint32_t known_network_watch;
 
 	uint64_t wdev_id;
 
@@ -168,6 +170,8 @@  struct dpp_sm {
 
 	struct l_dbus_message *pending;
 
+	struct l_idle *connect_idle;
+
 	/* PKEX-specific values */
 	char *pkex_id;
 	char *pkex_key;
@@ -515,6 +519,11 @@  static void dpp_reset(struct dpp_sm *dpp)
 		dpp->pkex_scan_id = 0;
 	}
 
+	if (dpp->connect_idle) {
+		l_idle_remove(dpp->connect_idle);
+		dpp->connect_idle = NULL;
+	}
+
 	dpp->state = DPP_STATE_NOTHING;
 	dpp->new_freq = 0;
 	dpp->frame_retry = 0;
@@ -570,6 +579,8 @@  static void dpp_free(struct dpp_sm *dpp)
 	if (station)
 		station_remove_state_watch(station, dpp->station_watch);
 
+	known_networks_watch_remove(dpp->known_network_watch);
+
 	l_free(dpp);
 }
 
@@ -812,8 +823,6 @@  static void dpp_write_config(struct dpp_configuration *config,
 {
 	_auto_(l_settings_free) struct l_settings *settings = l_settings_new();
 	_auto_(l_free) char *path;
-	_auto_(l_free) uint8_t *psk = NULL;
-	size_t psk_len;
 
 	path = storage_get_network_file_path(SECURITY_PSK, config->ssid);
 
@@ -822,22 +831,13 @@  static void dpp_write_config(struct dpp_configuration *config,
 		l_settings_remove_group(settings, "Security");
 	}
 
-	if (config->passphrase) {
+	if (config->passphrase)
 		l_settings_set_string(settings, "Security", "Passphrase",
 				config->passphrase);
-		if (network)
-			network_set_passphrase(network, config->passphrase);
-
-	} else if (config->psk) {
+	else if (config->psk)
 		l_settings_set_string(settings, "Security", "PreSharedKey",
 				config->psk);
 
-		psk = l_util_from_hexstring(config->psk, &psk_len);
-
-		if (network)
-			network_set_psk(network, psk);
-	}
-
 	if (config->send_hostname)
 		l_settings_set_bool(settings, "IPv4", "SendHostname", true);
 
@@ -856,14 +856,39 @@  static void dpp_scan_triggered(int err, void *user_data)
 		l_error("Failed to trigger DPP scan");
 }
 
+static void dpp_start_connect(struct l_idle *idle, void *user_data)
+{
+	struct dpp_sm *dpp = user_data;
+	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
+	struct scan_bss *bss;
+	struct network *network;
+	int ret;
+
+	network = station_network_find(station, dpp->config->ssid,
+					SECURITY_PSK);
+
+	dpp_reset(dpp);
+
+	if (!network) {
+		l_debug("Network was not found!");
+		return;
+	}
+
+	l_debug("connecting to %s from DPP", network_get_ssid(network));
+
+	bss = network_bss_select(network, true);
+	ret = network_autoconnect(network, bss);
+	if (ret < 0)
+		l_warn("failed to connect after DPP (%d) %s", ret,
+			strerror(ret));
+}
+
 static bool dpp_scan_results(int err, struct l_queue *bss_list,
 				const struct scan_freq_set *freqs,
 				void *userdata)
 {
 	struct dpp_sm *dpp = userdata;
 	struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
-	struct scan_bss *bss;
-	struct network *network;
 
 	if (err < 0)
 		goto reset;
@@ -880,18 +905,7 @@  static bool dpp_scan_results(int err, struct l_queue *bss_list,
 
 	station_set_scan_results(station, bss_list, freqs, false);
 
-	network = station_network_find(station, dpp->config->ssid,
-					SECURITY_PSK);
-
-	dpp_reset(dpp);
-
-	if (!network) {
-		l_debug("Network was not found after scanning");
-		return true;
-	}
-
-	bss = network_bss_select(network, true);
-	network_autoconnect(network, bss);
+	dpp_start_connect(NULL, dpp);
 
 	return true;
 
@@ -907,6 +921,51 @@  static void dpp_scan_destroy(void *userdata)
 	dpp_reset(dpp);
 }
 
+static void dpp_known_network_watch(enum known_networks_event event,
+					const struct network_info *info,
+					void *user_data)
+{
+	struct dpp_sm *dpp = user_data;
+
+	/*
+	 * Check the following
+	 *  - DPP is enrolling
+	 *  - DPP finished (dpp->config is set)
+	 *  - This is for the network DPP just configured
+	 *  - DPP isn't already trying to connect (e.g. if the profile was
+	 *    immediately modified after DPP synced it).
+	 *  - DPP didn't start a scan for the network.
+	 */
+	if (dpp->role != DPP_CAPABILITY_ENROLLEE)
+		return;
+	if (!dpp->config)
+		return;
+	if (strcmp(info->ssid, dpp->config->ssid))
+		return;
+	if (dpp->connect_idle)
+		return;
+	if (dpp->connect_scan_id)
+		return;
+
+	switch (event) {
+	case KNOWN_NETWORKS_EVENT_ADDED:
+	case KNOWN_NETWORKS_EVENT_UPDATED:
+		/*
+		 * network.c takes care of updating the settings for the
+		 * network. This callback just tells us to begin the connection.
+		 * We do have use an idle here because there is no strict
+		 * guarantee of ordering between known network events, e.g. DPP
+		 * could have been called into prior to network and the network
+		 * object isn't updated yet.
+		 */
+		dpp->connect_idle = l_idle_create(dpp_start_connect, dpp, NULL);
+		break;
+	case KNOWN_NETWORKS_EVENT_REMOVED:
+		l_warn("profile was removed before DPP could connect");
+		break;
+	}
+}
+
 static void dpp_handle_config_response_frame(const struct mmpdu_header *frame,
 				const void *body, size_t body_len,
 				int rssi, void *user_data)
@@ -1074,10 +1133,11 @@  static void dpp_handle_config_response_frame(const struct mmpdu_header *frame,
 
 	offchannel_cancel(dpp->wdev_id, dpp->offchannel_id);
 
-	if (network && bss)
-		__station_connect_network(station, network, bss,
-						STATION_STATE_CONNECTING);
-	else if (station) {
+	if (network && bss) {
+		l_debug("delaying connect until settings are synced");
+		dpp->config = config;
+		return;
+	} else if (station) {
 		struct scan_parameters params = {0};
 
 		params.ssid = (void *) config->ssid;
@@ -3780,6 +3840,8 @@  static void dpp_create(struct netdev *netdev)
 
 	dpp->station_watch = station_add_state_watch(station,
 					dpp_station_state_watch, dpp, NULL);
+	dpp->known_network_watch = known_networks_watch_add(
+					dpp_known_network_watch, dpp, NULL);
 
 	l_queue_push_tail(dpp_list, dpp);
 }