1 /*-
2 * Copyright (c) 2005 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/sysctl.h>
30
31 #include <err.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36
37 /*
38 * This regression test is intended to validate that the backlog parameter
39 * set by listen() is properly set, can be retrieved using SO_LISTENQLIMIT,
40 * and that it can be updated by later calls to listen(). We also check that
41 * SO_LISTENQLIMIT cannot be set.
42 *
43 * Future things to test:
44 *
45 * - That if we change the value of kern.ipc.soacceptqueue, the limits really
46 * do change.
47 *
48 * - That limits are, approximately, enforced and implemented.
49 *
50 * - All this on multiple socket types -- i.e., PF_LOCAL.
51 *
52 * - That we also test SO_LISTENQLEN and SO_LISTENINCQLEN.
53 */
54
55 /*
56 * We retrieve kern.ipc.soacceptqueue before running the tests in order to use a
57 * run-time set value of SOMAXCONN, rather than compile-time set. We assume
58 * that no other process will be simultaneously frobbing it, and these tests
59 * may fail if that assumption is not held.
60 */
61 static int somaxconn;
62
63 /*
64 * Retrieve the current socket listen queue limit using SO_LISTENQLIMIT.
65 */
66 static int
socket_get_backlog(int sock,int * backlogp,const char * testclass,const char * test,const char * testfunc)67 socket_get_backlog(int sock, int *backlogp, const char *testclass,
68 const char *test, const char *testfunc)
69 {
70 socklen_t len;
71 int i;
72
73 len = sizeof(i);
74 if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) {
75 warn("%s: %s: %s: socket_get_backlog: getsockopt("
76 "SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test,
77 testfunc);
78 return (-1);
79 }
80
81 if (len != sizeof(i)) {
82 warnx("%s: %s: %s: socket_get_backlog: getsockopt("
83 "SOL_SOCKET, SO_LISTENQLIMIT): returned size %d",
84 testclass, test, testfunc, len);
85 return (-1);
86 }
87
88 *backlogp = i;
89
90 return (0);
91 }
92
93 /*
94 * Create a socket, check the queue limit on creation, perform a listen(),
95 * and make sure that the limit was set as expected by listen().
96 */
97 static int
socket_listen(int domain,int type,int protocol,int backlog,int create_backlog_assertion,int listen_backlog_assertion,int * sockp,const char * domainstring,const char * typestring,const char * testclass,const char * test)98 socket_listen(int domain, int type, int protocol, int backlog,
99 int create_backlog_assertion, int listen_backlog_assertion, int *sockp,
100 const char *domainstring, const char *typestring, const char *testclass,
101 const char *test)
102 {
103 int backlog_retrieved, sock;
104
105 sock = socket(domain, type, protocol);
106 if (sock < 0) {
107 warn("%s: %s: socket_listen: socket(%s, %s)", testclass,
108 test, domainstring, typestring);
109 close(sock);
110 return (-1);
111 }
112
113 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
114 "socket_listen") < 0) {
115 close(sock);
116 return (-1);
117 }
118
119 if (backlog_retrieved != create_backlog_assertion) {
120 warnx("%s: %s: socket_listen: create backlog is %d not %d",
121 testclass, test, backlog_retrieved,
122 create_backlog_assertion);
123 close(sock);
124 return (-1);
125 }
126
127 if (listen(sock, backlog) < 0) {
128 warn("%s: %s: socket_listen: listen(, %d)", testclass, test,
129 backlog);
130 close(sock);
131 return (-1);
132 }
133
134 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
135 "socket_listen") < 0) {
136 close(sock);
137 return (-1);
138 }
139
140 if (backlog_retrieved != listen_backlog_assertion) {
141 warnx("%s: %s: socket_listen: listen backlog is %d not %d",
142 testclass, test, backlog_retrieved,
143 listen_backlog_assertion);
144 close(sock);
145 return (-1);
146 }
147
148 *sockp = sock;
149 return (0);
150 }
151
152 /*
153 * This test creates sockets and tests default states before and after
154 * listen(). Specifically, we expect a queue limit of 0 before listen, and
155 * then various settings for after listen(). If the passed backlog was
156 * either < 0 or > somaxconn, it should be set to somaxconn; otherwise, the
157 * passed queue depth.
158 */
159 static void
test_defaults(void)160 test_defaults(void)
161 {
162 int sock;
163
164 /*
165 * First pass. Confirm the default is 0. Listen with a backlog of
166 * 0 and confirm it gets set that way.
167 */
168 if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET",
169 "SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0)
170 exit(-1);
171 close(sock);
172
173 /*
174 * Second pass. Listen with a backlog of -1 and make sure it is set
175 * to somaxconn.
176 */
177 if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock,
178 "PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1")
179 < 0)
180 exit(-1);
181 close(sock);
182
183 /*
184 * Third pass. Listen with a backlog of 1 and make sure it is set to
185 * 1.
186 */
187 if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET",
188 "SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0)
189 exit(-1);
190 close(sock);
191
192 /*
193 * Fourth pass. Listen with a backlog of somaxconn and make sure it
194 * is set to somaxconn.
195 */
196 if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn,
197 &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
198 "default_0_listen_somaxconn") < 0)
199 exit(-1);
200 close(sock);
201
202 /*
203 * Fifth pass. Listen with a backlog of somaxconn+1 and make sure it
204 * is set to somaxconn.
205 */
206 if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn,
207 &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
208 "default_0_listen_somaxconn+1") < 0)
209 exit(-1);
210 close(sock);
211 }
212
213 /*
214 * Create a socket, set the initial listen() state, then update the queue
215 * depth using listen(). Check that the backlog is as expected after both
216 * the first and second listen().
217 */
218 static int
socket_listen_update(int domain __unused,int type __unused,int protocol __unused,int backlog,int update_backlog,int listen_backlog_assertion,int update_backlog_assertion,int * sockp,const char * domainstring,const char * typestring,const char * testclass,const char * test)219 socket_listen_update(int domain __unused, int type __unused,
220 int protocol __unused, int backlog,
221 int update_backlog, int listen_backlog_assertion,
222 int update_backlog_assertion, int *sockp, const char *domainstring,
223 const char *typestring, const char *testclass, const char *test)
224 {
225 int backlog_retrieved, sock;
226
227 sock = socket(PF_INET, SOCK_STREAM, 0);
228 if (sock < 0) {
229 warn("%s: %s: socket_listen_update: socket(%s, %s)",
230 testclass, test, domainstring, typestring);
231 return (-1);
232 }
233
234 if (listen(sock, backlog) < 0) {
235 warn("%s: %s: socket_listen_update: initial listen(, %d)",
236 testclass, test, backlog);
237 close(sock);
238 return (-1);
239 }
240
241 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
242 "socket_listen_update") < 0) {
243 close(sock);
244 return (-1);
245 }
246
247 if (backlog_retrieved != listen_backlog_assertion) {
248 warnx("%s: %s: socket_listen_update: initial backlog is %d "
249 "not %d", testclass, test, backlog_retrieved,
250 listen_backlog_assertion);
251 close(sock);
252 return (-1);
253 }
254
255 if (listen(sock, update_backlog) < 0) {
256 warn("%s: %s: socket_listen_update: update listen(, %d)",
257 testclass, test, update_backlog);
258 close(sock);
259 return (-1);
260 }
261
262 if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
263 "socket_listen_update") < 0) {
264 close(sock);
265 return (-1);
266 }
267
268 if (backlog_retrieved != update_backlog_assertion) {
269 warnx("%s: %s: socket_listen_update: updated backlog is %d "
270 "not %d", testclass, test, backlog_retrieved,
271 update_backlog_assertion);
272 close(sock);
273 return (-1);
274 }
275
276 *sockp = sock;
277 return (0);
278 }
279
280 /*
281 * This test tests using listen() to update the queue depth after a socket
282 * has already been marked as listening. We test several cases: setting the
283 * socket < 0, 0, 1, somaxconn, and somaxconn + 1.
284 */
285 static void
test_listen_update(void)286 test_listen_update(void)
287 {
288 int sock;
289
290 /*
291 * Set to 5, update to -1, which should give somaxconn.
292 */
293 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn,
294 &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
295 "update_5,-1") < 0)
296 exit(-1);
297 close(sock);
298
299 /*
300 * Set to 5, update to 0, which should give 0.
301 */
302 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock,
303 "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0")
304 < 0)
305 exit(-1);
306 close(sock);
307
308 /*
309 * Set to 5, update to 1, which should give 1.
310 */
311 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock,
312 "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1")
313 < 0)
314 exit(-1);
315 close(sock);
316
317 /*
318 * Set to 5, update to somaxconn, which should give somaxconn.
319 */
320 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5,
321 somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
322 "update_5,somaxconn") < 0)
323 exit(-1);
324 close(sock);
325
326 /*
327 * Set to 5, update to somaxconn+1, which should give somaxconn.
328 */
329 if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5,
330 somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
331 "update_5,somaxconn+1") < 0)
332 exit(-1);
333 close(sock);
334 }
335
336 /*
337 * SO_LISTENQLIMIT is a read-only socket option, so make sure we get an error
338 * if we try to write it.
339 */
340 static void
test_set_qlimit(void)341 test_set_qlimit(void)
342 {
343 int i, ret, sock;
344
345 sock = socket(PF_INET, SOCK_STREAM, 0);
346 if (sock < 0)
347 err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)");
348
349 i = 0;
350 ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i));
351 if (ret < 0 && errno != ENOPROTOOPT) {
352 warn("test_set_qlimit: setsockopt(SOL_SOCKET, "
353 "SO_LISTENQLIMIT, 0): unexpected error");
354 close(sock);
355 }
356
357 if (ret == 0) {
358 warnx("test_set_qlimit: setsockopt(SOL_SOCKET, "
359 "SO_LISTENQLIMIT, 0) succeeded");
360 close(sock);
361 exit(-1);
362 }
363 close(sock);
364 }
365
366 int
main(void)367 main(void)
368 {
369 size_t len;
370
371 len = sizeof(somaxconn);
372 if (sysctlbyname("kern.ipc.soacceptqueue", &somaxconn, &len, NULL, 0)
373 < 0)
374 err(-1, "sysctlbyname(kern.ipc.soacceptqueue)");
375
376 test_defaults();
377 test_listen_update();
378 test_set_qlimit();
379
380 return (0);
381 }
382