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