commit cfe57149140baac9bfd1f900a6e6e425e41b6900 from: Omar Polo date: Sat Jul 30 21:24:05 2022 UTC implement socket splicing on OpenBSD socket splicing allows to do zero-copy data transfers between sockets. This adds a specific implementation for OpenBSD using the setsockopt SO_SPLICE and a default implementation with libevent (that is the old code doing the copy in userland.) It's possible to do the same on linux with splice(2), not implementd though. commit - ecd6b12a1f94fa609c3f7484f12d4cc8bc5a46f1 commit + cfe57149140baac9bfd1f900a6e6e425e41b6900 blob - e12a0cfb06988605fc631ae39271ed44a52e41d2 blob + ca4a7aa15578c1b5edae7540faa824038de59c42 --- Makefile +++ Makefile @@ -4,11 +4,14 @@ VERSION = 0.4 PROG = lstun DISTNAME = ${PROG}-${VERSION} -HEADERS = log.h +HEADERS = log.h \ + lstun.h SOURCES = compats.c \ log.c \ lstun.c \ + splice.c \ + splice_bev.c \ tests.c OBJS = ${SOURCES:.c=.o} blob - aa7a9534a58d2c80e103a87a08936d9cfc8eca26 blob + 0adf97437ca6d672a99314a848d308eae7039d08 --- configure +++ configure @@ -275,6 +275,7 @@ HAVE_LIBEVENT2=0 # may not be checked HAVE_PLEDGE= HAVE_PROGRAM_INVOCATION_SHORT_NAME= HAVE_PR_SET_NAME= +HAVE_SO_SPLICE= HAVE_STRLCAT= HAVE_STRLCPY= HAVE_STRTONUM= @@ -445,6 +446,7 @@ runtest lib_socket LIB_SOCKET "" "" "-lsocket -lnsl" | runtest pledge PLEDGE || true runtest program_invocation_short_name PROGRAM_INVOCATION_SHORT_NAME || true runtest PR_SET_NAME PR_SET_NAME || true +runtest SO_SPLICE SO_SPLICE || true runtest static STATIC "" "-static" || true runtest strlcat STRLCAT || true runtest strlcpy STRLCPY || true @@ -559,6 +561,7 @@ cat < #include "log.h" +#include "lstun.h" #define MAXSOCK 32 #define BACKOFF 1 @@ -66,16 +67,6 @@ pid_t ssh_pid = -1; int conn; -struct conn { - int ntentative; - struct timeval retry; - struct event waitev; - int source; - struct bufferevent *sourcebev; - int to; - struct bufferevent *tobev; -}; - static void sig_handler(int sig, short event, void *data) { @@ -121,6 +112,17 @@ spawn_ssh(void) } static void +killing_time(int fd, short event, void *data) +{ + if (ssh_pid == -1) + return; + + log_debug("timeout expired, killing ssh (%d)", ssh_pid); + kill(ssh_pid, SIGTERM); + ssh_pid = -1; +} + +void conn_free(struct conn *c) { if (c->sourcebev != NULL) @@ -136,50 +138,7 @@ conn_free(struct conn *c) close(c->to); free(c); -} -static void -killing_time(int fd, short event, void *data) -{ - if (ssh_pid == -1) - return; - - log_debug("timeout expired, killing ssh (%d)", ssh_pid); - kill(ssh_pid, SIGTERM); - ssh_pid = -1; -} - -static void -nopcb(struct bufferevent *bev, void *d) -{ - return; -} - -static void -sreadcb(struct bufferevent *bev, void *d) -{ - struct conn *c = d; - - bufferevent_write_buffer(c->tobev, EVBUFFER_INPUT(bev)); -} - -static void -treadcb(struct bufferevent *bev, void *d) -{ - struct conn *c = d; - - bufferevent_write_buffer(c->sourcebev, EVBUFFER_INPUT(bev)); -} - -static void -errcb(struct bufferevent *bev, short event, void *d) -{ - struct conn *c = d; - - log_info("closing connection (event=%x)", event); - - conn_free(c); - if (--conn == 0) { log_debug("scheduling ssh termination (%llds)", (long long)timeout.tv_sec); @@ -264,16 +223,8 @@ try_to_connect(int fd, short event, void *d) log_info("connected!"); - c->sourcebev = bufferevent_new(c->source, sreadcb, nopcb, errcb, c); - c->tobev = bufferevent_new(c->to, treadcb, nopcb, errcb, c); - if (c->sourcebev == NULL || c->tobev == NULL) { - log_warn("bufferevent_new"); + if (conn_splice(c) == -1) conn_free(c); - return; - } - - bufferevent_enable(c->sourcebev, EV_READ|EV_WRITE); - bufferevent_enable(c->tobev, EV_READ|EV_WRITE); } static void blob - 3ac5d07899463dd2f7686bdb4d644b9b756021fd blob + 4a4aeb61dc59fbbf52851f5fad6d3a12dc91ab45 --- tests.c +++ tests.c @@ -99,6 +99,22 @@ main(void) return 0; } #endif /* TEST_PR_SET_NAME */ +#if TEST_SO_SPLICE +#include + +int +main(void) +{ + int src = 0, dst = 1; + + /* + * invalid usage, i'm only interested in checking if it + * compiles + */ + setsockopt(src, SOL_SOCKET, SO_SPLICE, &dst, sizeof(int)); + return 0; +} +#endif /* TEST_SO_SPLICE */ #if TEST_STATIC int main(void) blob - /dev/null blob + c57c79c233526cdcfb89e7f7761c0b94b4b75dad (mode 644) --- /dev/null +++ lstun.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct conn { + int ntentative; + struct timeval retry; + struct event waitev; + int source; + struct bufferevent *sourcebev; + int to; + struct bufferevent *tobev; +}; + +int conn_splice(struct conn *); +void conn_free(struct conn *); blob - /dev/null blob + ad33ac746ebe2a3cb533e1c25e26f9742c81277f (mode 644) --- /dev/null +++ splice.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#if HAVE_SO_SPLICE + +#include + +#include "log.h" +#include "lstun.h" + +static void +splice_done(int fd, short ev, void *data) +{ + struct conn *c = data; + + log_info("closing connection (event=%x)", ev); + conn_free(c); +} + +int +conn_splice(struct conn *c) +{ + if (setsockopt(c->source, SOL_SOCKET, SO_SPLICE, &c->to, sizeof(int)) + == -1) + return -1; + if (setsockopt(c->to, SOL_SOCKET, SO_SPLICE, &c->source, sizeof(int)) + == -1) + return -1; + + event_once(c->source, EV_READ, splice_done, c, NULL); + return 0; +} + +#endif /* HAVE_SO_SPLICE */ blob - /dev/null blob + 63498c504deb7aaca4573c0a1afcf27dfd0c093b (mode 644) --- /dev/null +++ splice_bev.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022 Omar Polo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "config.h" + +#if !HAVE_SO_SPLICE + +#include + +#include "log.h" +#include "lstun.h" + +static void +nopcb(struct bufferevent *bev, void *d) +{ + return; +} + +static void +sreadcb(struct bufferevent *bev, void *d) +{ + struct conn *c = d; + + bufferevent_write_buffer(c->tobev, EVBUFFER_INPUT(bev)); +} + +static void +treadcb(struct bufferevent *bev, void *d) +{ + struct conn *c = d; + + bufferevent_write_buffer(c->sourcebev, EVBUFFER_INPUT(bev)); +} + +static void +errcb(struct bufferevent *bev, short event, void *d) +{ + struct conn *c = d; + + log_info("closing connection (event=%x)", event); + conn_free(c); +} + +int +conn_splice(struct conn *c) +{ + c->sourcebev = bufferevent_new(c->source, sreadcb, nopcb, errcb, c); + c->tobev = bufferevent_new(c->to, treadcb, nopcb, errcb, c); + + if (c->sourcebev == NULL || c->tobev == NULL) { + log_warn("bufferevent_new"); + return -1; + } + + bufferevent_enable(c->sourcebev, EV_READ|EV_WRITE); + bufferevent_enable(c->tobev, EV_READ|EV_WRITE); + return 0; +} + +#endif /* !HAVE_SO_SPLICE */