diff options
Diffstat (limited to 'sysdep/unix')
-rw-r--r-- | sysdep/unix/Makefile | 2 | ||||
-rw-r--r-- | sysdep/unix/io.c | 77 | ||||
-rw-r--r-- | sysdep/unix/main.c | 5 | ||||
-rw-r--r-- | sysdep/unix/unix.h | 1 | ||||
-rw-r--r-- | sysdep/unix/wg_user.c | 298 | ||||
-rw-r--r-- | sysdep/unix/wg_user.h | 16 |
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 */ |