[PATCH v2 18/42] wireguard: Fix shutdown, ensure one exit and set no agent is used
Jussi Laakkonen <[email protected]> Wed, 13 Aug 2025 18:01:50 +0300
| Newsgroups | dev.linux.lists.connman |
|---|---|
| Message-ID | <[email protected]> |
Split disconnect into wg_disconnect() and disconnect(). Use disconnect()
to properly disconnect when error occurs with the appropriate error
code. Do the same in wg_disconnect() by using the error code to be from
device removal.
The shutdown process requires, that in order to do the proper transition
to IDLE, vpn_died() is to be called. This is normally called by the task
that has been killed so simulate this behavior in WireGuard by adding a
delayed call for that. It is to be executed only once as all the cleanup
is to be done at that last step. Use a delay of 50ms to simulate exit.
Add use of the VPN_FLAG_NO_AGENT to declare that WireGuard does not use
a VPN agent. This ensures that the state transition is done according
to state machine by __vpn_provider_connect().
Add separate create/free for struct wireguard_info for VPN consistency.
---
vpn/plugins/wireguard.c | 103 ++++++++++++++++++++++++++++++++--------
1 file changed, 82 insertions(+), 21 deletions(-)
diff --git a/vpn/plugins/wireguard.c b/vpn/plugins/wireguard.c
index b7fa77b3..ee97f378 100644
--- a/vpn/plugins/wireguard.c
+++ b/vpn/plugins/wireguard.c
@@ -65,6 +65,7 @@ struct wireguard_info {
GResolv *resolv;
guint resolv_id;
guint remove_resolv_id;
+ guint dying_id;
};
struct sockaddr_u {
@@ -90,6 +91,31 @@ struct {
{"WireGuard.PersistentKeepalive", true}
};
+static struct wireguard_info *create_private_data(struct vpn_provider *provider)
+{
+ struct wireguard_info *info;
+
+ info = g_malloc0(sizeof(struct wireguard_info));
+ info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
+ info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
+ info->device.first_peer = &info->peer;
+ info->device.last_peer = &info->peer;
+ info->provider = vpn_provider_ref(provider);
+
+ return info;
+}
+
+static void free_private_data(struct wireguard_info *info)
+{
+ if (vpn_provider_get_plugin_data(info->provider) == info)
+ vpn_provider_set_plugin_data(info->provider, NULL);
+
+ vpn_provider_unref(info->provider);
+ g_free(info->endpoint_fqdn);
+ g_free(info->port);
+ g_free(info);
+}
+
static int parse_key(const char *str, wg_key key)
{
unsigned char *buf;
@@ -422,6 +448,8 @@ static void resolve_endpoint_cb(GResolvResultStatus status,
run_dns_reresolve(info);
}
+static int disconnect(struct vpn_provider *provider, int error);
+
static gboolean wg_dns_reresolve_cb(gpointer user_data)
{
struct wireguard_info *info = user_data;
@@ -451,7 +479,7 @@ static gboolean wg_dns_reresolve_cb(gpointer user_data)
connman_error("failed to start hostname lookup for %s, err %d",
info->endpoint_fqdn,
error->code);
- vpn_died(NULL, -EIO, info->provider);
+ disconnect(info->provider, -EIO);
}
g_clear_error(&error);
@@ -467,7 +495,7 @@ static void run_dns_reresolve(struct wireguard_info *info)
if (vpn_provider_get_connection_errors(info->provider) >=
DNS_RERESOLVE_ERROR_LIMIT) {
connman_warn("reresolve error limit reached");
- vpn_died(NULL, -ENONET, info->provider);
+ disconnect(info->provider, -ENONET);
info->reresolve_id = 0;
return;
}
@@ -487,12 +515,7 @@ static int wg_connect(struct vpn_provider *provider,
char *ifname;
int err = -EINVAL;
- info = g_malloc0(sizeof(struct wireguard_info));
- info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
- info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
- info->device.first_peer = &info->peer;
- info->device.last_peer = &info->peer;
- info->provider = vpn_provider_ref(provider);
+ info = create_private_data(provider);
DBG("");
@@ -646,8 +669,34 @@ error:
goto done;
}
-static void wg_disconnect(struct vpn_provider *provider)
+struct wireguard_exit_data {
+ struct vpn_provider *provider;
+ int err;
+};
+
+static gboolean wg_died(gpointer user_data)
{
+ struct wireguard_exit_data *data = user_data;
+ struct wireguard_info *info;
+
+ DBG("");
+
+ /* No task for no daemon VPN - use vpn_died() with no task. */
+ vpn_died(NULL, data->err, data->provider);
+
+ info = vpn_provider_get_plugin_data(data->provider);
+ if (info)
+ free_private_data(info);
+
+ g_free(data);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Allow to overrule the exit code for vpn_died */
+static int disconnect(struct vpn_provider *provider, int err)
+{
+ struct wireguard_exit_data *data;
struct wireguard_info *info;
int exit_code;
@@ -655,7 +704,10 @@ static void wg_disconnect(struct vpn_provider *provider)
info = vpn_provider_get_plugin_data(provider);
if (!info)
- return;
+ return -ENODATA;
+
+ if (info->dying_id)
+ return -EALREADY;
if (info->reresolve_id > 0)
g_source_remove(info->reresolve_id);
@@ -663,21 +715,29 @@ static void wg_disconnect(struct vpn_provider *provider)
if (info->resolv || info->resolv_id)
remove_resolv(info);
- vpn_provider_set_plugin_data(provider, NULL);
-
vpn_provider_set_state(provider, VPN_PROVIDER_STATE_DISCONNECT);
exit_code = wg_del_device(info->device.name);
- vpn_provider_unref(info->provider);
- g_free(info->endpoint_fqdn);
- g_free(info->port);
- g_free(info);
+ /* Simulate a task-running VPN to issue vpn_died after exiting this */
+ data = g_malloc0(sizeof(struct wireguard_exit_data));
+ data->provider = provider;
+ data->err = err ? err : exit_code;
+
+ info->dying_id = g_timeout_add(50, wg_died, data);
+
+ return exit_code;
+}
+
+static void wg_disconnect(struct vpn_provider *provider)
+{
+ int exit_code;
+
+ DBG("");
- DBG("exiting with %d", exit_code);
+ exit_code = disconnect(provider, 0);
- /* No task for no daemon VPN - use VPN died with no task. */
- vpn_died(NULL, exit_code, provider);
+ DBG("exited with %d", exit_code);
}
static int wg_error_code(struct vpn_provider *provider, int exit_code)
@@ -715,11 +775,12 @@ static int wg_save(struct vpn_provider *provider, GKeyFile *keyfile)
}
static struct vpn_driver vpn_driver = {
- .flags = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
+ .flags = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON |
+ VPN_FLAG_NO_AGENT,
.connect = wg_connect,
.disconnect = wg_disconnect,
.save = wg_save,
- .error_code = wg_error_code
+ .error_code = wg_error_code,
};
static int wg_init(void)
--
2.39.5