1877cf210SMark Johnston /*- 2877cf210SMark Johnston * SPDX-License-Identifier: BSD-2-Clause 3877cf210SMark Johnston * 4877cf210SMark Johnston * Copyright (c) 2024 Stormshield 5877cf210SMark Johnston */ 6877cf210SMark Johnston 7877cf210SMark Johnston #include <sys/capsicum.h> 8877cf210SMark Johnston #include <sys/event.h> 9877cf210SMark Johnston #include <sys/filio.h> 10877cf210SMark Johnston #include <sys/socket.h> 11877cf210SMark Johnston #include <sys/wait.h> 12877cf210SMark Johnston 13877cf210SMark Johnston #include <netinet/in.h> 14877cf210SMark Johnston #include <netinet/tcp.h> 15877cf210SMark Johnston 16877cf210SMark Johnston #include <errno.h> 17877cf210SMark Johnston #include <poll.h> 18877cf210SMark Johnston #include <pthread.h> 19877cf210SMark Johnston #include <signal.h> 20877cf210SMark Johnston #include <stdio.h> 21877cf210SMark Johnston #include <stdlib.h> 22877cf210SMark Johnston #include <string.h> 23877cf210SMark Johnston #include <time.h> 24877cf210SMark Johnston 25877cf210SMark Johnston #include <atf-c.h> 26877cf210SMark Johnston 27877cf210SMark Johnston static void 28877cf210SMark Johnston checked_close(int fd) 29877cf210SMark Johnston { 30877cf210SMark Johnston int error; 31877cf210SMark Johnston 32877cf210SMark Johnston error = close(fd); 33877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno)); 34877cf210SMark Johnston } 35877cf210SMark Johnston 36877cf210SMark Johnston static int 37877cf210SMark Johnston fionread(int fd) 38877cf210SMark Johnston { 39877cf210SMark Johnston int data, error; 40877cf210SMark Johnston 41877cf210SMark Johnston data = 0; 42877cf210SMark Johnston error = ioctl(fd, FIONREAD, &data); 43877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s", strerror(errno)); 44877cf210SMark Johnston ATF_REQUIRE(data >= 0); 45877cf210SMark Johnston return (data); 46877cf210SMark Johnston } 47877cf210SMark Johnston 48877cf210SMark Johnston static void 49877cf210SMark Johnston noblocking(int fd) 50877cf210SMark Johnston { 51877cf210SMark Johnston int flags, error; 52877cf210SMark Johnston 53877cf210SMark Johnston flags = fcntl(fd, F_GETFL); 54877cf210SMark Johnston ATF_REQUIRE_MSG(flags != -1, "fcntl failed: %s", strerror(errno)); 55877cf210SMark Johnston flags |= O_NONBLOCK; 56877cf210SMark Johnston error = fcntl(fd, F_SETFL, flags); 57877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "fcntl failed: %s", strerror(errno)); 58877cf210SMark Johnston } 59877cf210SMark Johnston 60877cf210SMark Johnston /* 61877cf210SMark Johnston * Create a pair of connected TCP sockets, returned via the "out" array. 62877cf210SMark Johnston */ 63877cf210SMark Johnston static void 64877cf210SMark Johnston tcp_socketpair(int out[2], int domain) 65877cf210SMark Johnston { 66877cf210SMark Johnston struct sockaddr_in sin; 67877cf210SMark Johnston struct sockaddr_in6 sin6; 68877cf210SMark Johnston struct sockaddr *sinp; 69877cf210SMark Johnston int error, sd[2]; 70877cf210SMark Johnston 71877cf210SMark Johnston sd[0] = socket(domain, SOCK_STREAM, 0); 72877cf210SMark Johnston ATF_REQUIRE_MSG(sd[0] >= 0, "socket failed: %s", strerror(errno)); 73877cf210SMark Johnston sd[1] = socket(domain, SOCK_STREAM, 0); 74877cf210SMark Johnston ATF_REQUIRE_MSG(sd[1] >= 0, "socket failed: %s", strerror(errno)); 75877cf210SMark Johnston 76877cf210SMark Johnston error = setsockopt(sd[0], IPPROTO_TCP, TCP_NODELAY, &(int){ 1 }, 77877cf210SMark Johnston sizeof(int)); 78877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 79877cf210SMark Johnston error = setsockopt(sd[1], IPPROTO_TCP, TCP_NODELAY, &(int){ 1 }, 80877cf210SMark Johnston sizeof(int)); 81877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 82877cf210SMark Johnston 83877cf210SMark Johnston if (domain == PF_INET) { 84877cf210SMark Johnston memset(&sin, 0, sizeof(sin)); 85877cf210SMark Johnston sin.sin_family = AF_INET; 86877cf210SMark Johnston sin.sin_len = sizeof(sin); 87877cf210SMark Johnston sin.sin_addr.s_addr = htonl(INADDR_ANY); 88877cf210SMark Johnston sin.sin_port = htons(0); 89877cf210SMark Johnston sinp = (struct sockaddr *)&sin; 90877cf210SMark Johnston } else { 91877cf210SMark Johnston ATF_REQUIRE(domain == PF_INET6); 92877cf210SMark Johnston memset(&sin6, 0, sizeof(sin6)); 93877cf210SMark Johnston sin6.sin6_family = AF_INET6; 94877cf210SMark Johnston sin6.sin6_len = sizeof(sin6); 95877cf210SMark Johnston sin6.sin6_addr = in6addr_any; 96877cf210SMark Johnston sin6.sin6_port = htons(0); 97877cf210SMark Johnston sinp = (struct sockaddr *)&sin6; 98877cf210SMark Johnston } 99877cf210SMark Johnston 100877cf210SMark Johnston error = bind(sd[0], sinp, sinp->sa_len); 101877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno)); 102877cf210SMark Johnston error = listen(sd[0], 1); 103877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno)); 104877cf210SMark Johnston 105877cf210SMark Johnston error = getsockname(sd[0], sinp, &(socklen_t){ sinp->sa_len }); 106877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno)); 107877cf210SMark Johnston 108877cf210SMark Johnston error = connect(sd[1], sinp, sinp->sa_len); 109877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno)); 110877cf210SMark Johnston out[0] = accept(sd[0], NULL, NULL); 111877cf210SMark Johnston ATF_REQUIRE_MSG(out[0] >= 0, "accept failed: %s", strerror(errno)); 112877cf210SMark Johnston checked_close(sd[0]); 113877cf210SMark Johnston out[1] = sd[1]; 114877cf210SMark Johnston } 115877cf210SMark Johnston 116877cf210SMark Johnston static void 117877cf210SMark Johnston tcp4_socketpair(int out[2]) 118877cf210SMark Johnston { 119877cf210SMark Johnston tcp_socketpair(out, PF_INET); 120877cf210SMark Johnston } 121877cf210SMark Johnston 122877cf210SMark Johnston static void 123877cf210SMark Johnston tcp6_socketpair(int out[2]) 124877cf210SMark Johnston { 125877cf210SMark Johnston tcp_socketpair(out, PF_INET6); 126877cf210SMark Johnston } 127877cf210SMark Johnston 128877cf210SMark Johnston static off_t 129877cf210SMark Johnston nspliced(int sd) 130877cf210SMark Johnston { 131877cf210SMark Johnston off_t n; 132877cf210SMark Johnston socklen_t len; 133877cf210SMark Johnston int error; 134877cf210SMark Johnston 135877cf210SMark Johnston len = sizeof(n); 136877cf210SMark Johnston error = getsockopt(sd, SOL_SOCKET, SO_SPLICE, &n, &len); 137877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "getsockopt failed: %s", strerror(errno)); 138877cf210SMark Johnston ATF_REQUIRE_MSG(len == sizeof(n), "unexpected length: %d", len); 139877cf210SMark Johnston return (n); 140877cf210SMark Johnston } 141877cf210SMark Johnston 142877cf210SMark Johnston /* 143877cf210SMark Johnston * Use a macro so that ATF_REQUIRE_MSG prints a useful line number. 144877cf210SMark Johnston */ 145877cf210SMark Johnston #define check_nspliced(sd, n) do { \ 146877cf210SMark Johnston off_t sofar; \ 147877cf210SMark Johnston \ 148877cf210SMark Johnston sofar = nspliced(sd); \ 149877cf210SMark Johnston ATF_REQUIRE_MSG(sofar == (off_t)n, "spliced %jd bytes, expected %jd", \ 150877cf210SMark Johnston (intmax_t)sofar, (intmax_t)n); \ 151877cf210SMark Johnston } while (0) 152877cf210SMark Johnston 153877cf210SMark Johnston static void 154877cf210SMark Johnston splice_init(struct splice *sp, int fd, off_t max, struct timeval *tv) 155877cf210SMark Johnston { 156877cf210SMark Johnston memset(sp, 0, sizeof(*sp)); 157877cf210SMark Johnston sp->sp_fd = fd; 158877cf210SMark Johnston sp->sp_max = max; 159877cf210SMark Johnston if (tv != NULL) 160877cf210SMark Johnston sp->sp_idle = *tv; 161877cf210SMark Johnston else 162877cf210SMark Johnston sp->sp_idle.tv_sec = sp->sp_idle.tv_usec = 0; 163877cf210SMark Johnston } 164877cf210SMark Johnston 165877cf210SMark Johnston static void 166877cf210SMark Johnston unsplice(int fd) 167877cf210SMark Johnston { 168877cf210SMark Johnston struct splice sp; 169877cf210SMark Johnston int error; 170877cf210SMark Johnston 171877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 172877cf210SMark Johnston error = setsockopt(fd, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 173877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 174877cf210SMark Johnston } 175877cf210SMark Johnston 176877cf210SMark Johnston static void 177877cf210SMark Johnston unsplice_pair(int fd1, int fd2) 178877cf210SMark Johnston { 179877cf210SMark Johnston unsplice(fd1); 180877cf210SMark Johnston unsplice(fd2); 181877cf210SMark Johnston } 182877cf210SMark Johnston 183877cf210SMark Johnston static void 184877cf210SMark Johnston splice_pair(int fd1, int fd2, off_t max, struct timeval *tv) 185877cf210SMark Johnston { 186877cf210SMark Johnston struct splice sp; 187877cf210SMark Johnston int error; 188877cf210SMark Johnston 189877cf210SMark Johnston splice_init(&sp, fd1, max, tv); 190877cf210SMark Johnston error = setsockopt(fd2, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 191877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 192877cf210SMark Johnston 193877cf210SMark Johnston splice_init(&sp, fd2, max, tv); 194877cf210SMark Johnston error = setsockopt(fd1, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 195877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 196877cf210SMark Johnston } 197877cf210SMark Johnston 198877cf210SMark Johnston /* 199877cf210SMark Johnston * A structure representing a spliced pair of connections. left[1] is 200877cf210SMark Johnston * bidirectionally spliced with right[0]. 201877cf210SMark Johnston */ 202877cf210SMark Johnston struct splice_conn { 203877cf210SMark Johnston int left[2]; 204877cf210SMark Johnston int right[2]; 205877cf210SMark Johnston }; 206877cf210SMark Johnston 207877cf210SMark Johnston /* 208877cf210SMark Johnston * Initialize a splice connection with the given maximum number of bytes to 209877cf210SMark Johnston * splice and the given idle timeout. For now we're forced to use TCP socket, 210877cf210SMark Johnston * but at some point it would be nice (and simpler) to use pairs of PF_LOCAL 211877cf210SMark Johnston * sockets. 212877cf210SMark Johnston */ 213877cf210SMark Johnston static void 214877cf210SMark Johnston splice_conn_init_limits(struct splice_conn *sc, off_t max, struct timeval *tv) 215877cf210SMark Johnston { 216877cf210SMark Johnston memset(sc, 0, sizeof(*sc)); 217877cf210SMark Johnston tcp4_socketpair(sc->left); 218877cf210SMark Johnston tcp4_socketpair(sc->right); 219877cf210SMark Johnston splice_pair(sc->left[1], sc->right[0], max, tv); 220877cf210SMark Johnston } 221877cf210SMark Johnston 222877cf210SMark Johnston static void 223877cf210SMark Johnston splice_conn_init(struct splice_conn *sc) 224877cf210SMark Johnston { 225877cf210SMark Johnston splice_conn_init_limits(sc, 0, NULL); 226877cf210SMark Johnston } 227877cf210SMark Johnston 228877cf210SMark Johnston static void 229877cf210SMark Johnston splice_conn_check_empty(struct splice_conn *sc) 230877cf210SMark Johnston { 231877cf210SMark Johnston int data; 232877cf210SMark Johnston 233877cf210SMark Johnston data = fionread(sc->left[0]); 234877cf210SMark Johnston ATF_REQUIRE_MSG(data == 0, "unexpected data on left[0]: %d", data); 235877cf210SMark Johnston data = fionread(sc->left[1]); 236877cf210SMark Johnston ATF_REQUIRE_MSG(data == 0, "unexpected data on left[1]: %d", data); 237877cf210SMark Johnston data = fionread(sc->right[0]); 238877cf210SMark Johnston ATF_REQUIRE_MSG(data == 0, "unexpected data on right[0]: %d", data); 239877cf210SMark Johnston data = fionread(sc->right[1]); 240877cf210SMark Johnston ATF_REQUIRE_MSG(data == 0, "unexpected data on right[1]: %d", data); 241877cf210SMark Johnston } 242877cf210SMark Johnston 243877cf210SMark Johnston static void 244877cf210SMark Johnston splice_conn_fini(struct splice_conn *sc) 245877cf210SMark Johnston { 246877cf210SMark Johnston checked_close(sc->left[0]); 247877cf210SMark Johnston checked_close(sc->left[1]); 248877cf210SMark Johnston checked_close(sc->right[0]); 249877cf210SMark Johnston checked_close(sc->right[1]); 250877cf210SMark Johnston } 251877cf210SMark Johnston 252877cf210SMark Johnston static void 253877cf210SMark Johnston splice_conn_noblocking(struct splice_conn *sc) 254877cf210SMark Johnston { 255877cf210SMark Johnston noblocking(sc->left[0]); 256877cf210SMark Johnston noblocking(sc->left[1]); 257877cf210SMark Johnston noblocking(sc->right[0]); 258877cf210SMark Johnston noblocking(sc->right[1]); 259877cf210SMark Johnston } 260877cf210SMark Johnston 261877cf210SMark Johnston /* Pass a byte through a pair of spliced connections. */ 262877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_basic); 263877cf210SMark Johnston ATF_TC_BODY(splice_basic, tc) 264877cf210SMark Johnston { 265877cf210SMark Johnston struct splice_conn sc; 266877cf210SMark Johnston ssize_t n; 267877cf210SMark Johnston char c; 268877cf210SMark Johnston 269877cf210SMark Johnston splice_conn_init(&sc); 270877cf210SMark Johnston 271877cf210SMark Johnston check_nspliced(sc.left[1], 0); 272877cf210SMark Johnston check_nspliced(sc.right[0], 0); 273877cf210SMark Johnston 274877cf210SMark Johnston /* Left-to-right. */ 275877cf210SMark Johnston c = 'M'; 276877cf210SMark Johnston n = write(sc.left[0], &c, 1); 277877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 278877cf210SMark Johnston n = read(sc.right[1], &c, 1); 279877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 280877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c); 281877cf210SMark Johnston check_nspliced(sc.left[1], 1); 282877cf210SMark Johnston check_nspliced(sc.right[0], 0); 283877cf210SMark Johnston 284877cf210SMark Johnston /* Right-to-left. */ 285877cf210SMark Johnston c = 'J'; 286877cf210SMark Johnston n = write(sc.right[1], &c, 1); 287877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 288877cf210SMark Johnston n = read(sc.left[0], &c, 1); 289877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 290877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c); 291877cf210SMark Johnston check_nspliced(sc.left[1], 1); 292877cf210SMark Johnston check_nspliced(sc.right[0], 1); 293877cf210SMark Johnston 294877cf210SMark Johnston /* Unsplice and verify that the byte counts haven't changed. */ 295877cf210SMark Johnston unsplice(sc.left[1]); 296877cf210SMark Johnston unsplice(sc.right[0]); 297877cf210SMark Johnston check_nspliced(sc.left[1], 1); 298877cf210SMark Johnston check_nspliced(sc.right[0], 1); 299877cf210SMark Johnston 300877cf210SMark Johnston splice_conn_fini(&sc); 301877cf210SMark Johnston } 302877cf210SMark Johnston 303877cf210SMark Johnston static void 304877cf210SMark Johnston remove_rights(int fd, const cap_rights_t *toremove) 305877cf210SMark Johnston { 306877cf210SMark Johnston cap_rights_t rights; 307877cf210SMark Johnston int error; 308877cf210SMark Johnston 309877cf210SMark Johnston error = cap_rights_get(fd, &rights); 310877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "cap_rights_get failed: %s", 311877cf210SMark Johnston strerror(errno)); 312877cf210SMark Johnston cap_rights_remove(&rights, toremove); 313877cf210SMark Johnston error = cap_rights_limit(fd, &rights); 314877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "cap_rights_limit failed: %s", 315877cf210SMark Johnston strerror(errno)); 316877cf210SMark Johnston } 317877cf210SMark Johnston 318877cf210SMark Johnston /* 319877cf210SMark Johnston * Verify that splicing fails when the socket is missing the necessary rights. 320877cf210SMark Johnston */ 321877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_capsicum); 322877cf210SMark Johnston ATF_TC_BODY(splice_capsicum, tc) 323877cf210SMark Johnston { 324877cf210SMark Johnston struct splice sp; 325877cf210SMark Johnston cap_rights_t rights; 326877cf210SMark Johnston off_t n; 327877cf210SMark Johnston int error, left[2], right[2]; 328877cf210SMark Johnston 329877cf210SMark Johnston tcp4_socketpair(left); 330877cf210SMark Johnston tcp4_socketpair(right); 331877cf210SMark Johnston 332877cf210SMark Johnston /* 333*9743e9efSMark Johnston * Make sure that we can't splice a socket that's missing recv rights. 334877cf210SMark Johnston */ 335877cf210SMark Johnston remove_rights(left[1], cap_rights_init(&rights, CAP_RECV)); 336877cf210SMark Johnston splice_init(&sp, right[0], 0, NULL); 337877cf210SMark Johnston error = setsockopt(left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 338877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCAPABLE, error == -1); 339877cf210SMark Johnston 340877cf210SMark Johnston /* Make sure we can still splice left[1] in the other direction. */ 341877cf210SMark Johnston splice_init(&sp, left[1], 0, NULL); 342877cf210SMark Johnston error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 343877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 344877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 345877cf210SMark Johnston error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 346877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 347877cf210SMark Johnston 348877cf210SMark Johnston /* 349877cf210SMark Johnston * Now remove send rights from left[1] and verify that splicing is no 350877cf210SMark Johnston * longer possible. 351877cf210SMark Johnston */ 352877cf210SMark Johnston remove_rights(left[1], cap_rights_init(&rights, CAP_SEND)); 353877cf210SMark Johnston splice_init(&sp, left[1], 0, NULL); 354877cf210SMark Johnston error = setsockopt(right[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 355877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCAPABLE, error == -1); 356877cf210SMark Johnston 357877cf210SMark Johnston /* 358877cf210SMark Johnston * It's still ok to query the SO_SPLICE state though. 359877cf210SMark Johnston */ 360877cf210SMark Johnston n = -1; 361877cf210SMark Johnston error = getsockopt(left[1], SOL_SOCKET, SO_SPLICE, &n, 362877cf210SMark Johnston &(socklen_t){ sizeof(n) }); 363877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "getsockopt failed: %s", strerror(errno)); 364877cf210SMark Johnston ATF_REQUIRE(n == 0); 365877cf210SMark Johnston 366877cf210SMark Johnston /* 367877cf210SMark Johnston * Make sure that we can unsplice a spliced pair without any rights 368877cf210SMark Johnston * other than CAP_SETSOCKOPT. 369877cf210SMark Johnston */ 370877cf210SMark Johnston splice_pair(left[0], right[1], 0, NULL); 371877cf210SMark Johnston error = cap_rights_limit(left[0], 372877cf210SMark Johnston cap_rights_init(&rights, CAP_SETSOCKOPT)); 373877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "cap_rights_limit failed: %s", 374877cf210SMark Johnston strerror(errno)); 375877cf210SMark Johnston unsplice(left[0]); 376877cf210SMark Johnston 377877cf210SMark Johnston checked_close(left[0]); 378877cf210SMark Johnston checked_close(left[1]); 379877cf210SMark Johnston checked_close(right[0]); 380877cf210SMark Johnston checked_close(right[1]); 381877cf210SMark Johnston } 382877cf210SMark Johnston 383877cf210SMark Johnston /* 384877cf210SMark Johnston * Check various error cases in splice configuration. 385877cf210SMark Johnston */ 386877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_error); 387877cf210SMark Johnston ATF_TC_BODY(splice_error, tc) 388877cf210SMark Johnston { 389877cf210SMark Johnston struct splice_conn sc; 390877cf210SMark Johnston struct splice sp; 391877cf210SMark Johnston char path[PATH_MAX]; 392877cf210SMark Johnston int error, fd, sd, usd[2]; 393877cf210SMark Johnston 394877cf210SMark Johnston memset(&sc, 0, sizeof(sc)); 395877cf210SMark Johnston tcp4_socketpair(sc.left); 396877cf210SMark Johnston tcp4_socketpair(sc.right); 397877cf210SMark Johnston 398877cf210SMark Johnston /* A negative byte limit is invalid. */ 399877cf210SMark Johnston splice_init(&sp, sc.right[0], -3, NULL); 400877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 401877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 402877cf210SMark Johnston 403877cf210SMark Johnston /* Can't unsplice a never-spliced socket. */ 404877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 405877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 406877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 407877cf210SMark Johnston 408877cf210SMark Johnston /* Can't double-unsplice a socket. */ 409877cf210SMark Johnston splice_init(&sp, sc.right[0], 0, NULL); 410877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 411877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 412877cf210SMark Johnston unsplice(sc.left[1]); 413877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 414877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 415877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 416877cf210SMark Johnston 417877cf210SMark Johnston /* Can't splice a spliced socket */ 418877cf210SMark Johnston splice_init(&sp, sc.right[0], 0, NULL); 419877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 420877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 421877cf210SMark Johnston splice_init(&sp, sc.right[1], 0, NULL); 422877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 423877cf210SMark Johnston ATF_REQUIRE_ERRNO(EBUSY, error == -1); 424877cf210SMark Johnston splice_init(&sp, sc.right[0], 0, NULL); 425877cf210SMark Johnston error = setsockopt(sc.left[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 426877cf210SMark Johnston ATF_REQUIRE_ERRNO(EBUSY, error == -1); 427877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 428877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 429877cf210SMark Johnston 430877cf210SMark Johnston /* Can't splice to a non-socket. */ 431877cf210SMark Johnston snprintf(path, sizeof(path), "/tmp/splice_error.XXXXXX"); 432877cf210SMark Johnston fd = mkstemp(path); 433877cf210SMark Johnston ATF_REQUIRE_MSG(fd >= 0, "mkstemp failed: %s", strerror(errno)); 434877cf210SMark Johnston splice_init(&sp, fd, 0, NULL); 435877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 436877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1); 437877cf210SMark Johnston 438877cf210SMark Johnston /* Can't splice to an invalid fd. */ 439877cf210SMark Johnston checked_close(fd); 440877cf210SMark Johnston splice_init(&sp, fd, 0, NULL); 441877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 442877cf210SMark Johnston ATF_REQUIRE_ERRNO(EBADF, error == -1); 443877cf210SMark Johnston 444877cf210SMark Johnston /* Can't splice a unix stream socket. */ 445877cf210SMark Johnston error = socketpair(AF_UNIX, SOCK_STREAM, 0, usd); 446877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "socketpair failed: %s", strerror(errno)); 447877cf210SMark Johnston splice_init(&sp, usd[0], 0, NULL); 448877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 449877cf210SMark Johnston ATF_REQUIRE_ERRNO(EPROTONOSUPPORT, error == -1); 450877cf210SMark Johnston error = setsockopt(usd[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 451877cf210SMark Johnston ATF_REQUIRE_ERRNO(EPROTONOSUPPORT, error == -1); 452877cf210SMark Johnston checked_close(usd[0]); 453877cf210SMark Johnston checked_close(usd[1]); 454877cf210SMark Johnston 455877cf210SMark Johnston /* Can't splice an unconnected TCP socket. */ 456877cf210SMark Johnston sd = socket(PF_INET, SOCK_STREAM, 0); 457877cf210SMark Johnston ATF_REQUIRE_MSG(sd >= 0, "socket failed: %s", strerror(errno)); 458877cf210SMark Johnston splice_init(&sp, sd, 0, NULL); 459877cf210SMark Johnston error = setsockopt(sc.left[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 460877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 461877cf210SMark Johnston splice_init(&sp, sc.right[0], 0, NULL); 462877cf210SMark Johnston error = setsockopt(sd, SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 463877cf210SMark Johnston ATF_REQUIRE_ERRNO(ENOTCONN, error == -1); 464877cf210SMark Johnston 465877cf210SMark Johnston splice_conn_fini(&sc); 466877cf210SMark Johnston } 467877cf210SMark Johnston 468877cf210SMark Johnston /* 469877cf210SMark Johnston * Make sure that kevent() doesn't report read I/O events on spliced sockets. 470877cf210SMark Johnston */ 471877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_kevent); 472877cf210SMark Johnston ATF_TC_BODY(splice_kevent, tc) 473877cf210SMark Johnston { 474877cf210SMark Johnston struct splice_conn sc; 475877cf210SMark Johnston struct kevent kev; 476877cf210SMark Johnston struct timespec ts; 477877cf210SMark Johnston ssize_t n; 478877cf210SMark Johnston int error, nev, kq; 479877cf210SMark Johnston uint8_t b; 480877cf210SMark Johnston 481877cf210SMark Johnston splice_conn_init(&sc); 482877cf210SMark Johnston 483877cf210SMark Johnston kq = kqueue(); 484877cf210SMark Johnston ATF_REQUIRE_MSG(kq >= 0, "kqueue failed: %s", strerror(errno)); 485877cf210SMark Johnston 486877cf210SMark Johnston EV_SET(&kev, sc.left[1], EVFILT_READ, EV_ADD, 0, 0, NULL); 487877cf210SMark Johnston error = kevent(kq, &kev, 1, NULL, 0, NULL); 488877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "kevent failed: %s", strerror(errno)); 489877cf210SMark Johnston 490877cf210SMark Johnston memset(&ts, 0, sizeof(ts)); 491877cf210SMark Johnston nev = kevent(kq, NULL, 0, &kev, 1, &ts); 492877cf210SMark Johnston ATF_REQUIRE_MSG(nev >= 0, "kevent failed: %s", strerror(errno)); 493877cf210SMark Johnston ATF_REQUIRE(nev == 0); 494877cf210SMark Johnston 495877cf210SMark Johnston b = 'M'; 496877cf210SMark Johnston n = write(sc.left[0], &b, 1); 497877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 498877cf210SMark Johnston n = read(sc.right[1], &b, 1); 499877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 500877cf210SMark Johnston ATF_REQUIRE(b == 'M'); 501877cf210SMark Johnston 502877cf210SMark Johnston nev = kevent(kq, NULL, 0, &kev, 1, &ts); 503877cf210SMark Johnston ATF_REQUIRE_MSG(nev >= 0, "kevent failed: %s", strerror(errno)); 504877cf210SMark Johnston ATF_REQUIRE(nev == 0); 505877cf210SMark Johnston 506877cf210SMark Johnston b = 'J'; 507877cf210SMark Johnston n = write(sc.right[1], &b, 1); 508877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 509877cf210SMark Johnston n = read(sc.left[0], &b, 1); 510877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 511877cf210SMark Johnston ATF_REQUIRE(b == 'J'); 512877cf210SMark Johnston 513877cf210SMark Johnston splice_conn_fini(&sc); 514877cf210SMark Johnston checked_close(kq); 515877cf210SMark Johnston } 516877cf210SMark Johnston 517877cf210SMark Johnston /* 518877cf210SMark Johnston * Verify that a splice byte limit is applied. 519877cf210SMark Johnston */ 520877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_limit_bytes); 521877cf210SMark Johnston ATF_TC_BODY(splice_limit_bytes, tc) 522877cf210SMark Johnston { 523877cf210SMark Johnston struct splice_conn sc; 524877cf210SMark Johnston ssize_t n; 525877cf210SMark Johnston uint8_t b, buf[128]; 526877cf210SMark Johnston 527877cf210SMark Johnston splice_conn_init_limits(&sc, sizeof(buf) + 1, NULL); 528877cf210SMark Johnston 529877cf210SMark Johnston memset(buf, 'A', sizeof(buf)); 530877cf210SMark Johnston for (size_t total = sizeof(buf); total > 0; total -= n) { 531877cf210SMark Johnston n = write(sc.left[0], buf, total); 532877cf210SMark Johnston ATF_REQUIRE_MSG(n > 0, "write failed: %s", strerror(errno)); 533877cf210SMark Johnston } 534877cf210SMark Johnston for (size_t total = sizeof(buf); total > 0; total -= n) { 535877cf210SMark Johnston n = read(sc.right[1], buf, sizeof(buf)); 536877cf210SMark Johnston ATF_REQUIRE_MSG(n > 0, "read failed: %s", strerror(errno)); 537877cf210SMark Johnston } 538877cf210SMark Johnston 539877cf210SMark Johnston check_nspliced(sc.left[1], sizeof(buf)); 540877cf210SMark Johnston check_nspliced(sc.right[0], 0); 541877cf210SMark Johnston 542877cf210SMark Johnston /* Trigger an unsplice by writing the last byte. */ 543877cf210SMark Johnston b = 'B'; 544877cf210SMark Johnston n = write(sc.left[0], &b, 1); 545877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 546877cf210SMark Johnston n = read(sc.right[1], &b, 1); 547877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 548877cf210SMark Johnston ATF_REQUIRE(b == 'B'); 549877cf210SMark Johnston 550877cf210SMark Johnston /* 551877cf210SMark Johnston * The next byte should appear on the other side of the connection 552877cf210SMark Johnston * rather than the splice. 553877cf210SMark Johnston */ 554877cf210SMark Johnston b = 'C'; 555877cf210SMark Johnston n = write(sc.left[0], &b, 1); 556877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 557877cf210SMark Johnston n = read(sc.left[1], &b, 1); 558877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 559877cf210SMark Johnston ATF_REQUIRE(b == 'C'); 560877cf210SMark Johnston 561877cf210SMark Johnston splice_conn_check_empty(&sc); 562877cf210SMark Johnston 563877cf210SMark Johnston splice_conn_fini(&sc); 564877cf210SMark Johnston } 565877cf210SMark Johnston 566877cf210SMark Johnston /* 567877cf210SMark Johnston * Verify that a splice timeout limit is applied. 568877cf210SMark Johnston */ 569877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_limit_timeout); 570877cf210SMark Johnston ATF_TC_BODY(splice_limit_timeout, tc) 571877cf210SMark Johnston { 572877cf210SMark Johnston struct splice_conn sc; 573877cf210SMark Johnston ssize_t n; 574877cf210SMark Johnston int error; 575877cf210SMark Johnston uint8_t b, buf[128]; 576877cf210SMark Johnston 577877cf210SMark Johnston splice_conn_init_limits(&sc, 0, 578877cf210SMark Johnston &(struct timeval){ .tv_sec = 0, .tv_usec = 500000 /* 500ms */ }); 579877cf210SMark Johnston 580877cf210SMark Johnston /* Write some data through the splice. */ 581877cf210SMark Johnston memset(buf, 'A', sizeof(buf)); 582877cf210SMark Johnston for (size_t total = sizeof(buf); total > 0; total -= n) { 583877cf210SMark Johnston n = write(sc.left[0], buf, total); 584877cf210SMark Johnston ATF_REQUIRE_MSG(n > 0, "write failed: %s", strerror(errno)); 585877cf210SMark Johnston } 586877cf210SMark Johnston for (size_t total = sizeof(buf); total > 0; total -= n) { 587877cf210SMark Johnston n = read(sc.right[1], buf, sizeof(buf)); 588877cf210SMark Johnston ATF_REQUIRE_MSG(n > 0, "read failed: %s", strerror(errno)); 589877cf210SMark Johnston } 590877cf210SMark Johnston 591877cf210SMark Johnston check_nspliced(sc.left[1], sizeof(buf)); 592877cf210SMark Johnston check_nspliced(sc.right[0], 0); 593877cf210SMark Johnston 594877cf210SMark Johnston /* Wait for the splice to time out. */ 595877cf210SMark Johnston error = usleep(550000); 596877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "usleep failed: %s", strerror(errno)); 597877cf210SMark Johnston 598877cf210SMark Johnston /* 599877cf210SMark Johnston * The next byte should appear on the other side of the connection 600877cf210SMark Johnston * rather than the splice. 601877cf210SMark Johnston */ 602877cf210SMark Johnston b = 'C'; 603877cf210SMark Johnston n = write(sc.left[0], &b, 1); 604877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 605877cf210SMark Johnston n = read(sc.left[1], &b, 1); 606877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 607877cf210SMark Johnston ATF_REQUIRE(b == 'C'); 608877cf210SMark Johnston 609877cf210SMark Johnston splice_conn_fini(&sc); 610877cf210SMark Johnston } 611877cf210SMark Johnston 612877cf210SMark Johnston /* 613877cf210SMark Johnston * Make sure that listen() fails on spliced sockets, and that SO_SPLICE can't be 614877cf210SMark Johnston * used with listening sockets. 615877cf210SMark Johnston */ 616877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_listen); 617877cf210SMark Johnston ATF_TC_BODY(splice_listen, tc) 618877cf210SMark Johnston { 619877cf210SMark Johnston struct splice sp; 620877cf210SMark Johnston struct splice_conn sc; 621877cf210SMark Johnston int error, sd[3]; 622877cf210SMark Johnston 623877cf210SMark Johnston /* 624877cf210SMark Johnston * These should fail regardless since the sockets are connected, but it 625877cf210SMark Johnston * doesn't hurt to check. 626877cf210SMark Johnston */ 627877cf210SMark Johnston splice_conn_init(&sc); 628877cf210SMark Johnston error = listen(sc.left[1], 1); 629877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 630877cf210SMark Johnston error = listen(sc.right[0], 1); 631877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 632877cf210SMark Johnston splice_conn_fini(&sc); 633877cf210SMark Johnston 634877cf210SMark Johnston tcp4_socketpair(sd); 635877cf210SMark Johnston sd[2] = socket(PF_INET, SOCK_STREAM, 0); 636877cf210SMark Johnston ATF_REQUIRE_MSG(sd[2] >= 0, "socket failed: %s", strerror(errno)); 637877cf210SMark Johnston error = listen(sd[2], 1); 638877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno)); 639877cf210SMark Johnston 640877cf210SMark Johnston /* 641877cf210SMark Johnston * Make sure a listening socket can't be spliced in either direction. 642877cf210SMark Johnston */ 643877cf210SMark Johnston splice_init(&sp, sd[2], 0, NULL); 644877cf210SMark Johnston error = setsockopt(sd[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 645877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 646877cf210SMark Johnston splice_init(&sp, sd[1], 0, NULL); 647877cf210SMark Johnston error = setsockopt(sd[2], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 648877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 649877cf210SMark Johnston 650877cf210SMark Johnston /* 651877cf210SMark Johnston * Make sure we can't try to unsplice a listening socket. 652877cf210SMark Johnston */ 653877cf210SMark Johnston splice_init(&sp, -1, 0, NULL); 654877cf210SMark Johnston error = setsockopt(sd[2], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 655877cf210SMark Johnston ATF_REQUIRE_ERRNO(EINVAL, error == -1); 656877cf210SMark Johnston 657877cf210SMark Johnston checked_close(sd[0]); 658877cf210SMark Johnston checked_close(sd[1]); 659877cf210SMark Johnston checked_close(sd[2]); 660877cf210SMark Johnston } 661877cf210SMark Johnston 662877cf210SMark Johnston static void 663877cf210SMark Johnston sigalarm(int sig __unused) 664877cf210SMark Johnston { 665877cf210SMark Johnston } 666877cf210SMark Johnston 667877cf210SMark Johnston /* 668877cf210SMark Johnston * Our SO_SPLICE implementation doesn't do anything to prevent loops. We should 669877cf210SMark Johnston * however make sure that they are interruptible. 670877cf210SMark Johnston */ 671877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_loop); 672877cf210SMark Johnston ATF_TC_BODY(splice_loop, tc) 673877cf210SMark Johnston { 674877cf210SMark Johnston ssize_t n; 675877cf210SMark Johnston int sd[2], status; 676877cf210SMark Johnston pid_t child; 677877cf210SMark Johnston char c; 678877cf210SMark Johnston 679877cf210SMark Johnston tcp_socketpair(sd, PF_INET); 680877cf210SMark Johnston splice_pair(sd[0], sd[1], 0, NULL); 681877cf210SMark Johnston 682877cf210SMark Johnston /* 683877cf210SMark Johnston * Let the child process trigger an infinite loop. It should still be 684877cf210SMark Johnston * possible to kill the child with a signal, causing the connection to 685877cf210SMark Johnston * be dropped and ending the loop. 686877cf210SMark Johnston */ 687877cf210SMark Johnston child = fork(); 688877cf210SMark Johnston ATF_REQUIRE_MSG(child >= 0, "fork failed: %s", strerror(errno)); 689877cf210SMark Johnston if (child == 0) { 690877cf210SMark Johnston alarm(2); 691877cf210SMark Johnston c = 42; 692877cf210SMark Johnston n = write(sd[0], &c, 1); 693877cf210SMark Johnston if (n != 1) 694877cf210SMark Johnston _exit(2); 695877cf210SMark Johnston c = 24; 696877cf210SMark Johnston n = write(sd[1], &c, 1); 697877cf210SMark Johnston if (n != 1) 698877cf210SMark Johnston _exit(3); 699877cf210SMark Johnston 700877cf210SMark Johnston for (;;) { 701877cf210SMark Johnston /* Wait for SIGALARM. */ 702877cf210SMark Johnston sleep(100); 703877cf210SMark Johnston } 704877cf210SMark Johnston 705877cf210SMark Johnston _exit(0); 706877cf210SMark Johnston } else { 707877cf210SMark Johnston checked_close(sd[0]); 708877cf210SMark Johnston checked_close(sd[1]); 709877cf210SMark Johnston 710877cf210SMark Johnston child = waitpid(child, &status, 0); 711877cf210SMark Johnston ATF_REQUIRE_MSG(child >= 0, 712877cf210SMark Johnston "waitpid failed: %s", strerror(errno)); 713877cf210SMark Johnston ATF_REQUIRE(WIFSIGNALED(status)); 714877cf210SMark Johnston ATF_REQUIRE(WTERMSIG(status) == SIGALRM); 715877cf210SMark Johnston } 716877cf210SMark Johnston } 717877cf210SMark Johnston 718877cf210SMark Johnston /* 719877cf210SMark Johnston * Simple I/O test. 720877cf210SMark Johnston */ 721877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_nonblock); 722877cf210SMark Johnston ATF_TC_BODY(splice_nonblock, tc) 723877cf210SMark Johnston { 724877cf210SMark Johnston struct splice_conn sc; 725877cf210SMark Johnston char buf[200]; 726877cf210SMark Johnston size_t sofar; 727877cf210SMark Johnston ssize_t n; 728877cf210SMark Johnston 729877cf210SMark Johnston splice_conn_init(&sc); 730877cf210SMark Johnston splice_conn_noblocking(&sc); 731877cf210SMark Johnston 732877cf210SMark Johnston memset(buf, 'A', sizeof(buf)); 733877cf210SMark Johnston for (sofar = 0;;) { 734877cf210SMark Johnston n = write(sc.left[0], buf, sizeof(buf)); 735877cf210SMark Johnston if (n < 0) { 736877cf210SMark Johnston ATF_REQUIRE_ERRNO(EAGAIN, n == -1); 737877cf210SMark Johnston break; 738877cf210SMark Johnston } 739877cf210SMark Johnston sofar += n; 740877cf210SMark Johnston } 741877cf210SMark Johnston 742877cf210SMark Johnston while (sofar > 0) { 743877cf210SMark Johnston n = read(sc.right[1], buf, sizeof(buf)); 744877cf210SMark Johnston if (n < 0) { 745877cf210SMark Johnston ATF_REQUIRE_ERRNO(EAGAIN, n == -1); 746877cf210SMark Johnston usleep(100); 747877cf210SMark Johnston } else { 748877cf210SMark Johnston for (size_t i = 0; i < (size_t)n; i++) 749877cf210SMark Johnston ATF_REQUIRE(buf[i] == 'A'); 750877cf210SMark Johnston sofar -= n; 751877cf210SMark Johnston } 752877cf210SMark Johnston } 753877cf210SMark Johnston 754877cf210SMark Johnston splice_conn_fini(&sc); 755877cf210SMark Johnston } 756877cf210SMark Johnston 757877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_resplice); 758877cf210SMark Johnston ATF_TC_BODY(splice_resplice, tc) 759877cf210SMark Johnston { 760877cf210SMark Johnston struct splice_conn sc; 761877cf210SMark Johnston ssize_t n; 762877cf210SMark Johnston char c; 763877cf210SMark Johnston 764877cf210SMark Johnston splice_conn_init(&sc); 765877cf210SMark Johnston 766877cf210SMark Johnston /* Left-to-right. */ 767877cf210SMark Johnston c = 'M'; 768877cf210SMark Johnston n = write(sc.left[0], &c, 1); 769877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 770877cf210SMark Johnston n = read(sc.right[1], &c, 1); 771877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 772877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c); 773877cf210SMark Johnston check_nspliced(sc.left[1], 1); 774877cf210SMark Johnston check_nspliced(sc.right[0], 0); 775877cf210SMark Johnston 776877cf210SMark Johnston /* Right-to-left. */ 777877cf210SMark Johnston c = 'J'; 778877cf210SMark Johnston n = write(sc.right[1], &c, 1); 779877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 780877cf210SMark Johnston n = read(sc.left[0], &c, 1); 781877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 782877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c); 783877cf210SMark Johnston check_nspliced(sc.left[1], 1); 784877cf210SMark Johnston check_nspliced(sc.right[0], 1); 785877cf210SMark Johnston 786877cf210SMark Johnston /* Unsplice and verify that the byte counts haven't changed. */ 787877cf210SMark Johnston unsplice(sc.left[1]); 788877cf210SMark Johnston unsplice(sc.right[0]); 789877cf210SMark Johnston check_nspliced(sc.left[1], 1); 790877cf210SMark Johnston check_nspliced(sc.right[0], 1); 791877cf210SMark Johnston 792877cf210SMark Johnston /* Splice again, check that byte counts are reset. */ 793877cf210SMark Johnston splice_pair(sc.left[1], sc.right[0], 0, NULL); 794877cf210SMark Johnston check_nspliced(sc.left[1], 0); 795877cf210SMark Johnston check_nspliced(sc.right[0], 0); 796877cf210SMark Johnston 797877cf210SMark Johnston /* Left-to-right. */ 798877cf210SMark Johnston c = 'M'; 799877cf210SMark Johnston n = write(sc.left[0], &c, 1); 800877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 801877cf210SMark Johnston n = read(sc.right[1], &c, 1); 802877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 803877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'M', "unexpected character: %c", c); 804877cf210SMark Johnston check_nspliced(sc.left[1], 1); 805877cf210SMark Johnston check_nspliced(sc.right[0], 0); 806877cf210SMark Johnston 807877cf210SMark Johnston /* Right-to-left. */ 808877cf210SMark Johnston c = 'J'; 809877cf210SMark Johnston n = write(sc.right[1], &c, 1); 810877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 811877cf210SMark Johnston n = read(sc.left[0], &c, 1); 812877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 813877cf210SMark Johnston ATF_REQUIRE_MSG(c == 'J', "unexpected character: %c", c); 814877cf210SMark Johnston check_nspliced(sc.left[1], 1); 815877cf210SMark Johnston check_nspliced(sc.right[0], 1); 816877cf210SMark Johnston 817877cf210SMark Johnston splice_conn_fini(&sc); 818877cf210SMark Johnston } 819877cf210SMark Johnston 820877cf210SMark Johnston struct xfer_args { 821877cf210SMark Johnston pthread_barrier_t *barrier; 822877cf210SMark Johnston uint32_t bytes; 823877cf210SMark Johnston int fd; 824877cf210SMark Johnston }; 825877cf210SMark Johnston 826877cf210SMark Johnston static void * 827877cf210SMark Johnston xfer(void *arg) 828877cf210SMark Johnston { 829877cf210SMark Johnston struct xfer_args *xfer; 830877cf210SMark Johnston uint8_t *buf; 831877cf210SMark Johnston size_t sz; 832877cf210SMark Johnston ssize_t n; 833877cf210SMark Johnston uint32_t resid; 834877cf210SMark Johnston int error; 835877cf210SMark Johnston 836877cf210SMark Johnston xfer = arg; 837877cf210SMark Johnston 838877cf210SMark Johnston error = fcntl(xfer->fd, F_SETFL, O_NONBLOCK); 839877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "fcntl failed: %s", strerror(errno)); 840877cf210SMark Johnston 841877cf210SMark Johnston sz = MIN(xfer->bytes, 1024 * 1024); 842877cf210SMark Johnston buf = malloc(sz); 843877cf210SMark Johnston ATF_REQUIRE(buf != NULL); 844877cf210SMark Johnston arc4random_buf(buf, sz); 845877cf210SMark Johnston 846877cf210SMark Johnston pthread_barrier_wait(xfer->barrier); 847877cf210SMark Johnston 848877cf210SMark Johnston for (resid = xfer->bytes; xfer->bytes > 0 || resid > 0;) { 849877cf210SMark Johnston n = write(xfer->fd, buf, MIN(sz, xfer->bytes)); 850877cf210SMark Johnston if (n < 0) { 851877cf210SMark Johnston ATF_REQUIRE_ERRNO(EAGAIN, n == -1); 852877cf210SMark Johnston usleep(1000); 853877cf210SMark Johnston } else { 854877cf210SMark Johnston ATF_REQUIRE(xfer->bytes >= (size_t)n); 855877cf210SMark Johnston xfer->bytes -= n; 856877cf210SMark Johnston } 857877cf210SMark Johnston 858877cf210SMark Johnston n = read(xfer->fd, buf, sz); 859877cf210SMark Johnston if (n < 0) { 860877cf210SMark Johnston ATF_REQUIRE_ERRNO(EAGAIN, n == -1); 861877cf210SMark Johnston usleep(1000); 862877cf210SMark Johnston } else { 863877cf210SMark Johnston ATF_REQUIRE(resid >= (size_t)n); 864877cf210SMark Johnston resid -= n; 865877cf210SMark Johnston } 866877cf210SMark Johnston } 867877cf210SMark Johnston 868877cf210SMark Johnston free(buf); 869877cf210SMark Johnston return (NULL); 870877cf210SMark Johnston } 871877cf210SMark Johnston 872877cf210SMark Johnston /* 873877cf210SMark Johnston * Use two threads to transfer data between two spliced connections. 874877cf210SMark Johnston */ 875877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_throughput); 876877cf210SMark Johnston ATF_TC_BODY(splice_throughput, tc) 877877cf210SMark Johnston { 878877cf210SMark Johnston struct xfer_args xfers[2]; 879877cf210SMark Johnston pthread_t thread[2]; 880877cf210SMark Johnston pthread_barrier_t barrier; 881877cf210SMark Johnston struct splice_conn sc; 882877cf210SMark Johnston uint32_t bytes; 883877cf210SMark Johnston int error; 884877cf210SMark Johnston 885877cf210SMark Johnston /* Transfer an amount between 1B and 1GB. */ 886877cf210SMark Johnston bytes = arc4random_uniform(1024 * 1024 * 1024) + 1; 887877cf210SMark Johnston splice_conn_init(&sc); 888877cf210SMark Johnston 889877cf210SMark Johnston error = pthread_barrier_init(&barrier, NULL, 2); 890877cf210SMark Johnston ATF_REQUIRE(error == 0); 891877cf210SMark Johnston xfers[0] = (struct xfer_args){ 892877cf210SMark Johnston .barrier = &barrier, 893877cf210SMark Johnston .bytes = bytes, 894877cf210SMark Johnston .fd = sc.left[0] 895877cf210SMark Johnston }; 896877cf210SMark Johnston xfers[1] = (struct xfer_args){ 897877cf210SMark Johnston .barrier = &barrier, 898877cf210SMark Johnston .bytes = bytes, 899877cf210SMark Johnston .fd = sc.right[1] 900877cf210SMark Johnston }; 901877cf210SMark Johnston 902877cf210SMark Johnston error = pthread_create(&thread[0], NULL, xfer, &xfers[0]); 903877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, 904877cf210SMark Johnston "pthread_create failed: %s", strerror(errno)); 905877cf210SMark Johnston error = pthread_create(&thread[1], NULL, xfer, &xfers[1]); 906877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, 907877cf210SMark Johnston "pthread_create failed: %s", strerror(errno)); 908877cf210SMark Johnston 909877cf210SMark Johnston error = pthread_join(thread[0], NULL); 910877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, 911877cf210SMark Johnston "pthread_join failed: %s", strerror(errno)); 912877cf210SMark Johnston error = pthread_join(thread[1], NULL); 913877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, 914877cf210SMark Johnston "pthread_join failed: %s", strerror(errno)); 915877cf210SMark Johnston 916877cf210SMark Johnston error = pthread_barrier_destroy(&barrier); 917877cf210SMark Johnston ATF_REQUIRE(error == 0); 918877cf210SMark Johnston splice_conn_fini(&sc); 919877cf210SMark Johnston } 920877cf210SMark Johnston 921877cf210SMark Johnston /* 922877cf210SMark Johnston * Make sure it's possible to splice v4 and v6 sockets together. 923877cf210SMark Johnston */ 924877cf210SMark Johnston ATF_TC_WITHOUT_HEAD(splice_v4v6); 925877cf210SMark Johnston ATF_TC_BODY(splice_v4v6, tc) 926877cf210SMark Johnston { 927877cf210SMark Johnston struct splice sp; 928877cf210SMark Johnston ssize_t n; 929877cf210SMark Johnston int sd4[2], sd6[2]; 930877cf210SMark Johnston int error; 931877cf210SMark Johnston uint8_t b; 932877cf210SMark Johnston 933877cf210SMark Johnston tcp4_socketpair(sd4); 934877cf210SMark Johnston tcp6_socketpair(sd6); 935877cf210SMark Johnston 936877cf210SMark Johnston splice_init(&sp, sd6[0], 0, NULL); 937877cf210SMark Johnston error = setsockopt(sd4[1], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 938877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 939877cf210SMark Johnston 940877cf210SMark Johnston splice_init(&sp, sd4[1], 0, NULL); 941877cf210SMark Johnston error = setsockopt(sd6[0], SOL_SOCKET, SO_SPLICE, &sp, sizeof(sp)); 942877cf210SMark Johnston ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno)); 943877cf210SMark Johnston 944877cf210SMark Johnston b = 'M'; 945877cf210SMark Johnston n = write(sd4[0], &b, 1); 946877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 947877cf210SMark Johnston n = read(sd6[1], &b, 1); 948877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 949877cf210SMark Johnston ATF_REQUIRE(b == 'M'); 950877cf210SMark Johnston 951877cf210SMark Johnston b = 'J'; 952877cf210SMark Johnston n = write(sd6[1], &b, 1); 953877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "write failed: %s", strerror(errno)); 954877cf210SMark Johnston n = read(sd4[0], &b, 1); 955877cf210SMark Johnston ATF_REQUIRE_MSG(n == 1, "read failed: %s", strerror(errno)); 956877cf210SMark Johnston ATF_REQUIRE(b == 'J'); 957877cf210SMark Johnston 958877cf210SMark Johnston checked_close(sd4[0]); 959877cf210SMark Johnston checked_close(sd4[1]); 960877cf210SMark Johnston checked_close(sd6[0]); 961877cf210SMark Johnston checked_close(sd6[1]); 962877cf210SMark Johnston } 963877cf210SMark Johnston 964877cf210SMark Johnston ATF_TP_ADD_TCS(tp) 965877cf210SMark Johnston { 966877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_basic); 967877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_capsicum); 968877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_error); 969877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_kevent); 970877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_limit_bytes); 971877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_limit_timeout); 972877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_listen); 973877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_loop); 974877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_nonblock); 975877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_resplice); 976877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_throughput); 977877cf210SMark Johnston ATF_TP_ADD_TC(tp, splice_v4v6); 978877cf210SMark Johnston return (atf_no_error()); 979877cf210SMark Johnston } 980