diff options
Diffstat (limited to 'libhw/host_net.c')
-rw-r--r-- | libhw/host_net.c | 534 |
1 files changed, 0 insertions, 534 deletions
diff --git a/libhw/host_net.c b/libhw/host_net.c deleted file mode 100644 index e89cd66..0000000 --- a/libhw/host_net.c +++ /dev/null @@ -1,534 +0,0 @@ -/* libhw/host_net.c - <libhw/generic/net.h> implementation for hosted glibc - * - * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -#define _GNU_SOURCE /* for pthread_sigqueue(3gnu) */ -/* misc */ -#include <errno.h> /* for errno, EAGAIN, EINVAL */ -#include <error.h> /* for error(3gnu) */ -#include <stdlib.h> /* for abs(), shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */ -#include <unistd.h> /* for read(), write() */ -/* net */ -#include <arpa/inet.h> /* for htons(3p) */ -#include <netinet/in.h> /* for struct sockaddr_in */ -#include <sys/socket.h> /* for struct sockaddr{,_storage}, SOCK_*, SOL_*, SO_*, socket(), setsockopt(), bind(), listen(), accept() */ -/* async */ -#include <pthread.h> /* for pthread_* */ -#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */ - -#include <libcr/coroutine.h> -#include <libmisc/assert.h> -#include <libmisc/macro.h> -#include <libobj/obj.h> - -#include <libhw/generic/alarmclock.h> - -#define IMPLEMENTATION_FOR_LIBHW_HOST_NET_H YES -#include <libhw/host_net.h> - -#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */ - -LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static) -LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static) - -LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static) -LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static) -LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static) -LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static) -LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static) -LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static) - -LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static) -LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static) - -/* common *********************************************************************/ - -static int hostnet_sig_io = 0; - -static void hostnet_handle_sig_io(int LM_UNUSED(sig), siginfo_t *info, void *LM_UNUSED(ucontext)) { - cr_unpause_from_intrhandler((cid_t)info->si_value.sival_int); -} - -static void hostnet_init(void) { - struct sigaction action = {0}; - - if (hostnet_sig_io) - return; - - hostnet_sig_io = host_sigrt_alloc(); - - action.sa_flags = SA_SIGINFO; - action.sa_sigaction = hostnet_handle_sig_io; - if (sigaction(hostnet_sig_io, &action, NULL) < 0) - error(1, errno, "sigaction"); -} - -#define WAKE_COROUTINE(args) do { \ - int r; \ - union sigval val = {0}; \ - val.sival_int = (int)((args)->cr_coroutine); \ - do { \ - r = pthread_sigqueue((args)->cr_thread, \ - hostnet_sig_io, \ - val); \ - assert(r == 0 || r == EAGAIN); \ - } while (r == EAGAIN); \ - } while (0) - -static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) { - pthread_t thread; - bool saved = cr_save_and_disable_interrupts(); - if (pthread_create(&thread, NULL, fn, args)) - return true; - cr_pause_and_yield(); - cr_restore_interrupts(saved); - if (pthread_join(thread, NULL)) - return true; - return false; -} - -enum hostnet_timeout_op { - OP_NONE, - OP_SEND, - OP_RECV, -}; - -static inline ssize_t hostnet_map_negerrno(ssize_t v, enum hostnet_timeout_op op) { - if (v >= 0) - return v; - switch (v) { - case -EHOSTUNREACH: - return -NET_EARP_TIMEOUT; - case -ETIMEDOUT: - switch (op) { - case OP_NONE: - assert_notreached("impossible ETIMEDOUT"); - case OP_SEND: - return -NET_EACK_TIMEOUT; - case OP_RECV: - return -NET_ERECV_TIMEOUT; - } - assert_notreached("invalid timeout op"); - case -EBADF: - return -NET_ECLOSED; - case -EMSGSIZE: - return -NET_EMSGSIZE; - default: - return -NET_EOTHER; - } -} - -/* TCP init() ( AKA socket(3) + listen(3) )************************************/ - -void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) { - int listenerfd; - union { - struct sockaddr_in in; - struct sockaddr gen; - } addr = { 0 }; - - hostnet_init(); - - addr.in.sin_family = AF_INET; - addr.in.sin_port = htons(port); - listenerfd = socket(AF_INET, SOCK_STREAM, 0); - if (listenerfd < 0) - error(1, errno, "socket"); - if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) - error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd); - if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0) - error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd); - if (bind(listenerfd, &addr.gen, sizeof addr) < 0) - error(1, errno, "bind(fd=%d)", listenerfd); - if (listen(listenerfd, 0) < 0) - error(1, errno, "listen(fd=%d)", listenerfd); - - self->fd = listenerfd; -} - -/* TCP listener accept() ******************************************************/ - -struct hostnet_pthread_accept_args { - pthread_t cr_thread; - cid_t cr_coroutine; - - int listenerfd; - - int *ret_connfd; -}; - -static void *hostnet_pthread_accept(void *_args) { - struct hostnet_pthread_accept_args *args = _args; - *(args->ret_connfd) = accept(args->listenerfd, NULL, NULL); - if (*(args->ret_connfd) < 0) - *(args->ret_connfd) = -errno; - WAKE_COROUTINE(args); - return NULL; -} - -static lo_interface net_stream_conn hostnet_tcplist_accept(struct hostnet_tcp_listener *listener) { - assert(listener); - - int ret_connfd; - struct hostnet_pthread_accept_args args = { - .cr_thread = pthread_self(), - .cr_coroutine = cr_getcid(), - .listenerfd = listener->fd, - .ret_connfd = &ret_connfd, - }; - if (RUN_PTHREAD(hostnet_pthread_accept, &args)) - return LO_NULL(net_stream_conn); - - if (ret_connfd < 0) - return LO_NULL(net_stream_conn); - - listener->active_conn.fd = ret_connfd; - listener->active_conn.read_deadline_ns = 0; - return lo_box_hostnet_tcp_as_net_stream_conn(&listener->active_conn); -} - -/* TCP listener close() *******************************************************/ - -static int hostnet_tcplist_close(struct hostnet_tcp_listener *listener) { - assert(listener); - - return hostnet_map_negerrno(shutdown(listener->fd, SHUT_RDWR) ? -errno : 0, OP_NONE); -} - -/* TCP read() *****************************************************************/ - -static void hostnet_tcp_set_read_deadline(struct _hostnet_tcp_conn *conn, uint64_t ts_ns) { - assert(conn); - - conn->read_deadline_ns = ts_ns; -} - -struct hostnet_pthread_readv_args { - pthread_t cr_thread; - cid_t cr_coroutine; - - int connfd; - struct timeval timeout; - const struct iovec *iov; - int iovcnt; - - ssize_t *ret; -}; - -static void *hostnet_pthread_readv(void *_args) { - struct hostnet_pthread_readv_args *args = _args; - - *(args->ret) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO, - &args->timeout, sizeof(args->timeout)); - if (*(args->ret) < 0) - goto end; - - *(args->ret) = readv(args->connfd, args->iov, args->iovcnt); - if (*(args->ret) < 0) - goto end; - - end: - if (*(args->ret) < 0) - *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND); - WAKE_COROUTINE(args); - return NULL; -} - -static ssize_t hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) { - assert(conn); - assert(iov); - assert(iovcnt > 0); - - ssize_t ret; - struct hostnet_pthread_readv_args args = { - .cr_thread = pthread_self(), - .cr_coroutine = cr_getcid(), - - .connfd = conn->fd, - .iov = iov, - .iovcnt = iovcnt, - - .ret = &ret, - }; - if (conn->read_deadline_ns) { - uint64_t now_ns = LO_CALL(bootclock, get_time_ns); - if (conn->read_deadline_ns < now_ns) - return -NET_ERECV_TIMEOUT; - args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); - } else { - args.timeout = (host_us_time_t){0}; - } - - if (RUN_PTHREAD(hostnet_pthread_readv, &args)) - return -NET_ETHREAD; - return ret; -} - -/* TCP write() ****************************************************************/ - -struct hostnet_pthread_writev_args { - pthread_t cr_thread; - cid_t cr_coroutine; - - int connfd; - const struct iovec *iov; - int iovcnt; - - ssize_t *ret; -}; - -static void *hostnet_pthread_writev(void *_args) { - struct hostnet_pthread_writev_args *args = _args; - - size_t count = 0; - struct iovec *iov = alloca(sizeof(struct iovec)*args->iovcnt); - for (int i = 0; i < args->iovcnt; i++) { - iov[i] = args->iov[i]; - count += args->iov[i].iov_len; - } - int iovcnt = args->iovcnt; - - size_t done = 0; - while (done < count) { - ssize_t r = writev(args->connfd, iov, iovcnt); - if (r < 0) { - hostnet_map_negerrno(-errno, OP_RECV); - break; - } - done += r; - while (iovcnt && (size_t)r >= iov[0].iov_len) { - r -= iov[0].iov_len; - iov++; - iovcnt--; - } - if (r > 0) { - iov[0].iov_base += r; - iov[0].iov_len -= r; - } - } - if (done == count) - *(args->ret) = done; - WAKE_COROUTINE(args); - return NULL; -} - -static ssize_t hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) { - assert(conn); - assert(iov); - assert(iovcnt > 0); - - ssize_t ret; - struct hostnet_pthread_writev_args args = { - .cr_thread = pthread_self(), - .cr_coroutine = cr_getcid(), - - .connfd = conn->fd, - .iov = iov, - .iovcnt = iovcnt, - - .ret = &ret, - }; - if (RUN_PTHREAD(hostnet_pthread_writev, &args)) - return -NET_ETHREAD; - return ret; -} - -/* TCP close() ****************************************************************/ - -static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn) { - assert(conn); - return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RDWR) ? -errno : 0, OP_NONE); -} -static int hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) { - assert(conn); - return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RD) ? -errno : 0, OP_NONE); -} -static int hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) { - assert(conn); - return hostnet_map_negerrno(shutdown(conn->fd, SHUT_WR) ? -errno : 0, OP_NONE); -} - -/* UDP init() *****************************************************************/ - -void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) { - int fd; - union { - struct sockaddr_in in; - struct sockaddr gen; - struct sockaddr_storage stor; - } addr = { 0 }; - - hostnet_init(); - - addr.in.sin_family = AF_INET; - addr.in.sin_port = htons(port); - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) - error(1, errno, "socket"); - if (bind(fd, &addr.gen, sizeof addr) < 0) - error(1, errno, "bind"); - - self->fd = fd; - self->read_deadline_ns = 0; -} - -/* UDP sendto() ***************************************************************/ - -struct hostnet_pthread_sendto_args { - pthread_t cr_thread; - cid_t cr_coroutine; - - int connfd; - void *buf; - size_t count; - struct net_ip4_addr node; - uint16_t port; - - ssize_t *ret; -}; - -static void *hostnet_pthread_sendto(void *_args) { - struct hostnet_pthread_sendto_args *args = _args; - union { - struct sockaddr_in in; - struct sockaddr gen; - struct sockaddr_storage stor; - } addr = { 0 }; - - addr.in.sin_family = AF_INET; - addr.in.sin_addr.s_addr = - (((uint32_t)args->node.octets[0])<<24) | - (((uint32_t)args->node.octets[1])<<16) | - (((uint32_t)args->node.octets[2])<< 8) | - (((uint32_t)args->node.octets[3])<< 0) ; - addr.in.sin_port = htons(args->port); - *(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr)); - if (*(args->ret) < 0) - *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND); - WAKE_COROUTINE(args); - return NULL; -} - -static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size_t count, - struct net_ip4_addr node, uint16_t port) { - assert(conn); - - ssize_t ret; - struct hostnet_pthread_sendto_args args = { - .cr_thread = pthread_self(), - .cr_coroutine = cr_getcid(), - - .connfd = conn->fd, - .buf = buf, - .count = count, - .node = node, - .port = port, - - .ret = &ret, - }; - if (RUN_PTHREAD(hostnet_pthread_sendto, &args)) - return -NET_ETHREAD; - return ret; -} - -/* UDP recvfrom() *************************************************************/ - -static void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn, - uint64_t ts_ns) { - assert(conn); - - conn->read_deadline_ns = ts_ns; -} - -struct hostnet_pthread_recvfrom_args { - pthread_t cr_thread; - cid_t cr_coroutine; - - int connfd; - struct timeval timeout; - void *buf; - size_t count; - - ssize_t *ret_size; - struct net_ip4_addr *ret_node; - uint16_t *ret_port; -}; - -static void *hostnet_pthread_recvfrom(void *_args) { - struct hostnet_pthread_recvfrom_args *args = _args; - - union { - struct sockaddr_in in; - struct sockaddr gen; - struct sockaddr_storage stor; - } addr = { 0 }; - socklen_t addr_size; - - *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO, - &args->timeout, sizeof(args->timeout)); - if (*(args->ret_size) < 0) - goto end; - - *(args->ret_size) = recvfrom(args->connfd, args->buf, args->count, - MSG_TRUNC, &addr.gen, &addr_size); - if (*(args->ret_size) < 0) - goto end; - - assert(addr.in.sin_family == AF_INET); - if (args->ret_node) { - args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF; - args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF; - args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF; - args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF; - } - if (args->ret_port) { - (*args->ret_port) = ntohs(addr.in.sin_port); - } - - end: - if (*(args->ret_size) < 0) - *(args->ret_size) = hostnet_map_negerrno(-errno, OP_RECV); - WAKE_COROUTINE(args); - return NULL; -} - -static ssize_t hostnet_udp_recvfrom(struct hostnet_udp_conn *conn, void *buf, size_t count, - struct net_ip4_addr *ret_node, uint16_t *ret_port) { - assert(conn); - - ssize_t ret; - struct hostnet_pthread_recvfrom_args args = { - .cr_thread = pthread_self(), - .cr_coroutine = cr_getcid(), - - .connfd = conn->fd, - .buf = buf, - .count = count, - - .ret_size = &ret, - .ret_node = ret_node, - .ret_port = ret_port, - }; - if (conn->read_deadline_ns) { - uint64_t now_ns = LO_CALL(bootclock, get_time_ns); - if (conn->read_deadline_ns < now_ns) - return -NET_ERECV_TIMEOUT; - args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); - } else { - args.timeout = (host_us_time_t){0}; - } - - if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args)) - return -NET_ETHREAD; - return ret; -} - -/* UDP close() ****************************************************************/ - -static int hostnet_udp_close(struct hostnet_udp_conn *conn) { - assert(conn); - - return hostnet_map_negerrno(close(conn->fd) ? -errno : 0, OP_NONE); -} |