wireguard: Regular reresolve endpoint address 88/261188/1
authorDaniel Wagner <wagi@monom.org>
Sun, 22 Mar 2020 19:50:52 +0000 (20:50 +0100)
committerNishant Chaprana <n.chaprana@samsung.com>
Tue, 13 Jul 2021 06:20:17 +0000 (11:50 +0530)
In case the WireGuard endpoint is hosted on a dynamic IP address, the
endpoint might change during runtime. Reresolve the endpoint on a
regular basis and update the WireGuard device when it IP address has
changed.

Reported by Christian Hewitt

Change-Id: Ice3b554a064585fb03ae8a7a89baccbe53e8576b
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
vpn/plugins/wireguard.c

index de2dbda..536adbf 100644 (file)
 #include "vpn.h"
 #include "wireguard.h"
 
+#define DNS_RERESOLVE_TIMEOUT 20
+
+struct wireguard_info {
+       struct wg_device device;
+       struct wg_peer peer;
+       char *endpoint_fqdn;
+       char *port;
+       int reresolve_id;
+};
+
 static int parse_key(const char *str, wg_key key)
 {
        unsigned char *buf;
@@ -116,7 +126,7 @@ static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
        return 0;
 }
 
-static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
+static int parse_endpoint(const char *host, const char *port, struct sockaddr *addr)
 {
        struct addrinfo hints;
        struct addrinfo *result, *rp;
@@ -151,7 +161,7 @@ static int parse_endpoint(const char *host, const char *port, wg_peer *peer)
                return -EINVAL;
        }
 
-       memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+       memcpy(addr, rp->ai_addr, rp->ai_addrlen);
        freeaddrinfo(result);
 
        return 0;
@@ -236,10 +246,59 @@ static char *get_ifname(void)
        return NULL;
 }
 
-struct wireguard_info {
-       struct wg_device device;
-       struct wg_peer peer;
-};
+static bool sockaddr_cmp_addr(struct sockaddr *a, struct sockaddr *b)
+{
+       if (a->sa_family != b->sa_family)
+               return false;
+
+       if (a->sa_family == AF_INET) {
+               struct sockaddr_in *a4 = (struct sockaddr_in *)a;
+               struct sockaddr_in *b4 = (struct sockaddr_in *)b;
+
+               return !memcmp(a4, b4, sizeof(struct sockaddr_in));
+       } else if (a->sa_family == AF_INET6) {
+               struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)a;
+               struct sockaddr_in6 *b6 = (struct sockaddr_in6 *)b;
+
+               return !memcmp(a6->sin6_addr.s6_addr,
+                               b6->sin6_addr.s6_addr,
+                               sizeof(a6->sin6_addr.s6_addr));
+       }
+
+       return false;
+}
+
+static gboolean wg_dns_reresolve_cb(gpointer user_data)
+{
+       struct wireguard_info *info = user_data;
+       int err;
+       struct sockaddr addr;
+
+       DBG("");
+
+       err = parse_endpoint(info->endpoint_fqdn,
+                       info->port, &addr);
+       if (err)
+               return TRUE;
+
+       if (sockaddr_cmp_addr(&addr, &info->peer.endpoint.addr))
+               return TRUE;
+
+       if (addr.sa_family == AF_INET)
+               memcpy(&info->peer.endpoint.addr, &addr,
+                       sizeof(info->peer.endpoint.addr4));
+       else
+               memcpy(&info->peer.endpoint.addr, &addr,
+                       sizeof(info->peer.endpoint.addr6));
+
+       DBG("Endpoint address has changed, udpate WireGuard device");
+       err = wg_set_device(&info->device);
+       if (err)
+               DBG("Failed to update Endpoint address for WireGuard device %s",
+                       info->device.name);
+
+       return TRUE;
+}
 
 static int wg_connect(struct vpn_provider *provider,
                        struct connman_task *task, const char *if_name,
@@ -323,10 +382,13 @@ static int wg_connect(struct vpn_provider *provider,
                option = "51820";
 
        gateway = vpn_provider_get_string(provider, "Host");
-       err = parse_endpoint(gateway, option, &info->peer);
+       err = parse_endpoint(gateway, option, &info->peer.endpoint.addr);
        if (err)
                goto done;
 
+       info->endpoint_fqdn = g_strdup(gateway);
+       info->port = g_strdup(option);
+
        option = vpn_provider_get_string(provider, "WireGuard.Address");
        if (!option) {
                DBG("Missing WireGuard.Address configuration");
@@ -367,6 +429,11 @@ done:
 
        connman_ipaddress_free(ipaddress);
 
+       if (!err)
+               info->reresolve_id =
+                       g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
+                                               wg_dns_reresolve_cb, info);
+
        return err;
 }
 
@@ -377,10 +444,16 @@ static void wg_disconnect(struct vpn_provider *provider)
        info = vpn_provider_get_plugin_data(provider);
        if (!info)
                return;
+
+       if (info->reresolve_id > 0)
+               g_source_remove(info->reresolve_id);
+
        vpn_provider_set_plugin_data(provider, NULL);
 
        wg_del_device(info->device.name);
 
+       g_free(info->endpoint_fqdn);
+       g_free(info->port);
        g_free(info);
 }