xref: /freebsd/tests/sys/kern/socket_accf.c (revision 19307b86)
1e87ff1eaSGleb Smirnoff /*-
2e87ff1eaSGleb Smirnoff  * SPDX-License-Identifier: BSD-2-Clause
3e87ff1eaSGleb Smirnoff  *
4c68eed82SGleb Smirnoff  * Copyright (c) 2022-2024 Gleb Smirnoff <glebius@FreeBSD.org>
5e87ff1eaSGleb Smirnoff  *
6e87ff1eaSGleb Smirnoff  * Redistribution and use in source and binary forms, with or without
7e87ff1eaSGleb Smirnoff  * modification, are permitted provided that the following conditions
8e87ff1eaSGleb Smirnoff  * are met:
9e87ff1eaSGleb Smirnoff  * 1. Redistributions of source code must retain the above copyright
10e87ff1eaSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer.
11e87ff1eaSGleb Smirnoff  * 2. Redistributions in binary form must reproduce the above copyright
12e87ff1eaSGleb Smirnoff  *    notice, this list of conditions and the following disclaimer in the
13e87ff1eaSGleb Smirnoff  *    documentation and/or other materials provided with the distribution.
14e87ff1eaSGleb Smirnoff  *
15e87ff1eaSGleb Smirnoff  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e87ff1eaSGleb Smirnoff  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e87ff1eaSGleb Smirnoff  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e87ff1eaSGleb Smirnoff  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19e87ff1eaSGleb Smirnoff  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e87ff1eaSGleb Smirnoff  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e87ff1eaSGleb Smirnoff  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e87ff1eaSGleb Smirnoff  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e87ff1eaSGleb Smirnoff  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e87ff1eaSGleb Smirnoff  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e87ff1eaSGleb Smirnoff  * SUCH DAMAGE.
26e87ff1eaSGleb Smirnoff  */
27e87ff1eaSGleb Smirnoff 
28e87ff1eaSGleb Smirnoff #include <sys/socket.h>
29e87ff1eaSGleb Smirnoff #include <netinet/in.h>
30e87ff1eaSGleb Smirnoff #include <errno.h>
31e87ff1eaSGleb Smirnoff #include <fcntl.h>
32c68eed82SGleb Smirnoff #include <stdlib.h>
33e87ff1eaSGleb Smirnoff 
34e87ff1eaSGleb Smirnoff #include <atf-c.h>
35e87ff1eaSGleb Smirnoff 
36e87ff1eaSGleb Smirnoff static int
listensock(struct sockaddr_in * sin)37e87ff1eaSGleb Smirnoff listensock(struct sockaddr_in *sin)
38e87ff1eaSGleb Smirnoff {
39e87ff1eaSGleb Smirnoff 	int l;
40e87ff1eaSGleb Smirnoff 
41e87ff1eaSGleb Smirnoff 	ATF_REQUIRE((l = socket(PF_INET, SOCK_STREAM, 0)) > 0);
42e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(fcntl(l, F_SETFL, O_NONBLOCK) != -1);
43e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
44e87ff1eaSGleb Smirnoff 	    sizeof(int)) == 0);
45e87ff1eaSGleb Smirnoff 	*sin = (struct sockaddr_in){
46e87ff1eaSGleb Smirnoff 		.sin_len = sizeof(sin),
47e87ff1eaSGleb Smirnoff 		.sin_family = AF_INET,
48e87ff1eaSGleb Smirnoff 		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
49e87ff1eaSGleb Smirnoff 	};
50e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(bind(l, (struct sockaddr *)sin, sizeof(*sin)) == 0);
51e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(getsockname(l, (struct sockaddr *)sin,
52e87ff1eaSGleb Smirnoff 	    &(socklen_t){ sizeof(*sin) }) == 0);
53e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(listen(l, -1) == 0);
54e87ff1eaSGleb Smirnoff 
55e87ff1eaSGleb Smirnoff 	return (l);
56e87ff1eaSGleb Smirnoff }
57e87ff1eaSGleb Smirnoff 
58e87ff1eaSGleb Smirnoff static int
clientsock(struct sockaddr_in * sin)59e87ff1eaSGleb Smirnoff clientsock(struct sockaddr_in *sin)
60e87ff1eaSGleb Smirnoff {
61e87ff1eaSGleb Smirnoff 	int s;
62e87ff1eaSGleb Smirnoff 
63e87ff1eaSGleb Smirnoff 	ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, 0)) > 0);
64e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(connect(s, (struct sockaddr *)sin, sizeof(*sin)) == 0);
65e87ff1eaSGleb Smirnoff 
66e87ff1eaSGleb Smirnoff 	return (s);
67e87ff1eaSGleb Smirnoff }
68e87ff1eaSGleb Smirnoff 
69e87ff1eaSGleb Smirnoff static void
accfon(int l,struct accept_filter_arg * af)70e87ff1eaSGleb Smirnoff accfon(int l, struct accept_filter_arg *af)
71e87ff1eaSGleb Smirnoff {
72e87ff1eaSGleb Smirnoff 
73e87ff1eaSGleb Smirnoff 	if (setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, af, sizeof(*af)) != 0) {
74e87ff1eaSGleb Smirnoff 		if (errno == ENOENT)
75e87ff1eaSGleb Smirnoff 			atf_tc_skip("Accept filter %s not loaded in kernel",
76e87ff1eaSGleb Smirnoff 			    af->af_name);
77e87ff1eaSGleb Smirnoff 		else
78e87ff1eaSGleb Smirnoff 			atf_tc_fail("setsockopt(SO_ACCEPTFILTER): %s",
79e87ff1eaSGleb Smirnoff 			    strerror(errno));
80e87ff1eaSGleb Smirnoff 	}
81e87ff1eaSGleb Smirnoff }
82e87ff1eaSGleb Smirnoff 
83e87ff1eaSGleb Smirnoff /*
84e87ff1eaSGleb Smirnoff  * XXX: return from send(2) on a localhost connection doesn't guarantee that
85e87ff1eaSGleb Smirnoff  * netisr has fully processed and delivered the data to the remote local
86e87ff1eaSGleb Smirnoff  * socket.  Sleep a fraction of second to "guarantee" that it did.
87e87ff1eaSGleb Smirnoff  */
88e87ff1eaSGleb Smirnoff static ssize_t
usend(int s,const void * msg,size_t len)89e87ff1eaSGleb Smirnoff usend(int s, const void *msg, size_t len)
90e87ff1eaSGleb Smirnoff {
91e87ff1eaSGleb Smirnoff 	ssize_t rv;
92e87ff1eaSGleb Smirnoff 
93e87ff1eaSGleb Smirnoff 	rv = send(s, msg, len, 0);
94e87ff1eaSGleb Smirnoff 	usleep(100000);
95e87ff1eaSGleb Smirnoff 	return (rv);
96e87ff1eaSGleb Smirnoff }
97e87ff1eaSGleb Smirnoff 
98e87ff1eaSGleb Smirnoff ATF_TC_WITHOUT_HEAD(data);
ATF_TC_BODY(data,tc)99e87ff1eaSGleb Smirnoff ATF_TC_BODY(data, tc)
100e87ff1eaSGleb Smirnoff {
101e87ff1eaSGleb Smirnoff 	struct accept_filter_arg afa = {
102e87ff1eaSGleb Smirnoff 		.af_name = "dataready"
103e87ff1eaSGleb Smirnoff 	};
104e87ff1eaSGleb Smirnoff 	struct sockaddr_in sin;
105e87ff1eaSGleb Smirnoff 	int l, s, a;
106e87ff1eaSGleb Smirnoff 
107e87ff1eaSGleb Smirnoff 	l = listensock(&sin);
108e87ff1eaSGleb Smirnoff 	accfon(l, &afa);
109e87ff1eaSGleb Smirnoff 	s = clientsock(&sin);
110e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(accept(l, NULL, 0) == -1);
111e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(errno == EAGAIN);
112e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
113e87ff1eaSGleb Smirnoff 	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
114e87ff1eaSGleb Smirnoff }
115e87ff1eaSGleb Smirnoff 
116e87ff1eaSGleb Smirnoff ATF_TC_WITHOUT_HEAD(http);
ATF_TC_BODY(http,tc)117e87ff1eaSGleb Smirnoff ATF_TC_BODY(http, tc)
118e87ff1eaSGleb Smirnoff {
119e87ff1eaSGleb Smirnoff 	struct accept_filter_arg afa = {
120e87ff1eaSGleb Smirnoff 		.af_name = "httpready"
121e87ff1eaSGleb Smirnoff 	};
122e87ff1eaSGleb Smirnoff 	struct sockaddr_in sin;
123e87ff1eaSGleb Smirnoff 	int l, s, a;
124e87ff1eaSGleb Smirnoff 
125e87ff1eaSGleb Smirnoff 	l = listensock(&sin);
126e87ff1eaSGleb Smirnoff 	accfon(l, &afa);
127e87ff1eaSGleb Smirnoff 	s = clientsock(&sin);
128e87ff1eaSGleb Smirnoff 
129e87ff1eaSGleb Smirnoff 	/* 1) No data. */
130e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(accept(l, NULL, 0) == -1);
131e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(errno == EAGAIN);
132e87ff1eaSGleb Smirnoff 
133e87ff1eaSGleb Smirnoff 	/* 2) Data, that doesn't look like HTTP. */
134e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
135e87ff1eaSGleb Smirnoff 	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
136e87ff1eaSGleb Smirnoff 
137e87ff1eaSGleb Smirnoff 	close(s);
138e87ff1eaSGleb Smirnoff 	close(a);
139e87ff1eaSGleb Smirnoff 
140e87ff1eaSGleb Smirnoff #define	CHUNK1	"GET / "
141e87ff1eaSGleb Smirnoff #define	CHUNK2	"HTTP/1.0\r\n\n"
142e87ff1eaSGleb Smirnoff #define	LEN(c)	(sizeof(c) - 1)
143e87ff1eaSGleb Smirnoff 
144e87ff1eaSGleb Smirnoff 	/* 3) Partial HTTP. */
145e87ff1eaSGleb Smirnoff 	s = clientsock(&sin);
146e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(usend(s, CHUNK1, LEN(CHUNK1)) == LEN(CHUNK1));
147e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(accept(l, NULL, 0) == -1);
148e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(errno == EAGAIN);
149e87ff1eaSGleb Smirnoff 
150e87ff1eaSGleb Smirnoff 	/* 4) Complete HTTP. */
151e87ff1eaSGleb Smirnoff 	ATF_REQUIRE(usend(s, CHUNK2, LEN(CHUNK2)) == LEN(CHUNK2));
152e87ff1eaSGleb Smirnoff 	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
153e87ff1eaSGleb Smirnoff }
154e87ff1eaSGleb Smirnoff 
155c68eed82SGleb Smirnoff ATF_TC_WITHOUT_HEAD(tls);
ATF_TC_BODY(tls,tc)156c68eed82SGleb Smirnoff ATF_TC_BODY(tls, tc)
157c68eed82SGleb Smirnoff {
158c68eed82SGleb Smirnoff 	struct accept_filter_arg afa = {
159c68eed82SGleb Smirnoff 		.af_name = "tlsready"
160c68eed82SGleb Smirnoff 	};
161c68eed82SGleb Smirnoff 	struct sockaddr_in sin;
162c68eed82SGleb Smirnoff 	int l, s, a;
163c68eed82SGleb Smirnoff 
164c68eed82SGleb Smirnoff 	l = listensock(&sin);
165c68eed82SGleb Smirnoff 	accfon(l, &afa);
166c68eed82SGleb Smirnoff 	s = clientsock(&sin);
167c68eed82SGleb Smirnoff 
168c68eed82SGleb Smirnoff 	/* 1) No data. */
169c68eed82SGleb Smirnoff 	ATF_REQUIRE(accept(l, NULL, 0) == -1);
170c68eed82SGleb Smirnoff 	ATF_REQUIRE(errno == EAGAIN);
171c68eed82SGleb Smirnoff 
172c68eed82SGleb Smirnoff 	/* 2) Less than 5 bytes. */
173c68eed82SGleb Smirnoff 	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
174c68eed82SGleb Smirnoff 	ATF_REQUIRE(errno == EAGAIN);
175c68eed82SGleb Smirnoff 
176c68eed82SGleb Smirnoff 	/* 3) Something that doesn't look like TLS handshake. */
177c68eed82SGleb Smirnoff 	ATF_REQUIRE(usend(s, "bar", sizeof("bar")) == sizeof("bar"));
178c68eed82SGleb Smirnoff 	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
179c68eed82SGleb Smirnoff 
180c68eed82SGleb Smirnoff 	close(s);
181c68eed82SGleb Smirnoff 	close(a);
182c68eed82SGleb Smirnoff 
183c68eed82SGleb Smirnoff 	/* 4) Partial TLS record. */
184c68eed82SGleb Smirnoff 	s = clientsock(&sin);
185c68eed82SGleb Smirnoff 	struct {
186c68eed82SGleb Smirnoff 		uint8_t  type;
187c68eed82SGleb Smirnoff 		uint16_t version;
188c68eed82SGleb Smirnoff 		uint16_t length;
189c68eed82SGleb Smirnoff 	} __attribute__((__packed__)) header = {
190c68eed82SGleb Smirnoff 		.type = 0x16,
191c68eed82SGleb Smirnoff 		.length = htons((uint16_t)(arc4random() % 16384)),
192c68eed82SGleb Smirnoff 	};
193c68eed82SGleb Smirnoff 	_Static_assert(sizeof(header) == 5, "");
194c68eed82SGleb Smirnoff 	ATF_REQUIRE(usend(s, &header, sizeof(header)) == sizeof(header));
195c68eed82SGleb Smirnoff 	ssize_t sent = 0;
196c68eed82SGleb Smirnoff 	do {
197c68eed82SGleb Smirnoff 		size_t len;
198c68eed82SGleb Smirnoff 		char *buf;
199c68eed82SGleb Smirnoff 
200c68eed82SGleb Smirnoff 		ATF_REQUIRE(accept(l, NULL, 0) == -1);
201c68eed82SGleb Smirnoff 		ATF_REQUIRE(errno == EAGAIN);
202c68eed82SGleb Smirnoff 
203c68eed82SGleb Smirnoff 		len = arc4random() % 1024;
204c68eed82SGleb Smirnoff 		buf = alloca(len);
205c68eed82SGleb Smirnoff 		ATF_REQUIRE(usend(s, buf, len) == (ssize_t)len);
206c68eed82SGleb Smirnoff 		sent += len;
207c68eed82SGleb Smirnoff 	} while (sent < ntohs(header.length));
208c68eed82SGleb Smirnoff 	/* TLS header with bytes >= declared length. */
209c68eed82SGleb Smirnoff 	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
210c68eed82SGleb Smirnoff }
211c68eed82SGleb Smirnoff 
21219307b86SGleb Smirnoff /* Check changing to a different filter. */
21319307b86SGleb Smirnoff ATF_TC_WITHOUT_HEAD(change);
ATF_TC_BODY(change,tc)21419307b86SGleb Smirnoff ATF_TC_BODY(change, tc)
21519307b86SGleb Smirnoff {
21619307b86SGleb Smirnoff 	struct accept_filter_arg dfa = {
21719307b86SGleb Smirnoff 		.af_name = "dataready"
21819307b86SGleb Smirnoff 	};
21919307b86SGleb Smirnoff 	struct accept_filter_arg hfa = {
22019307b86SGleb Smirnoff 		.af_name = "httpready"
22119307b86SGleb Smirnoff 	};
22219307b86SGleb Smirnoff 	struct sockaddr_in sin;
22319307b86SGleb Smirnoff 	int n, l;
22419307b86SGleb Smirnoff 
22519307b86SGleb Smirnoff 	l = listensock(&sin);
22619307b86SGleb Smirnoff 	accfon(l, &dfa);
22719307b86SGleb Smirnoff 
22819307b86SGleb Smirnoff 	/* Refuse to change filter without explicit removal of the old one. */
22919307b86SGleb Smirnoff 	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
23019307b86SGleb Smirnoff 	    sizeof(hfa)) != 0 && errno == EBUSY);
23119307b86SGleb Smirnoff 
23219307b86SGleb Smirnoff 	/* But allow after clearing. */
23319307b86SGleb Smirnoff 	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) == 0);
23419307b86SGleb Smirnoff 	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
23519307b86SGleb Smirnoff 	    sizeof(hfa)) == 0);
23619307b86SGleb Smirnoff 
23719307b86SGleb Smirnoff 	/* Must be listening socket. */
23819307b86SGleb Smirnoff 	ATF_REQUIRE((n = socket(PF_INET, SOCK_STREAM, 0)) > 0);
23919307b86SGleb Smirnoff 	ATF_REQUIRE(setsockopt(n, SOL_SOCKET, SO_ACCEPTFILTER, &dfa,
24019307b86SGleb Smirnoff 	    sizeof(dfa)) != 0 && errno == EINVAL);
24119307b86SGleb Smirnoff }
24219307b86SGleb Smirnoff 
ATF_TP_ADD_TCS(tp)243e87ff1eaSGleb Smirnoff ATF_TP_ADD_TCS(tp)
244e87ff1eaSGleb Smirnoff {
245e87ff1eaSGleb Smirnoff 	ATF_TP_ADD_TC(tp, data);
246e87ff1eaSGleb Smirnoff 	ATF_TP_ADD_TC(tp, http);
247c68eed82SGleb Smirnoff 	ATF_TP_ADD_TC(tp, tls);
24819307b86SGleb Smirnoff 	ATF_TP_ADD_TC(tp, change);
249e87ff1eaSGleb Smirnoff 
250e87ff1eaSGleb Smirnoff 	return (atf_no_error());
251e87ff1eaSGleb Smirnoff }
252