summaryrefslogtreecommitdiff
path: root/sysdep/unix
diff options
context:
space:
mode:
Diffstat (limited to 'sysdep/unix')
-rw-r--r--sysdep/unix/Makefile2
-rw-r--r--sysdep/unix/io.c77
-rw-r--r--sysdep/unix/main.c5
-rw-r--r--sysdep/unix/unix.h1
-rw-r--r--sysdep/unix/wg_user.c298
-rw-r--r--sysdep/unix/wg_user.h16
6 files changed, 398 insertions, 1 deletions
diff --git a/sysdep/unix/Makefile b/sysdep/unix/Makefile
index d0d36b5f..ef422342 100644
--- a/sysdep/unix/Makefile
+++ b/sysdep/unix/Makefile
@@ -1,4 +1,4 @@
-src := alloc.c io.c krt.c log.c main.c random.c
+src := alloc.c io.c krt.c log.c main.c random.c wg_user.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index 6aedcfb6..a3c4a29f 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -810,6 +810,8 @@ sk_free(resource *r)
{
sock *s = (sock *) r;
+ log(L_TRACE "sk_free %d %d", s->fd, s->type);
+
sk_free_bufs(s);
#ifdef HAVE_LIBSSH
@@ -1063,6 +1065,14 @@ sk_tcp_connected(sock *s)
s->tx_hook(s);
}
+static void
+sk_unix_connected(sock *s)
+{
+ sk_alloc_bufs(s);
+ s->type = SK_UNIX;
+ s->tx_hook(s);
+}
+
#ifdef HAVE_LIBSSH
static void
sk_ssh_connected(sock *s)
@@ -1529,6 +1539,64 @@ sk_open_unix(sock *s, char *name)
return 0;
}
+static void hexdump(const char *data, socklen_t size)
+{
+ char buf[1024]="";
+
+ for (unsigned int i = 0; i < size; i++)
+ {
+ sprintf(buf + i*3, "%02x ", data[i]);
+ }
+ log(L_TRACE "sk_connect_unix: %s", buf);
+}
+
+int
+sk_connect_unix(sock *s, char *name, socklen_t namelen)
+{
+ struct sockaddr_un sa;
+ int fd;
+
+ if (namelen > sizeof(sa.sun_path))
+ return -1;
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -2;
+
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ close(fd);
+ return -3;
+ }
+
+ /* Path length checked in test_old_bird() */
+ memset(&sa, 0, sizeof(sa));
+ sa.sun_family = AF_UNIX;
+ memcpy(sa.sun_path, name, namelen);
+
+ //hexdump((const char*)&sa, sizeof(sa.sun_family) + namelen);
+
+ s->fd = fd;
+ s->type = SK_UNIX_ACTIVE;
+ s->ttx = ""; /* Force s->ttx != s->tpos */
+
+ if (connect(fd, (struct sockaddr *) &sa, sizeof(sa.sun_family) + namelen) >= 0)
+ sk_unix_connected(s);
+ else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS &&
+ errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH)
+ {
+ ERR2("connect");
+ }
+
+ sk_insert(s);
+ return 0;
+
+ err:
+ close(fd);
+ s->fd = -1;
+ return -1;
+}
+
#define CMSG_RX_SPACE MAX(CMSG4_SPACE_PKTINFO+CMSG4_SPACE_TTL, \
CMSG6_SPACE_PKTINFO+CMSG6_SPACE_TTL)
@@ -1894,7 +1962,10 @@ sk_read_noflush(sock *s, int revents)
}
}
else if (!c)
+ {
+ if (s->type == SK_UNIX) log(L_TRACE "Unix socket nothing to read");
s->err_hook(s, 0);
+ }
else
{
s->rpos += c;
@@ -1955,6 +2026,12 @@ sk_write_noflush(sock *s)
return 0;
}
+ case SK_UNIX_ACTIVE:
+ {
+ sk_unix_connected(s);
+ return 0;
+ }
+
#ifdef HAVE_LIBSSH
case SK_SSH_ACTIVE:
{
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 0d7788bb..1df811e7 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -30,6 +30,7 @@
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h"
+#include "lib/tunnel_encaps.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@@ -605,6 +606,7 @@ unlink_pid_file(void)
void
cmd_shutdown(void)
{
+ log_msg(L_INFO "cmd_shutdown");
if (cli_access_restricted())
return;
@@ -615,6 +617,7 @@ cmd_shutdown(void)
void
async_shutdown(void)
{
+ log_msg(L_INFO "async_shutdown");
DBG("Shutting down...\n");
order_shutdown(0);
}
@@ -622,6 +625,7 @@ async_shutdown(void)
void
sysdep_shutdown_done(void)
{
+ log_msg(L_INFO "sysdep_shutdown_done");
unlink_pid_file();
unlink(path_control_socket);
log_msg(L_FATAL "Shutdown completed");
@@ -899,6 +903,7 @@ main(int argc, char **argv)
mpls_init();
// roa_init();
config_init();
+ tunnel_encap_init();
uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group);
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index ad85d1ea..6fa8df6e 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -109,6 +109,7 @@ void io_loop(void);
void io_log_dump(void);
int sk_open_unix(struct birdsock *s, char *name);
struct rfile *rf_open(struct pool *, const char *name, const char *mode);
+int sk_connect_unix(struct birdsock *s, char *name, socklen_t namelen);
void *rf_file(struct rfile *f);
int rf_fileno(struct rfile *f);
void test_old_bird(char *path);
diff --git a/sysdep/unix/wg_user.c b/sysdep/unix/wg_user.c
new file mode 100644
index 00000000..550f499e
--- /dev/null
+++ b/sysdep/unix/wg_user.c
@@ -0,0 +1,298 @@
+#define LOCAL_DEBUG
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+#include "lib/lists.h"
+#include "lib/ip.h"
+#include "lib/socket.h"
+#include "nest/iface.h"
+#include "sysdep/unix/unix.h"
+#include "sysdep/unix/wg_user.h"
+#include "sysdep/linux/wireguard.h"
+
+static
+socklen_t get_socket_path(const char *ifname, char *buf, uint size)
+{
+ int pos = 0;
+
+ if (size < 1)
+ return 0;
+
+#ifdef ANDROID
+ /* Abstract socket */
+ buf[pos++] = 0;
+#endif
+ bsnprintf(buf+pos, size-pos, SOCKET_PATH "%s.sock", ifname);
+ return pos + strlen(buf+pos);
+}
+
+bool wg_has_userspace(const char *ifname)
+{
+ struct stat sb;
+ char tmp[sizeof(struct sockaddr_un)];
+ socklen_t tmplen = get_socket_path(ifname, tmp, sizeof(tmp));
+
+ if (tmplen > 0 && tmp[0] == 0)
+ /* System with abstract socket (Android) always use WireGuard's userspace
+ implementation. */
+ return true;
+ else if (stat(tmp, &sb) == 0)
+ return (sb.st_mode & S_IFMT) == S_IFSOCK;
+ else
+ {
+ DBG(L_TRACE "WG: no socket %s", tmp);
+ 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)
+{
+ if (size > 0)
+ {
+ char buf[1024]="";
+ buf[sizeof(buf) - 1] = '\0';
+ strncpy(buf, sk->rbuf, sizeof(buf) - 1);
+ log(L_TRACE "WG: RX %p %d '%s'", sk, size, buf);
+ /* TODO interpret received data */
+ }
+
+ /* Clear rx buffer */
+ return 1;
+}
+
+static
+void user_tx_hook(struct birdsock *bs)
+{
+ log(L_TRACE "WG: TX %p %d", bs, bs->tpos - bs->ttx);
+
+ uint size = (uintptr_t)bs->data;
+
+ if (size > 0)
+ {
+ int res = sk_send(bs, bs->tbsize - size);
+
+ /* Send data, <0=err, >0=ok, 0=sleep */
+ if (res < 0)
+ {
+ log(L_TRACE "WG: send %d", res);
+ }
+
+ bs->data = NULL;
+ }
+}
+
+/* errno or zero if EOF */
+static
+void user_err_hook(struct birdsock *bs, int err)
+{
+ /* if (err == 0) */
+ /* return; */
+
+ log(L_TRACE "WG: ERR %p %d %s", bs, err, bs->err);
+ rfree(bs);
+}
+
+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)
+{
+ 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 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);
+ if (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)
+ wg_put_key("private_key", dev->private_key, buf, size);
+#if 0
+ /* Setting listen_port causes dead-lock in wireguard-go. */
+ if (dev->flags & WGDEVICE_HAS_LISTEN_PORT)
+ wg_put_u16("listen_port", dev->listen_port, buf, size);
+#endif
+ if (dev->flags & WGDEVICE_REPLACE_PEERS)
+ wg_put_bool("replace_peers", true, 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(&peer->endpoint, buf, size);
+ if (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)
+ wg_put_bool("replace_allowed_ips", true, 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;
+}
+
+int
+wg_user_set_device(struct pool *pool,
+ const char *ifname,
+ struct wg_device *dev)
+{
+ char path[sizeof(struct sockaddr_un)];
+ socklen_t pathlen = get_socket_path(ifname, path, sizeof(path));
+
+ struct birdsock *sock = sk_new(pool);
+ sock->rx_hook = user_rx_hook;
+ sock->tx_hook = user_tx_hook;
+ sock->err_hook = user_err_hook;
+ sock->fast_rx = 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(dev, &pos, &size);
+
+ log(L_TRACE "WG: put %d %d %s", len, size, sock->tbuf);
+
+ if (len < 0)
+ {
+ rfree(sock);
+ return -1;
+ }
+
+ sock->data = (void*)(uintptr_t)size;
+
+ int res = sk_connect_unix(sock, path, pathlen);
+ log(L_TRACE "WG: socket %s %d %d %d %s %d %s %d", res<0?strerror(errno):NULL, res, res<0?errno:0, sock->fd, ifname, path[0], path + 1, pathlen);
+ DBG(L_TRACE "WG: socket %d %d %s", res, sock->fd, path);
+ if (res < 0)
+ {
+ rfree(sock);
+ return -1;
+ }
+
+ /* abort(); */
+ return -1;
+}
diff --git a/sysdep/unix/wg_user.h b/sysdep/unix/wg_user.h
new file mode 100644
index 00000000..5bd9a41a
--- /dev/null
+++ b/sysdep/unix/wg_user.h
@@ -0,0 +1,16 @@
+#ifndef _BIRD_WG_USER_H
+#define _BIRD_WG_USER_H
+
+#include "sysdep/config.h"
+
+#ifndef SOCKET_PATH
+#define SOCKET_PATH PATH_RUNSTATEDIR "/wireguard/"
+#endif /* SOCKET_PATH */
+
+struct pool;
+struct wg_device;
+
+bool wg_has_userspace(const char *ifname);
+int wg_user_set_device(struct pool *pool, const char *ifname, struct wg_device *dev);
+
+#endif /* _BIRD_WG_USER_H */