xref: /freebsd-src/tests/sys/kern/socket_splice.c (revision 9743e9efdf5f0d2338d7cfeed8f09d89d889bac4)
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