diff options
author | Mikael Magnusson <mikma@users.sourceforge.net> | 2019-04-02 00:00:06 +0200 |
---|---|---|
committer | Mikael Magnusson <mikma@users.sourceforge.net> | 2021-01-12 23:34:17 +0100 |
commit | 58aa48eb9cf45999e4d1205d6009d93a6d2b4295 (patch) | |
tree | 89a7b5e49b976109df183214fc151e24641d71f3 | |
parent | f0d7e5f600985d37c18b9d2a00498c151174eeaf (diff) |
Wireguard: Add user space support
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | proto/wireguard/wireguard.c | 265 |
2 files changed, 263 insertions, 3 deletions
diff --git a/Makefile.in b/Makefile.in index e0ff4a1d..f3b47053 100644 --- a/Makefile.in +++ b/Makefile.in @@ -163,6 +163,7 @@ $(objdir)/sysdep/paths.h: Makefile echo >$@ "/* Generated by Makefile, don't edit manually! */" echo >>$@ "#define PATH_CONFIG_FILE \"@CONFIG_FILE@\"" echo >>$@ "#define PATH_CONTROL_SOCKET \"@CONTROL_SOCKET@\"" + echo >>$@ "#define PATH_RUNSTATEDIR \"$(runstatedir)\"" if test -n "@iproutedir@" ; then echo >>$@ "#define PATH_IPROUTE_DIR \"@iproutedir@\"" ; fi # Unit tests rules 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); } |