]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - tools/regression/sockets/listen_backlog/listen_backlog.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / tools / regression / sockets / listen_backlog / listen_backlog.c
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  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/sysctl.h>
32
33 #include <err.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38
39 /*
40  * This regression test is intended to validate that the backlog parameter
41  * set by listen() is properly set, can be retrieved using SO_LISTENQLIMIT,
42  * and that it can be updated by later calls to listen().  We also check that
43  * SO_LISTENQLIMIT cannot be set.
44  *
45  * Future things to test:
46  *
47  * - That if we change the value of kern.ipc.somaxconn, the limits really
48  *   do change.
49  *
50  * - That limits are, approximately, enforced and implemented.
51  *
52  * - All this on multiple socket types -- i.e., PF_LOCAL.
53  *
54  * - That we also test SO_LISTENQLEN and SO_LISTENINCQLEN.
55  */
56
57 /*
58  * We retrieve kern.ipc.somaxconn before running the tests in order to use a
59  * run-time set value of SOMAXCONN, rather than compile-time set.  We assume
60  * that no other process will be simultaneously frobbing it, and these tests
61  * may fail if that assumption is not held.
62  */
63 static int      somaxconn;
64
65 /*
66  * Retrieve the current socket listen queue limit using SO_LISTENQLIMIT.
67  */
68 static int
69 socket_get_backlog(int sock, int *backlogp, const char *testclass,
70     const char *test, const char *testfunc)
71 {
72         socklen_t len;
73         int i;
74
75         len = sizeof(i);
76         if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) {
77                 warn("%s: %s: %s: socket_get_backlog: getsockopt("
78                     "SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test,
79                     testfunc);
80                 return (-1);
81         }
82
83         if (len != sizeof(i)) {
84                 warnx("%s: %s: %s: socket_get_backlog: getsockopt("
85                     "SOL_SOCKET, SO_LISTENQLIMIT): returned size %d",
86                     testclass, test, testfunc, len);
87                 return (-1);
88         }
89
90         *backlogp = i;
91
92         return (0);
93 }
94
95 /*
96  * Create a socket, check the queue limit on creation, perform a listen(),
97  * and make sure that the limit was set as expected by listen().
98  */
99 static int
100 socket_listen(int domain, int type, int protocol, int backlog,
101     int create_backlog_assertion, int listen_backlog_assertion, int *sockp,
102     const char *domainstring, const char *typestring, const char *testclass,
103     const char *test)
104 {
105         int backlog_retrieved, sock;
106
107         sock = socket(domain, type, protocol);
108         if (sock < 0) {
109                 warn("%s: %s: socket_listen: socket(%s, %s)", testclass,
110                     test, domainstring, typestring);
111                 close(sock);
112                 return (-1);
113         }
114
115         if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
116             "socket_listen") < 0) {
117                 close(sock);
118                 return (-1);
119         }
120
121         if (backlog_retrieved != create_backlog_assertion) {
122                 warnx("%s: %s: socket_listen: create backlog is %d not %d",
123                     testclass, test, backlog_retrieved,
124                     create_backlog_assertion);
125                 close(sock);
126                 return (-1);
127         }
128
129         if (listen(sock, backlog) < 0) {
130                 warn("%s: %s: socket_listen: listen(, %d)", testclass, test,
131                     backlog);
132                 close(sock);
133                 return (-1);
134         }
135
136         if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
137             "socket_listen") < 0) {
138                 close(sock);
139                 return (-1);
140         }
141
142         if (backlog_retrieved != listen_backlog_assertion) {
143                 warnx("%s: %s: socket_listen: listen backlog is %d not %d",
144                     testclass, test, backlog_retrieved,
145                     listen_backlog_assertion);
146                 close(sock);
147                 return (-1);
148         }
149
150         *sockp = sock;
151         return (0);
152 }
153
154 /*
155  * This test creates sockets and tests default states before and after
156  * listen().  Specifically, we expect a queue limit of 0 before listen, and
157  * then various settings for after listen().  If the passed backlog was
158  * either < 0 or > somaxconn, it should be set to somaxconn; otherwise, the
159  * passed queue depth.
160  */
161 static void
162 test_defaults(void)
163 {
164         int sock;
165
166         /*
167          * First pass.  Confirm the default is 0.  Listen with a backlog of
168          * 0 and confirm it gets set that way.
169          */
170         if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET",
171             "SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0)
172                 exit(-1);
173         close(sock);
174
175         /*
176          * Second pass.  Listen with a backlog of -1 and make sure it is set
177          * to somaxconn.
178          */
179         if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock,
180             "PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1")
181             < 0)
182                 exit(-1);
183         close(sock);
184
185         /*
186          * Third pass.  Listen with a backlog of 1 and make sure it is set to
187          * 1.
188          */
189         if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET",
190             "SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0)
191                 exit(-1);
192         close(sock);
193
194         /*
195          * Fourth pass.  Listen with a backlog of somaxconn and make sure it
196          * is set to somaxconn.
197          */
198         if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn,
199             &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
200             "default_0_listen_somaxconn") < 0)
201                 exit(-1);
202         close(sock);
203
204         /*
205          * Fifth pass.  Listen with a backlog of somaxconn+1 and make sure it
206          * is set to somaxconn.
207          */
208         if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn,
209             &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
210             "default_0_listen_somaxconn+1") < 0)
211                 exit(-1);
212         close(sock);
213 }
214
215 /*
216  * Create a socket, set the initial listen() state, then update the queue
217  * depth using listen().  Check that the backlog is as expected after both
218  * the first and second listen().
219  */
220 static int
221 socket_listen_update(int domain __unused, int type __unused,
222     int protocol __unused, int backlog,
223     int update_backlog, int listen_backlog_assertion,
224     int update_backlog_assertion, int *sockp, const char *domainstring,
225     const char *typestring, const char *testclass, const char *test)
226 {
227         int backlog_retrieved, sock;
228
229         sock = socket(PF_INET, SOCK_STREAM, 0);
230         if (sock < 0) {
231                 warn("%s: %s: socket_listen_update: socket(%s, %s)",
232                     testclass, test, domainstring, typestring);
233                 return (-1);
234         }
235
236         if (listen(sock, backlog) < 0) {
237                 warn("%s: %s: socket_listen_update: initial listen(, %d)",
238                     testclass, test, backlog);
239                 close(sock);
240                 return (-1);
241         }
242
243         if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
244             "socket_listen_update") < 0) {
245                 close(sock);
246                 return (-1);
247         }
248
249         if (backlog_retrieved != listen_backlog_assertion) {
250                 warnx("%s: %s: socket_listen_update: initial backlog is %d "
251                     "not %d", testclass, test, backlog_retrieved,
252                     listen_backlog_assertion);
253                 close(sock);
254                 return (-1);
255         }
256
257         if (listen(sock, update_backlog) < 0) {
258                 warn("%s: %s: socket_listen_update: update listen(, %d)",
259                     testclass, test, update_backlog);
260                 close(sock);
261                 return (-1);
262         }
263
264         if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
265             "socket_listen_update") < 0) {
266                 close(sock);
267                 return (-1);
268         }
269
270         if (backlog_retrieved != update_backlog_assertion) {
271                 warnx("%s: %s: socket_listen_update: updated backlog is %d "
272                     "not %d", testclass, test, backlog_retrieved,
273                     update_backlog_assertion);
274                 close(sock);
275                 return (-1);
276         }
277
278         *sockp = sock;
279         return (0);
280 }
281
282 /*
283  * This test tests using listen() to update the queue depth after a socket
284  * has already been marked as listening.  We test several cases: setting the
285  * socket < 0, 0, 1, somaxconn, and somaxconn + 1.
286  */
287 static void
288 test_listen_update(void)
289 {
290         int sock;
291
292         /*
293          * Set to 5, update to -1, which should give somaxconn.
294          */
295         if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn,
296             &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
297             "update_5,-1") < 0)
298                 exit(-1);
299         close(sock);
300
301         /*
302          * Set to 5, update to 0, which should give 0.
303          */
304         if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock,
305             "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0")
306             < 0)
307                 exit(-1);
308         close(sock);
309
310         /*
311          * Set to 5, update to 1, which should give 1.
312          */
313         if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock,
314             "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1")
315             < 0)
316                 exit(-1);
317         close(sock);
318
319         /*
320          * Set to 5, update to somaxconn, which should give somaxconn.
321          */
322         if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5,
323             somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
324             "update_5,somaxconn") < 0)
325                 exit(-1);
326         close(sock);
327
328         /*
329          * Set to 5, update to somaxconn+1, which should give somaxconn.
330          */
331         if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5,
332             somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
333             "update_5,somaxconn+1") < 0)
334                 exit(-1);
335         close(sock);
336 }
337
338 /*
339  * SO_LISTENQLIMIT is a read-only socket option, so make sure we get an error
340  * if we try to write it.
341  */
342 static void
343 test_set_qlimit(void)
344 {
345         int i, ret, sock;
346
347         sock = socket(PF_INET, SOCK_STREAM, 0);
348         if (sock < 0)
349                 err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)");
350
351         i = 0;
352         ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i));
353         if (ret < 0 && errno != ENOPROTOOPT) {
354                 warn("test_set_qlimit: setsockopt(SOL_SOCKET, "
355                     "SO_LISTENQLIMIT, 0): unexpected error");
356                 close(sock);
357         }
358
359         if (ret == 0) {
360                 warnx("test_set_qlimit: setsockopt(SOL_SOCKET, "
361                     "SO_LISTENQLIMIT, 0) succeeded");
362                 close(sock);
363                 exit(-1);
364         }
365         close(sock);
366 }
367
368 int
369 main(void)
370 {
371         size_t len;
372
373         len = sizeof(somaxconn);
374         if (sysctlbyname("kern.ipc.somaxconn", &somaxconn, &len, NULL, 0)
375             < 0)
376                 err(-1, "sysctlbyname(kern.ipc.somaxconn)");
377
378         test_defaults();
379         test_listen_update();
380         test_set_qlimit();
381
382         return (0);
383 }