summaryrefslogtreecommitdiff
path: root/proto/wireguard
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2019-04-02 00:00:06 +0200
committerMikael Magnusson <mikma@users.sourceforge.net>2020-05-08 18:52:55 +0200
commit28e5a4486f9f2c2a68af0e6f150065e6067b4dfd (patch)
treeaee2a25bf2e082f046bd22809c0b16a6372f8f6c /proto/wireguard
parent46686164f71d1ea7a906eb551334c4c58d7fcf6e (diff)
Wireguard: Add user space support
Diffstat (limited to 'proto/wireguard')
-rw-r--r--proto/wireguard/wireguard.c265
1 files changed, 262 insertions, 3 deletions
diff --git a/proto/wireguard/wireguard.c b/proto/wireguard/wireguard.c
index 970a383f..261c0e6e 100644
--- a/proto/wireguard/wireguard.c
+++ b/proto/wireguard/wireguard.c
@@ -2,12 +2,259 @@
#include <stdio.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include "lib/lists.h"
+#include "lib/socket.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "sysdep/linux/wireguard.h"
+#include "sysdep/unix/unix.h"
#include "wireguard.h"
#include "proto/bgp/bgp.h"
+#define SOCKET_PATH PATH_RUNSTATEDIR "/wireguard/"
+
+static
+char *get_socket_path(struct wg_proto *p, char *buf, uint size)
+{
+ struct wg_config *c = (struct wg_config *) p->p.cf;
+ bsnprintf(buf, size, SOCKET_PATH "%s.sock", c->ifname);
+ return buf;
+}
+
+static
+bool has_userspace(struct wg_proto *p)
+{
+ struct wg_config *c = (struct wg_config *) p->p.cf;
+ struct stat sb;
+ char tmp[sizeof(struct sockaddr_un)];
+
+ if (stat(get_socket_path(p, tmp, sizeof(tmp)), &sb) == 0)
+ return (sb.st_mode & S_IFMT) == S_IFSOCK;
+ else
+ {
+ log(L_TRACE "WG: no socket %s", tmp);
+ return false;
+ }
+}
+
+/* NULL=receiving turned off, returns 1 to clear rx buffer */
+static
+int user_rx_hook(struct birdsock *sk, uint size)
+{
+ log(L_TRACE "WG: RX %d", size);
+ return 1;
+}
+
+static
+void user_tx_hook(struct birdsock *bs)
+{
+ log(L_TRACE "WG: TX");
+}
+
+/* errno or zero if EOF */
+static
+void user_err_hook(struct birdsock *bs, int err)
+{
+ /* if (err == 0) */
+ /* return; */
+
+ log(L_TRACE "WG: ERR %d %s", err, bs->err);
+ if (bs->fd >= 0)
+ close(bs->fd);
+ bs->fd = -1;
+}
+
+static void
+wg_puts(const char *str, byte **buf, uint *size)
+{
+ int len = strlen(str);
+ if (0 < len && len < (int)*size)
+ {
+ strcpy(*buf, str);
+ *size -= len;
+ *buf += len;
+ }
+ else
+ *size = 0;
+}
+
+static void
+wg_put_str(const char *key, const char *value, byte **buf, uint *size)
+{
+ char tmp[128];
+
+ int len = snprintf(tmp, sizeof(tmp), "%s=%s\n", key, value);
+ if (0 < len && len < (int)*size)
+ {
+ strcpy(*buf, tmp);
+ *size -= len;
+ *buf += len;
+ }
+ else
+ *size = 0;
+}
+
+static void
+wg_put_u16(const char *key, u16 value, byte **buf, uint *size)
+{
+ char tmp[64];
+
+ int len = snprintf(tmp, sizeof(tmp), "%u", value);
+ if (len > 0)
+ wg_put_str(key, tmp, buf, size);
+ else
+ *size = 0;
+}
+
+static void
+wg_put_bool(const char *key, bool value, byte **buf, uint *size)
+{
+ char tmp[64];
+
+ if (value)
+ wg_put_str(key, "true", buf, size);
+}
+
+static void
+wg_put_key(const char *key, wg_key value, byte **buf, uint *size)
+{
+ char tmp[128];
+
+ for (uint i=0; i < sizeof(wg_key); i++)
+ bsnprintf(tmp+2*i, sizeof(tmp)-2*i, "%02x", value[i]);
+
+ wg_put_str(key, tmp, buf, size);
+}
+
+static void
+wg_put_endpoint(const char *key, const wg_endpoint *endpoint, byte **buf, uint *size)
+{
+ char tmp[INET6_ADDRSTRLEN + 16];
+ ip_addr ip;
+ struct iface *ifa = NULL;
+ uint port = 0;
+
+ if (sockaddr_read((sockaddr*)&endpoint->addr, endpoint->addr.sa_family,
+ &ip, &ifa, &port) == 0)
+ {
+ char *pos = NULL;
+
+ if (ipa_is_ip4(ip))
+ pos = ip4_ntop(ipa_to_ip4(ip), tmp);
+ else
+ {
+ tmp[0] = '[';
+ pos = ip6_ntop(ipa_to_ip6(ip), tmp + 1);
+ if (ifa)
+ pos += bsprintf(pos, "%%%u", ifa->index);
+ *pos++ = ']';
+ }
+ bsprintf(pos, ":%u", port);
+ wg_put_str("endpoint", tmp, buf, size);
+ }
+}
+
+static void
+wg_put_allowedip(wg_allowedip *allowedip, byte **buf, uint *size)
+{
+ char tmp[INET6_ADDRSTRLEN + 10];
+ ip_addr ip = IP6_NONE;
+
+ switch (allowedip->family)
+ {
+ case AF_INET:
+ ip = ipa_from_in4(allowedip->ip4);
+ break;
+ case AF_INET6:
+ ip = ipa_from_in6(allowedip->ip6);
+ break;
+ default:
+ return;
+ }
+
+ int res = bsnprintf(tmp, sizeof(tmp), "%I/%u", ip, allowedip->cidr);
+
+ if (res < 0)
+ {
+ *size = 0;
+ return;
+ }
+
+ wg_put_str("allowed_ip", tmp, buf, size);
+}
+
+static int
+user_put_device(wg_device *dev, byte **buf, uint *size)
+{
+ wg_put_u16("set", 1, buf, size);
+ wg_put_key("private_key", dev->private_key, buf, size);
+ wg_put_u16("listen_port", dev->listen_port, buf, size);
+ wg_put_bool("replace_peers", dev->flags & WGDEVICE_REPLACE_PEERS, buf, size);
+
+ wg_peer *peer = NULL;
+ wg_for_each_peer(dev, peer)
+ {
+ wg_put_key("public_key", peer->public_key, buf, size);
+ wg_put_endpoint("endpoint", &peer->endpoint, buf, size);
+ wg_put_bool("replace_allowed_ips", peer->flags & WGPEER_REPLACE_ALLOWEDIPS, buf, size);
+
+ wg_allowedip *allowedip = NULL;
+ wg_for_each_allowedip(peer, allowedip)
+ {
+ wg_put_allowedip(allowedip, buf, size);
+ }
+ }
+ wg_puts("\n", buf, size);
+
+ if (size > 0)
+ return 0;
+ else
+ return -1;
+}
+
+static int
+user_set_device(struct wg_proto *p)
+{
+ struct wg_config *c = (struct wg_config *) p->p.cf;
+ char path[sizeof(struct sockaddr_un)];
+
+ bsnprintf(path, sizeof(path), SOCKET_PATH "%s.sock", c->ifname);
+
+ struct birdsock *sock = sk_new(p->p.pool);
+ sock->rx_hook = user_rx_hook;
+ sock->tx_hook = user_tx_hook;
+ sock->err_hook = user_err_hook;
+ int res = sk_connect_unix(sock, path);
+ log(L_TRACE "WG: socket %d %d %s", res, sock->fd, path);
+ if (res < 0)
+ return -1;
+
+ uint tbsize = 8192;
+ sk_set_tbsize(sock, tbsize);
+ sk_set_rbsize(sock, 16);
+ byte *pos = sock->tbuf;
+ uint size = tbsize;
+ int len = user_put_device(p->dev, &pos, &size);
+
+ log(L_TRACE "WG: put %d %s", size, sock->tbuf);
+
+ if (len < 0)
+ {
+ /* FIXME close */
+ return -1;
+ }
+ res = sk_send(sock, tbsize - size);
+ /* Send data, <0=err, >0=ok, 0=sleep */
+ log(L_TRACE "WG: send %d", res);
+
+ /* abort(); */
+ return -1;
+}
+
static
int get_device(struct wg_proto *p, wg_device **pdev, const char *device_name)
{
@@ -79,6 +326,18 @@ int get_device(struct wg_proto *p, wg_device **pdev, const char *device_name)
return 0;
}
+static int
+set_device(struct wg_proto *p)
+{
+ if (has_userspace(p))
+ return user_set_device(p);
+ else
+ {
+ log(L_TRACE "WG: wg_set_device");
+ return wg_set_device(p->dev);
+ }
+}
+
static void
wg_init_entry(void *e_)
{
@@ -455,7 +714,7 @@ wg_rt_notify(struct proto *P, struct channel *CH, struct network *n,
}
if (dirty) {
- int res = wg_set_device(dev);
+ int res = set_device(p);
log(L_TRACE "WG: wg_set_device %d", res);
}
}
@@ -597,7 +856,7 @@ wg_rt_notify(struct proto *P, struct channel *CH, struct network *n,
}
peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;
- int res = wg_set_device(dev);
+ int res = set_device(p);
log(L_TRACE "WG: wg_set_device %d", res);
}
}
@@ -664,7 +923,7 @@ wg_start(struct proto *P)
if (get_device(p, &p->dev, cf->ifname) >= 0)
{
- int res = wg_set_device(p->dev);
+ int res = set_device(p);
log(L_TRACE "WG: wg_set_device %d", res);
}