#include "vpn.h"
#include "wireguard.h"
+#define DNS_RERESOLVE_TIMEOUT 20
+#if defined TIZEN_EXT
+#define ADD_ALLOWED_IP_ROUTE_TIMEOUT 2
+#endif /* TIZEN_EXT */
+
+struct wireguard_info {
+ struct wg_device device;
+ struct wg_peer peer;
+ char *endpoint_fqdn;
+ char *port;
+ int reresolve_id;
+#if defined TIZEN_EXT
+ int allowed_ip_route_id;
+#endif /* TIZEN_EXT */
+};
+
+struct sockaddr_u {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ };
+};
+
static int parse_key(const char *str, wg_key key)
{
unsigned char *buf;
int i;
curaip = NULL;
+#if defined TIZEN_EXT
+ /**
+ * Note: At API level we donot check Wireguard specific configurations,
+ * User can provide any space separated string for IP.
+ * Ideally it should be using "10.0.0.2/32, 10.0.0.3/32"
+ * However there can be cases like "10.0.0.2/32,10.0.0.3/32"
+ *
+ * So we need to parse the allowed_ips configuration properly.
+ */
+ tokens = g_strsplit(allowed_ips, ",", -1);
+#else
tokens = g_strsplit(allowed_ips, ", ", -1);
+#endif
for (i = 0; tokens[i]; i++) {
+#if defined TIZEN_EXT
+ int len = strlen(tokens[i]);
+ char *ptr = tokens[i];
+ int j = -1;
+
+ //skip forward spaces
+ while (++j < len && ptr[j] == ' ')
+ ;// Do Nothing
+
+ if (!ptr[j])
+ continue;
+
+ toks = g_strsplit(ptr + j, "/", -1);
+ if (g_strv_length(toks) != 2) {
+ DBG("Ignore AllowedIPs value [%s]", ptr + j);
+ g_strfreev(toks);
+ continue;
+ }
+
+ DBG("Parsed AllowedIPs [%s]", ptr + j);
+#else
+
toks = g_strsplit(tokens[i], "/", -1);
if (g_strv_length(toks) != 2) {
DBG("Ignore AllowedIPs value %s", tokens[i]);
g_strfreev(toks);
continue;
}
+#endif
allowedip = g_malloc0(sizeof(*allowedip));
allowedip->family = AF_INET6;
memcpy(&allowedip->ip6, buf, sizeof(allowedip->ip6));
} else {
+#if defined TIZEN_EXT
+ DBG("Ignore AllowedIPs value [%s]", ptr + j);
+#else
DBG("Ignore AllowedIPs value %s", tokens[i]);
+#endif
g_free(allowedip);
g_strfreev(toks);
continue;
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_u *addr)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
return -EINVAL;
}
- memcpy(&peer->endpoint.addr, rp->ai_addr, rp->ai_addrlen);
+ memcpy(addr, rp->ai_addr, rp->ai_addrlen);
freeaddrinfo(result);
return 0;
for (i = 0; i < 256; i++) {
data.ifname = g_strdup_printf("wg%d", i);
data.found = false;
- __vpn_ipconfig_foreach(ifname_check_cb, &data);
+ vpn_ipconfig_foreach(ifname_check_cb, &data);
if (!data.found)
return data.ifname;
return NULL;
}
-struct wireguard_info {
- struct wg_device device;
- struct wg_peer peer;
-};
+static bool sockaddr_cmp_addr(struct sockaddr_u *a, struct sockaddr_u *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family)
+ return false;
+
+ if (a->sa.sa_family == AF_INET)
+ return !memcmp(&a->sin, &b->sin, sizeof(struct sockaddr_in));
+ else if (a->sa.sa_family == AF_INET6)
+ return !memcmp(a->sin6.sin6_addr.s6_addr,
+ b->sin6.sin6_addr.s6_addr,
+ sizeof(a->sin6.sin6_addr.s6_addr));
+
+ return false;
+}
+
+#if defined TIZEN_EXT
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+#define MAX_SIZE_ERROR_BUFFER 256
+#define MAX_CMD_SIZE 80
+
+typedef struct {
+ struct vpn_provider *provider;
+ struct wireguard_info *info;
+} wg_allowed_ip_route_cb_data_s;
+
+static int wg_execute_cmd(const char *format, ...)
+{
+ int status = 0;
+ int rv = 0;
+ char cmd[MAX_CMD_SIZE] = { 0, };
+ char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
+ pid_t pid = 0;
+ va_list va_ptr;
+ gchar **args = NULL;
+
+ va_start(va_ptr, format);
+ vsnprintf(cmd, (unsigned int) MAX_CMD_SIZE, format, va_ptr);
+ va_end(va_ptr);
+
+ if (strlen(cmd) == 0)
+ return -EIO;
+
+ DBG("command: %s", cmd);
+
+ args = g_strsplit_set(cmd, " ", -1);
+
+ errno = 0;
+ if (!(pid = fork())) {
+ DBG("pid(%d), ppid (%d)", getpid(), getppid());
+
+ errno = 0;
+ if (execv(args[0], args) == -1) {
+ DBG("Fail to execute command (%s)",
+ strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
+ g_strfreev(args);
+ exit(1);
+ }
+ } else if (pid > 0) {
+ if (waitpid(pid, &status, 0) == -1)
+ DBG("wait pid (%u) status (%d)", pid, status);
+
+ if (WIFEXITED(status)) {
+ rv = WEXITSTATUS(status);
+ DBG("exited, status=%d", rv);
+
+ } else if (WIFSIGNALED(status)) {
+ DBG("killed by signal %d", WTERMSIG(status));
+
+ } else if (WIFSTOPPED(status)) {
+ DBG("stopped by signal %d", WSTOPSIG(status));
+
+ } else if (WIFCONTINUED(status)) {
+ DBG("continued");
+ }
+
+ g_strfreev(args);
+ return rv;
+ }
+
+ DBG("failed to fork(%s)", strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER));
+ g_strfreev(args);
+
+ return -EIO;
+}
+
+static gboolean wg_add_allowed_ip_route_cb(gpointer user_data)
+{
+ wg_allowed_ip_route_cb_data_s *cb_data = user_data;
+ const char *allowed_ips;
+ char **tokens = NULL;
+ char **toks = NULL;
+ int i;
+
+ DBG("");
+
+ allowed_ips = vpn_provider_get_string(cb_data->provider, "WireGuard.AllowedIPs");
+ if (!allowed_ips) {
+ DBG("WireGuard.AllowedIPs is missing");
+ goto done;
+ }
+
+ tokens = g_strsplit(allowed_ips, ",", -1);
+ if (g_strv_length(tokens) == 0)
+ goto done;
+
+ for (i = 0; tokens[i]; i++) {
+ int len = strlen(tokens[i]);
+ char *ptr = tokens[i];
+ int j = -1;
+
+ //skip forward spaces
+ while (++j < len && ptr[j] == ' ')
+ ;// Do Nothing
+
+ if (!ptr[j])
+ continue;
+
+ toks = g_strsplit(ptr + j, "/", -1);
+ if (g_strv_length(toks) != 2) {
+ DBG("Ignore AllowedIPs value [%s]", ptr + j);
+ g_strfreev(toks);
+ continue;
+ }
+ g_strfreev(toks);
+
+ DBG("Allowed IP: [%s], Device Name: [%s]", ptr + j, cb_data->info->device.name);
+
+ // TODO: Remove system call to add route entry present in wg_execute_cmd()
+ if (!g_strcmp0("0.0.0.0/0", ptr + j)) {
+ // TODO: Update default route because user wants all data to be routed from wg0,
+ // when 0.0.0.0/0 is passed in allowed ips list.
+ // Should we replace the default route?
+ // If yes, then how to recover default route when wg0 tunnel is removed.
+ } else {
+ wg_execute_cmd("/usr/sbin/ip route add %s dev %s", ptr + j, cb_data->info->device.name);
+ }
+ }
+
+done:
+ if (tokens)
+ g_strfreev(tokens);
+
+ free(cb_data);
+
+ return FALSE;
+}
+#endif /* TIZEN_EXT */
+
+static gboolean wg_dns_reresolve_cb(gpointer user_data)
+{
+ struct wireguard_info *info = user_data;
+ struct sockaddr_u addr;
+ int err;
+
+ DBG("");
+
+ err = parse_endpoint(info->endpoint_fqdn,
+ info->port, &addr);
+ if (err)
+ return TRUE;
+
+ if (sockaddr_cmp_addr(&addr,
+ (struct sockaddr_u *)&info->peer.endpoint.addr))
+ return TRUE;
+
+ if (addr.sa.sa_family == AF_INET)
+ memcpy(&info->peer.endpoint.addr, &addr.sin,
+ sizeof(info->peer.endpoint.addr4));
+ else
+ memcpy(&info->peer.endpoint.addr, &addr.sin6,
+ 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,
option = "51820";
gateway = vpn_provider_get_string(provider, "Host");
- err = parse_endpoint(gateway, option, &info->peer);
+ err = parse_endpoint(gateway, option,
+ (struct sockaddr_u *)&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");
err = -ENOENT;
goto done;
}
- stpncpy(info->device.name, ifname, sizeof(info->device.name));
+ stpncpy(info->device.name, ifname, sizeof(info->device.name) - 1);
g_free(ifname);
err = wg_add_device(info->device.name);
connman_ipaddress_free(ipaddress);
+#if defined TIZEN_EXT
+ if (!err) {
+ const char *allowed_ips = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
+ if (allowed_ips) {
+ wg_allowed_ip_route_cb_data_s *cb_data =
+ (wg_allowed_ip_route_cb_data_s *) calloc(1, sizeof(wg_allowed_ip_route_cb_data_s));
+ if (cb_data) {
+ cb_data->provider = provider;
+ cb_data->info = info;
+
+ info->allowed_ip_route_id =
+ g_timeout_add_seconds(ADD_ALLOWED_IP_ROUTE_TIMEOUT,
+ wg_add_allowed_ip_route_cb, cb_data);
+ }
+ }
+ }
+#endif /* TIZEN_EXT */
+
+ if (!err)
+ info->reresolve_id =
+ g_timeout_add_seconds(DNS_RERESOLVE_TIMEOUT,
+ wg_dns_reresolve_cb, info);
+
return err;
}
info = vpn_provider_get_plugin_data(provider);
if (!info)
return;
+
+#if defined TIZEN_EXT
+ if (info->allowed_ip_route_id > 0)
+ g_source_remove(info->allowed_ip_route_id);
+#endif /* TIZEN_EXT */
+
+ 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);
}
+#if defined TIZEN_EXT
+static int wg_save(struct vpn_provider *provider, GKeyFile *keyfile)
+{
+ const char *option;
+
+ DBG("");
+
+ /*
+ * The client/own device listen port.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.ListenPort");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.ListenPort",
+ option);
+
+ /*
+ * comma separated DNS.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.DNS");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.DNS",
+ option);
+
+ /*
+ * The client private key.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.PrivateKey");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.PrivateKey",
+ option);
+
+ /*
+ * The server public key.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.PublicKey");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.PublicKey",
+ option);
+
+ /*
+ * The preshared key.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.PresharedKey");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.PresharedKey",
+ option);
+
+ /*
+ * Subnets accessed via VPN tunnel, 0.0.0.0/0 routes all traffic.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.AllowedIPs");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.AllowedIPs",
+ option);
+
+ /*
+ * The time in seconds to emit periodic keep alive message.
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.PersistentKeepalive");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.PersistentKeepalive",
+ option);
+
+ /*
+ * The server listen port, default: 51820
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.EndpointPort");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.EndpointPort",
+ option);
+
+ /*
+ * Save Address: The internal IP of the client node
+ */
+ option = vpn_provider_get_string(provider, "WireGuard.Address");
+ if (option)
+ g_key_file_set_string(keyfile,
+ vpn_provider_get_save_group(provider),
+ "WireGuard.Address",
+ option);
+
+ return 0;
+}
+#endif
+
static struct vpn_driver vpn_driver = {
.flags = VPN_FLAG_NO_TUN | VPN_FLAG_NO_DAEMON,
.connect = wg_connect,
.disconnect = wg_disconnect,
+#if defined TIZEN_EXT
+ .save = wg_save,
+#endif
};
static int wg_init(void)