aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Prestwood <prestwoj@gmail.com>2023-12-19 10:08:24 -0800
committerDenis Kenzior <denkenz@gmail.com>2023-12-19 12:41:50 -0600
commit8f5109c4393ea9ea429ff080d3e57c1392a2a637 (patch)
treef1e8a6518c26d07f98013ecf3a66f905982bf616
parent7a76385ec9f9f208cfb2706b4f8b1352e69e6fa0 (diff)
dpp: fix extra settings not being used when connecting
Before this change DPP was writing the credentials both to disk and into the network object directly. This allowed the connection to work fine but additional settings were not picked up due to network_set_passphrase/psk loading the settings before they were written. Instead DPP can avoid setting the credentials to the network object entirely and just write them to disk. Then, wait for known networks to notify that the profile was either created or updated then DPP can proceed to connecting. network_autoconnect() will take care of loading the profile that DPP wrote and remove the need for DPP to touch the network object at all. One thing to note is that an idle callback is still needed from within the known networks callback. This is because a new profile requires network.c to set the network_info which is done in the known networks callback. Rather than assume that network.c will be called into before dpp.c an l_idle was added.
-rw-r--r--src/dpp.c124
1 files changed, 93 insertions, 31 deletions
diff --git a/src/dpp.c b/src/dpp.c
index 1ff4b99e3..af6574fbe 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);
}