4 * Copyright (C) 2019 Daniel Wagner. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <arpa/inet.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/log.h>
39 #include <connman/task.h>
40 #include <connman/ipconfig.h>
41 #include <connman/inet.h>
42 #include <connman/dbus.h>
43 #include <connman/setting.h>
44 #include <connman/vpn-dbus.h>
46 #include "../vpn-provider.h"
50 #include "wireguard.h"
52 #define DNS_RERESOLVE_TIMEOUT 20
54 #define ADD_ALLOWED_IP_ROUTE_TIMEOUT 2
55 #endif /* TIZEN_EXT */
57 struct wireguard_info {
58 struct wg_device device;
64 int allowed_ip_route_id;
65 #endif /* TIZEN_EXT */
71 struct sockaddr_in sin;
72 struct sockaddr_in6 sin6;
76 static int parse_key(const char *str, wg_key key)
81 buf = g_base64_decode(str, &len);
94 static int parse_allowed_ips(const char *allowed_ips, wg_peer *peer)
96 struct wg_allowedip *curaip, *allowedip;
97 char buf[INET6_ADDRSTRLEN];
98 char **tokens, **toks;
103 #if defined TIZEN_EXT
105 * Note: At API level we donot check Wireguard specific configurations,
106 * User can provide any space separated string for IP.
107 * Ideally it should be using "10.0.0.2/32, 10.0.0.3/32"
108 * However there can be cases like "10.0.0.2/32,10.0.0.3/32"
110 * So we need to parse the allowed_ips configuration properly.
112 tokens = g_strsplit(allowed_ips, ",", -1);
114 tokens = g_strsplit(allowed_ips, ", ", -1);
116 for (i = 0; tokens[i]; i++) {
117 #if defined TIZEN_EXT
118 int len = strlen(tokens[i]);
119 char *ptr = tokens[i];
122 //skip forward spaces
123 while (++j < len && ptr[j] == ' ')
129 toks = g_strsplit(ptr + j, "/", -1);
130 if (g_strv_length(toks) != 2) {
131 DBG("Ignore AllowedIPs value [%s]", ptr + j);
136 DBG("Parsed AllowedIPs [%s]", ptr + j);
139 toks = g_strsplit(tokens[i], "/", -1);
140 if (g_strv_length(toks) != 2) {
141 DBG("Ignore AllowedIPs value %s", tokens[i]);
147 allowedip = g_malloc0(sizeof(*allowedip));
149 if (inet_pton(AF_INET, toks[0], buf) == 1) {
150 allowedip->family = AF_INET;
151 memcpy(&allowedip->ip4, buf, sizeof(allowedip->ip4));
152 } else if (inet_pton(AF_INET6, toks[0], buf) == 1) {
153 allowedip->family = AF_INET6;
154 memcpy(&allowedip->ip6, buf, sizeof(allowedip->ip6));
156 #if defined TIZEN_EXT
157 DBG("Ignore AllowedIPs value [%s]", ptr + j);
159 DBG("Ignore AllowedIPs value %s", tokens[i]);
166 allowedip->cidr = g_ascii_strtoull(toks[1], &send, 10);
169 peer->first_allowedip = allowedip;
171 curaip->next_allowedip = allowedip;
174 #if defined TIZEN_EXT
179 peer->last_allowedip = curaip;
185 static int parse_endpoint(const char *host, const char *port, struct sockaddr_u *addr)
187 struct addrinfo hints;
188 struct addrinfo *result, *rp;
191 memset(&hints, 0, sizeof(struct addrinfo));
192 hints.ai_family = AF_UNSPEC;
193 hints.ai_socktype = SOCK_DGRAM;
195 hints.ai_protocol = 0;
197 if (getaddrinfo(host, port, &hints, &result) < 0) {
198 DBG("Failed to resolve host address");
202 for (rp = result; rp; rp = rp->ai_next) {
203 sk = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
206 if (connect(sk, rp->ai_addr, rp->ai_addrlen) != -1) {
216 freeaddrinfo(result);
220 memcpy(addr, rp->ai_addr, rp->ai_addrlen);
221 freeaddrinfo(result);
226 static int parse_address(const char *address, const char *gateway,
227 struct connman_ipaddress **ipaddress)
229 char buf[INET6_ADDRSTRLEN];
230 unsigned char prefixlen;
235 tokens = g_strsplit(address, "/", -1);
236 if (g_strv_length(tokens) != 2) {
241 prefixlen = g_ascii_strtoull(tokens[1], &end, 10);
243 if (inet_pton(AF_INET, tokens[0], buf) == 1) {
244 netmask = g_strdup_printf("%d.%d.%d.%d",
245 ((0xffffffff << (32 - prefixlen)) >> 24) & 0xff,
246 ((0xffffffff << (32 - prefixlen)) >> 16) & 0xff,
247 ((0xffffffff << (32 - prefixlen)) >> 8) & 0xff,
248 ((0xffffffff << (32 - prefixlen)) >> 0) & 0xff);
250 *ipaddress = connman_ipaddress_alloc(AF_INET);
251 err = connman_ipaddress_set_ipv4(*ipaddress, tokens[0],
254 } else if (inet_pton(AF_INET6, tokens[0], buf) == 1) {
255 *ipaddress = connman_ipaddress_alloc(AF_INET6);
256 err = connman_ipaddress_set_ipv6(*ipaddress, tokens[0],
259 DBG("Invalid Wireguard.Address value");
263 connman_ipaddress_set_p2p(*ipaddress, true);
267 connman_ipaddress_free(*ipaddress);
277 static void ifname_check_cb(int index, void *user_data)
279 struct ifname_data *data = (struct ifname_data *)user_data;
282 ifname = connman_inet_ifname(index);
284 if (!g_strcmp0(ifname, data->ifname))
288 static char *get_ifname(void)
290 struct ifname_data data;
293 for (i = 0; i < 256; i++) {
294 data.ifname = g_strdup_printf("wg%d", i);
296 vpn_ipconfig_foreach(ifname_check_cb, &data);
307 static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
309 if (a->sa.sa_family != b->sa.sa_family)
312 if (a->sa.sa_family == AF_INET)
313 return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
314 else if (a->sa.sa_family == AF_INET6)
315 return !memcmp(a->sin6.sin6_addr.s6_addr,
316 b->sin6.sin6_addr.s6_addr,
317 sizeof(a->sin6.sin6_addr.s6_addr));
322 #if defined TIZEN_EXT
324 #include <sys/types.h>
325 #include <sys/wait.h>
328 #define MAX_SIZE_ERROR_BUFFER 256
329 #define MAX_CMD_SIZE 80
332 struct vpn_provider *provider;
333 struct wireguard_info *info;
334 } wg_allowed_ip_route_cb_data_s;
336 static int wg_execute_cmd(const char *format, ...)
340 char cmd[MAX_CMD_SIZE] = { 0, };
341 char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
346 va_start(va_ptr, format);
347 vsnprintf(cmd, (unsigned int) MAX_CMD_SIZE, format, va_ptr);
350 if (strlen(cmd) == 0)
353 DBG("command: %s", cmd);
355 args = g_strsplit_set(cmd, " ", -1);
358 if (!(pid = fork())) {
359 DBG("pid(%d), ppid (%d)", getpid(), getppid());
362 if (execv(args[0], args) == -1) {
363 DBG("Fail to execute command (%s)",
364 strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
368 } else if (pid > 0) {
369 if (waitpid(pid, &status, 0) == -1)
370 DBG("wait pid (%u) status (%d)", pid, status);
372 if (WIFEXITED(status)) {
373 rv = WEXITSTATUS(status);
374 DBG("exited, status=%d", rv);
376 } else if (WIFSIGNALED(status)) {
377 DBG("killed by signal %d", WTERMSIG(status));
379 } else if (WIFSTOPPED(status)) {
380 DBG("stopped by signal %d", WSTOPSIG(status));
382 } else if (WIFCONTINUED(status)) {
390 DBG("failed to fork(%s)", strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
396 static gboolean wg_add_allowed_ip_route_cb(gpointer user_data)
398 wg_allowed_ip_route_cb_data_s *cb_data = user_data;
399 const char *allowed_ips;
400 char **tokens = NULL;
406 allowed_ips = vpn_provider_get_string(cb_data->provider, "WireGuard.AllowedIPs");
408 DBG("WireGuard.AllowedIPs is missing");
412 tokens = g_strsplit(allowed_ips, ",", -1);
413 if (g_strv_length(tokens) == 0)
416 for (i = 0; tokens[i]; i++) {
417 int len = strlen(tokens[i]);
418 char *ptr = tokens[i];
421 //skip forward spaces
422 while (++j < len && ptr[j] == ' ')
428 toks = g_strsplit(ptr + j, "/", -1);
429 if (g_strv_length(toks) != 2) {
430 DBG("Ignore AllowedIPs value [%s]", ptr + j);
436 DBG("Allowed IP: [%s], Device Name: [%s]", ptr + j, cb_data->info->device.name);
438 // TODO: Remove system call to add route entry present in wg_execute_cmd()
439 if (!g_strcmp0("0.0.0.0/0", ptr + j)) {
440 // TODO: Update default route because user wants all data to be routed from wg0,
441 // when 0.0.0.0/0 is passed in allowed ips list.
442 // Should we replace the default route?
443 // If yes, then how to recover default route when wg0 tunnel is removed.
445 wg_execute_cmd("/usr/sbin/ip route add %s dev %s", ptr + j, cb_data->info->device.name);
457 #endif /* TIZEN_EXT */
459 static gboolean wg_dns_reresolve_cb(gpointer user_data)
461 struct wireguard_info *info = user_data;
462 struct sockaddr_u addr;
467 err = parse_endpoint(info->endpoint_fqdn,
472 if (sockaddr_cmp_addr(&addr,
473 (struct sockaddr_u *)&info->peer.endpoint.addr))
476 if (addr.sa.sa_family == AF_INET)
477 memcpy(&info->peer.endpoint.addr, &addr.sin,
478 sizeof(info->peer.endpoint.addr4));
480 memcpy(&info->peer.endpoint.addr, &addr.sin6,
481 sizeof(info->peer.endpoint.addr6));
483 DBG("Endpoint address has changed, udpate WireGuard device");
484 err = wg_set_device(&info->device);
486 DBG("Failed to update Endpoint address for WireGuard device %s",
492 static int wg_connect(struct vpn_provider *provider,
493 struct connman_task *task, const char *if_name,
494 vpn_provider_connect_cb_t cb,
495 const char *dbus_sender, void *user_data)
497 struct connman_ipaddress *ipaddress = NULL;
498 struct wireguard_info *info;
499 const char *option, *gateway;
503 info = g_malloc0(sizeof(struct wireguard_info));
504 info->peer.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS;
505 info->device.flags = WGDEVICE_HAS_PRIVATE_KEY;
506 info->device.first_peer = &info->peer;
507 info->device.last_peer = &info->peer;
509 vpn_provider_set_plugin_data(provider, info);
511 option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
514 info->device.listen_port = g_ascii_strtoull(option, &end, 10);
515 info->device.flags |= WGDEVICE_HAS_LISTEN_PORT;
518 option = vpn_provider_get_string(provider, "WireGuard.DNS");
520 err = vpn_provider_set_nameservers(provider, option);
525 option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
527 DBG("WireGuard.PrivateKey is missing");
530 err = parse_key(option, info->device.private_key);
534 option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
536 DBG("WireGuard.PublicKey is missing");
539 err = parse_key(option, info->peer.public_key);
543 option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
545 info->peer.flags |= WGPEER_HAS_PRESHARED_KEY;
546 err = parse_key(option, info->peer.preshared_key);
551 option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
553 DBG("WireGuard.AllowedIPs is missing");
556 err = parse_allowed_ips(option, &info->peer);
560 option = vpn_provider_get_string(provider,
561 "WireGuard.PersistentKeepalive");
564 info->peer.persistent_keepalive_interval =
565 g_ascii_strtoull(option, &end, 10);
566 info->peer.flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;
569 option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
573 gateway = vpn_provider_get_string(provider, "Host");
574 err = parse_endpoint(gateway, option,
575 (struct sockaddr_u *)&info->peer.endpoint.addr);
579 info->endpoint_fqdn = g_strdup(gateway);
580 info->port = g_strdup(option);
582 option = vpn_provider_get_string(provider, "WireGuard.Address");
584 DBG("Missing WireGuard.Address configuration");
587 err = parse_address(option, gateway, &ipaddress);
591 ifname = get_ifname();
593 DBG("Failed to find an usable device name");
597 stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
600 err = wg_add_device(info->device.name);
602 DBG("Failed to creating WireGuard device %s", info->device.name);
606 err = wg_set_device(&info->device);
608 DBG("Failed to configure WireGuard device %s", info->device.name);
609 wg_del_device(info->device.name);
612 vpn_set_ifname(provider, info->device.name);
614 vpn_provider_set_ipaddress(provider, ipaddress);
618 cb(provider, user_data, err);
620 connman_ipaddress_free(ipaddress);
622 #if defined TIZEN_EXT
624 const char *allowed_ips = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
626 wg_allowed_ip_route_cb_data_s *cb_data =
627 (wg_allowed_ip_route_cb_data_s *) calloc(1, sizeof(wg_allowed_ip_route_cb_data_s));
629 cb_data->provider = provider;
630 cb_data->info = info;
632 info->allowed_ip_route_id =
633 g_timeout_add_seconds(ADD_ALLOWED_IP_ROUTE_TIMEOUT,
634 wg_add_allowed_ip_route_cb, cb_data);
638 #endif /* TIZEN_EXT */
642 g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
643 wg_dns_reresolve_cb, info);
648 static void wg_disconnect(struct vpn_provider *provider)
650 struct wireguard_info *info;
652 info = vpn_provider_get_plugin_data(provider);
656 #if defined TIZEN_EXT
657 if (info->allowed_ip_route_id > 0)
658 g_source_remove(info->allowed_ip_route_id);
659 #endif /* TIZEN_EXT */
661 if (info->reresolve_id > 0)
662 g_source_remove(info->reresolve_id);
664 vpn_provider_set_plugin_data(provider, NULL);
666 wg_del_device(info->device.name);
668 g_free(info->endpoint_fqdn);
673 #if defined TIZEN_EXT
674 static int wg_save(struct vpn_provider *provider, GKeyFile *keyfile)
681 * The client/own device listen port.
683 option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
685 g_key_file_set_string(keyfile,
686 vpn_provider_get_save_group(provider),
687 "WireGuard.ListenPort",
691 * comma separated DNS.
693 option = vpn_provider_get_string(provider, "WireGuard.DNS");
695 g_key_file_set_string(keyfile,
696 vpn_provider_get_save_group(provider),
701 * The client private key.
703 option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
705 g_key_file_set_string(keyfile,
706 vpn_provider_get_save_group(provider),
707 "WireGuard.PrivateKey",
711 * The server public key.
713 option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
715 g_key_file_set_string(keyfile,
716 vpn_provider_get_save_group(provider),
717 "WireGuard.PublicKey",
723 option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
725 g_key_file_set_string(keyfile,
726 vpn_provider_get_save_group(provider),
727 "WireGuard.PresharedKey",
731 * Subnets accessed via VPN tunnel, 0.0.0.0/0 routes all traffic.
733 option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
735 g_key_file_set_string(keyfile,
736 vpn_provider_get_save_group(provider),
737 "WireGuard.AllowedIPs",
741 * The time in seconds to emit periodic keep alive message.
743 option = vpn_provider_get_string(provider, "WireGuard.PersistentKeepalive");
745 g_key_file_set_string(keyfile,
746 vpn_provider_get_save_group(provider),
747 "WireGuard.PersistentKeepalive",
751 * The server listen port, default: 51820
753 option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
755 g_key_file_set_string(keyfile,
756 vpn_provider_get_save_group(provider),
757 "WireGuard.EndpointPort",
761 * Save Address: The internal IP of the client node
763 option = vpn_provider_get_string(provider, "WireGuard.Address");
765 g_key_file_set_string(keyfile,
766 vpn_provider_get_save_group(provider),
774 static struct vpn_driver vpn_driver = {
775 .flags = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
776 .connect = wg_connect,
777 .disconnect = wg_disconnect,
778 #if defined TIZEN_EXT
783 static int wg_init(void)
785 return vpn_register("wireguard", &vpn_driver, NULL);
788 static void wg_exit(void)
790 vpn_unregister("wireguard");
793 CONNMAN_PLUGIN_DEFINE(wireguard, "WireGuard VPN plugin", VERSION,
794 CONNMAN_PLUGIN_PRIORITY_DEFAULT, wg_init, wg_exit)