From dcf1362c210452ac25638d0120a0b687cb59fd48 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 5 Sep 2022 12:30:07 +0200 Subject: [PATCH] pex: add support for sending/receiving global PEX messages via unix socket This can be used for allowing another protocol (e.g. DHT) to run on the same port, making it easier to deal with NAT Signed-off-by: Felix Fietkau --- main.c | 8 ++- pex-msg.c | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++--- pex-msg.h | 11 +++ pex.c | 38 +++++++---- pex.h | 2 +- 5 files changed, 228 insertions(+), 26 deletions(-) diff --git a/main.c b/main.c index e4e9331..be24db7 100644 --- a/main.c +++ b/main.c @@ -101,9 +101,10 @@ static void add_networks(void) int main(int argc, char **argv) { struct cmdline_network *net; + const char *unix_socket = NULL; int ch; - while ((ch = getopt(argc, argv, "D:dh:M:N:P:")) != -1) { + while ((ch = getopt(argc, argv, "D:dh:u:M:N:P:")) != -1) { switch (ch) { case 'D': data_dir = optarg; @@ -126,13 +127,16 @@ int main(int argc, char **argv) case 'P': global_pex_port = atoi(optarg); break; + case 'u': + unix_socket = optarg; + break; } } uloop_init(); unetd_ubus_init(); unetd_write_hosts(); - global_pex_open(); + global_pex_open(unix_socket); add_networks(); uloop_run(); pex_close(); diff --git a/pex-msg.c b/pex-msg.c index 43a6960..4f0b195 100644 --- a/pex-msg.c +++ b/pex-msg.c @@ -4,11 +4,13 @@ */ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -19,12 +21,33 @@ static char pex_tx_buf[PEX_BUF_SIZE]; static FILE *pex_urandom; -static struct uloop_fd pex_fd; +static struct uloop_fd pex_fd, pex_unix_fd; static LIST_HEAD(requests); static struct uloop_timeout gc_timer; static int pex_raw_v4_fd = -1, pex_raw_v6_fd = -1; static pex_recv_cb_t pex_recv_cb; +static pex_recv_control_cb_t pex_control_cb; +static int pex_unix_tx_fd = -1; + +static const void * +get_mapped_sockaddr(const void *addr) +{ + static struct sockaddr_in6 sin6; + const struct sockaddr_in *sin = addr; + + if (!sin || sin->sin_family != AF_INET) + return addr; + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr.s6_addr[10] = 0xff; + sin6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&sin6.sin6_addr.s6_addr[12], &sin->sin_addr, sizeof(struct in_addr)); + sin6.sin6_port = sin->sin_port; + + return &sin6; +} struct pex_msg_update_recv_ctx { struct list_head list; @@ -112,12 +135,20 @@ void *pex_msg_append(size_t len) static void pex_fd_cb(struct uloop_fd *fd, unsigned int events) { - struct sockaddr_in6 sin6; - static char buf[PEX_BUF_SIZE]; + static struct sockaddr_in6 sin6; + static char buf[PEX_RX_BUF_SIZE]; struct pex_hdr *hdr = (struct pex_hdr *)buf; ssize_t len; while (1) { + static struct iovec iov[2] = { + { .iov_base = &sin6 }, + { .iov_base = buf }, + }; + static struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + }; socklen_t slen = sizeof(sin6); len = recvfrom(fd->fd, buf, sizeof(buf), 0, (struct sockaddr *)&sin6, &slen); @@ -135,6 +166,39 @@ pex_fd_cb(struct uloop_fd *fd, unsigned int events) if (!len) continue; + if (IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { + struct sockaddr_in *sin = (struct sockaddr_in *)&sin6; + struct in_addr in = *(struct in_addr *)&sin6.sin6_addr.s6_addr[12]; + int port = sin6.sin6_port; + + memset(&sin6, 0, sizeof(sin6)); + sin->sin_port = port; + sin->sin_family = AF_INET; + sin->sin_addr = in; + slen = sizeof(*sin); + } + +retry: + if (pex_unix_tx_fd >= 0) { + iov[0].iov_len = slen; + iov[1].iov_len = len; + if (sendmsg(pex_unix_tx_fd, &msg, 0) < 0) { + switch (errno) { + case EINTR: + goto retry; + case EMSGSIZE: + case ENOBUFS: + case EAGAIN: + continue; + default: + perror("sendmsg"); + close(pex_unix_tx_fd); + pex_unix_tx_fd = -1; + break; + } + } + } + if (len < sizeof(*hdr) + sizeof(struct pex_ext_hdr)) continue; @@ -146,6 +210,86 @@ pex_fd_cb(struct uloop_fd *fd, unsigned int events) } } +static void +pex_unix_cb(struct uloop_fd *fd, unsigned int events) +{ + static char buf[PEX_RX_BUF_SIZE]; + static struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + ssize_t len; + + while (1) { + const struct sockaddr *sa = (struct sockaddr *)buf; + uint8_t fd_buf[CMSG_SPACE(sizeof(int))] = { 0 }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = fd_buf, + .msg_controllen = CMSG_LEN(sizeof(int)), + }; + struct cmsghdr *cmsg; + socklen_t slen; + int *pfd; + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + pfd = (int *)CMSG_DATA(cmsg); + *pfd = -1; + + len = recvmsg(fd->fd, &msg, 0); + if (len < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + break; + + pex_close(); + return; + } + + if (*pfd >= 0) { + if (pex_unix_tx_fd >= 0) + close(pex_unix_tx_fd); + + pex_unix_tx_fd = *pfd; + } + + if (!len) + continue; + + if (len < sizeof(*sa)) + continue; + + if (sa->sa_family == AF_LOCAL) { + slen = sizeof(struct sockaddr); + len -= slen; + if (len < sizeof(struct pex_msg_local_control)) + continue; + + if (pex_control_cb) + pex_control_cb((struct pex_msg_local_control *)&buf[slen], len); + + continue; + } + + if (sa->sa_family == AF_INET) + slen = sizeof(struct sockaddr_in); + else if (sa->sa_family == AF_INET6) + slen = sizeof(struct sockaddr_in6); + else + continue; + + sa = get_mapped_sockaddr(sa); + sendto(pex_fd.fd, buf + slen, len - slen, 0, sa, sizeof(struct sockaddr_in6)); + } +} + static inline uint32_t csum_tcpudp_nofold(uint32_t saddr, uint32_t daddr, uint32_t len, uint8_t proto) { @@ -268,8 +412,10 @@ int __pex_msg_send(int fd, const void *addr, void *ip_hdr, size_t ip_hdrlen) hdr->len -= sizeof(struct pex_ext_hdr); if (ip_hdrlen) fd = sa->sa_family == AF_INET6 ? pex_raw_v6_fd : pex_raw_v4_fd; - else + else { fd = pex_fd.fd; + sa = addr = get_mapped_sockaddr(addr); + } if (fd < 0) return -1; @@ -612,11 +758,29 @@ close_raw: return -1; } -void pex_close(void) +int pex_unix_open(const char *path, pex_recv_control_cb_t cb) { - if (!pex_fd.cb) - return; + mode_t prev_mask; + int fd; + + pex_control_cb = cb; + unlink(path); + prev_mask = umask(0177); + fd = usock(USOCK_UDP | USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, path, NULL); + umask(prev_mask); + if (fd < 0) + return -1; + + pex_unix_fd.cb = pex_unix_cb; + pex_unix_fd.fd = fd; + uloop_fd_add(&pex_unix_fd, ULOOP_READ); + + return 0; +} + +void pex_close(void) +{ if (pex_raw_v4_fd >= 0) close(pex_raw_v4_fd); if (pex_raw_v6_fd >= 0) @@ -624,9 +788,20 @@ void pex_close(void) pex_raw_v4_fd = -1; pex_raw_v6_fd = -1; - fclose(pex_urandom); - uloop_fd_delete(&pex_fd); - close(pex_fd.fd); + if (pex_urandom) + fclose(pex_urandom); + + if (pex_fd.cb) { + uloop_fd_delete(&pex_fd); + close(pex_fd.fd); + } + + if (pex_unix_fd.cb) { + uloop_fd_delete(&pex_unix_fd); + close(pex_unix_fd.fd); + } + pex_fd.cb = NULL; + pex_unix_fd.cb = NULL; pex_urandom = NULL; } diff --git a/pex-msg.h b/pex-msg.h index 653eb04..e8e4f11 100644 --- a/pex-msg.h +++ b/pex-msg.h @@ -6,9 +6,11 @@ #include #include "curve25519.h" #include "siphash.h" +#include "utils.h" #define UNETD_GLOBAL_PEX_PORT 51819 #define PEX_BUF_SIZE 1024 +#define PEX_RX_BUF_SIZE 16384 #define UNETD_NET_DATA_SIZE_MAX (128 * 1024) enum pex_opcode { @@ -85,9 +87,18 @@ struct pex_msg_update_send_ctx { int rem; }; +struct pex_msg_local_control { + int msg_type; + uint8_t auth_id[PEX_ID_LEN]; + union network_endpoint ep; + int timeout; +}; + typedef void (*pex_recv_cb_t)(struct pex_hdr *hdr, struct sockaddr_in6 *addr); +typedef void (*pex_recv_control_cb_t)(struct pex_msg_local_control *msg, int len); int pex_open(void *addr, size_t addr_len, pex_recv_cb_t cb, bool server); +int pex_unix_open(const char *path, pex_recv_control_cb_t cb); void pex_close(void); uint64_t pex_network_hash(const uint8_t *auth_key, uint64_t req_id); diff --git a/pex.c b/pex.c index d598339..64e2bd2 100644 --- a/pex.c +++ b/pex.c @@ -879,17 +879,6 @@ global_pex_recv(struct pex_hdr *hdr, struct sockaddr_in6 *addr) if (!peer) break; - if (IN6_IS_ADDR_V4MAPPED(&addr->sin6_addr)) { - struct sockaddr_in *sin = (struct sockaddr_in *)addr; - struct in_addr in = *(struct in_addr *)&addr->sin6_addr.s6_addr[12]; - int port = addr->sin6_port; - - memset(addr, 0, sizeof(*addr)); - sin->sin_port = port; - sin->sin_family = AF_INET; - sin->sin_addr = in; - } - D_PEER(net, peer, "receive endpoint notification from %s", inet_ntop(addr->sin6_family, network_endpoint_addr((void *)addr, &addr_len), buf, sizeof(buf))); @@ -899,12 +888,35 @@ global_pex_recv(struct pex_hdr *hdr, struct sockaddr_in6 *addr) } } -int global_pex_open(void) +static void +pex_recv_control(struct pex_msg_local_control *msg, int len) +{ + struct network *net; + + if (msg->msg_type != 0) + return; + + net = global_pex_find_network(msg->auth_id); + if (!net) + return; + + if (!msg->timeout) + msg->timeout = 60; + network_pex_create_host(net, &msg->ep, msg->timeout); +} + +int global_pex_open(const char *unix_path) { struct sockaddr_in6 sin6 = {}; + int ret; sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(global_pex_port); - return pex_open(&sin6, sizeof(sin6), global_pex_recv, true); + ret = pex_open(&sin6, sizeof(sin6), global_pex_recv, true); + + if (unix_path) + pex_unix_open(unix_path, pex_recv_control); + + return ret; } diff --git a/pex.h b/pex.h index 123b4a9..f16f77c 100644 --- a/pex.h +++ b/pex.h @@ -43,6 +43,6 @@ static inline bool network_pex_active(struct network_pex *pex) return pex->fd.fd >= 0; } -int global_pex_open(void); +int global_pex_open(const char *unix_path); #endif -- 2.30.2