From cce6ed197cf6503e5ff068c2a25ad8f435b12068 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Sun, 22 Mar 2020 20:50:52 +0100 Subject: [PATCH] wireguard: Regular reresolve endpoint address 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 --- vpn/plugins/wireguard.c | 87 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/vpn/plugins/wireguard.c b/vpn/plugins/wireguard.c index de2dbda..536adbf 100644 --- a/vpn/plugins/wireguard.c +++ b/vpn/plugins/wireguard.c @@ -49,6 +49,16 @@ #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); } -- 2.7.4