1150286Srwatson/*-
2150286Srwatson * Copyright (c) 2005 Robert N. M. Watson
3150286Srwatson * All rights reserved.
4150286Srwatson *
5150286Srwatson * Redistribution and use in source and binary forms, with or without
6150286Srwatson * modification, are permitted provided that the following conditions
7150286Srwatson * are met:
8150286Srwatson * 1. Redistributions of source code must retain the above copyright
9150286Srwatson *    notice, this list of conditions and the following disclaimer.
10150286Srwatson * 2. Redistributions in binary form must reproduce the above copyright
11150286Srwatson *    notice, this list of conditions and the following disclaimer in the
12150286Srwatson *    documentation and/or other materials provided with the distribution.
13150286Srwatson *
14150286Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15150286Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16150286Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17150286Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18150286Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19150286Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20150286Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21150286Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22150286Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23150286Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24150286Srwatson * SUCH DAMAGE.
25150286Srwatson *
26150286Srwatson * $FreeBSD$
27150286Srwatson */
28150286Srwatson
29150286Srwatson#include <sys/types.h>
30150286Srwatson#include <sys/socket.h>
31150286Srwatson#include <sys/sysctl.h>
32150286Srwatson
33150286Srwatson#include <err.h>
34150286Srwatson#include <errno.h>
35150286Srwatson#include <stdio.h>
36150286Srwatson#include <stdlib.h>
37150286Srwatson#include <unistd.h>
38150286Srwatson
39150286Srwatson/*
40150286Srwatson * This regression test is intended to validate that the backlog parameter
41150286Srwatson * set by listen() is properly set, can be retrieved using SO_LISTENQLIMIT,
42150286Srwatson * and that it can be updated by later calls to listen().  We also check that
43150286Srwatson * SO_LISTENQLIMIT cannot be set.
44150286Srwatson *
45150286Srwatson * Future things to test:
46150286Srwatson *
47150286Srwatson * - That if we change the value of kern.ipc.somaxconn, the limits really
48150286Srwatson *   do change.
49150286Srwatson *
50150286Srwatson * - That limits are, approximately, enforced and implemented.
51150286Srwatson *
52150286Srwatson * - All this on multiple socket types -- i.e., PF_LOCAL.
53150286Srwatson *
54150286Srwatson * - That we also test SO_LISTENQLEN and SO_LISTENINCQLEN.
55150286Srwatson */
56150286Srwatson
57150286Srwatson/*
58150286Srwatson * We retrieve kern.ipc.somaxconn before running the tests in order to use a
59150286Srwatson * run-time set value of SOMAXCONN, rather than compile-time set.  We assume
60150286Srwatson * that no other process will be simultaneously frobbing it, and these tests
61150286Srwatson * may fail if that assumption is not held.
62150286Srwatson */
63150286Srwatsonstatic int	somaxconn;
64150286Srwatson
65150286Srwatson/*
66150286Srwatson * Retrieve the current socket listen queue limit using SO_LISTENQLIMIT.
67150286Srwatson */
68150286Srwatsonstatic int
69150286Srwatsonsocket_get_backlog(int sock, int *backlogp, const char *testclass,
70150286Srwatson    const char *test, const char *testfunc)
71150286Srwatson{
72150286Srwatson	socklen_t len;
73150286Srwatson	int i;
74150286Srwatson
75150286Srwatson	len = sizeof(i);
76150286Srwatson	if (getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, &len) < 0) {
77150286Srwatson		warn("%s: %s: %s: socket_get_backlog: getsockopt("
78150286Srwatson		    "SOL_SOCKET, SO_LISTENQLIMIT)", testclass, test,
79150286Srwatson		    testfunc);
80150286Srwatson		return (-1);
81150286Srwatson	}
82150286Srwatson
83150286Srwatson	if (len != sizeof(i)) {
84150286Srwatson		warnx("%s: %s: %s: socket_get_backlog: getsockopt("
85150286Srwatson		    "SOL_SOCKET, SO_LISTENQLIMIT): returned size %d",
86150286Srwatson		    testclass, test, testfunc, len);
87150286Srwatson		return (-1);
88150286Srwatson	}
89150286Srwatson
90150286Srwatson	*backlogp = i;
91150286Srwatson
92150286Srwatson	return (0);
93150286Srwatson}
94150286Srwatson
95150286Srwatson/*
96150286Srwatson * Create a socket, check the queue limit on creation, perform a listen(),
97150286Srwatson * and make sure that the limit was set as expected by listen().
98150286Srwatson */
99150286Srwatsonstatic int
100150286Srwatsonsocket_listen(int domain, int type, int protocol, int backlog,
101150286Srwatson    int create_backlog_assertion, int listen_backlog_assertion, int *sockp,
102150286Srwatson    const char *domainstring, const char *typestring, const char *testclass,
103150286Srwatson    const char *test)
104150286Srwatson{
105150286Srwatson	int backlog_retrieved, sock;
106150286Srwatson
107150286Srwatson	sock = socket(domain, type, protocol);
108150286Srwatson	if (sock < 0) {
109150286Srwatson		warn("%s: %s: socket_listen: socket(%s, %s)", testclass,
110150286Srwatson		    test, domainstring, typestring);
111150286Srwatson		close(sock);
112150286Srwatson		return (-1);
113150286Srwatson	}
114150286Srwatson
115150286Srwatson	if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
116150286Srwatson	    "socket_listen") < 0) {
117150286Srwatson		close(sock);
118150286Srwatson		return (-1);
119150286Srwatson	}
120150286Srwatson
121150286Srwatson	if (backlog_retrieved != create_backlog_assertion) {
122150286Srwatson		warnx("%s: %s: socket_listen: create backlog is %d not %d",
123150286Srwatson		    testclass, test, backlog_retrieved,
124150286Srwatson		    create_backlog_assertion);
125150286Srwatson		close(sock);
126150286Srwatson		return (-1);
127150286Srwatson	}
128150286Srwatson
129150286Srwatson	if (listen(sock, backlog) < 0) {
130150286Srwatson		warn("%s: %s: socket_listen: listen(, %d)", testclass, test,
131150286Srwatson		    backlog);
132150286Srwatson		close(sock);
133150286Srwatson		return (-1);
134150286Srwatson	}
135150286Srwatson
136150286Srwatson	if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
137150286Srwatson	    "socket_listen") < 0) {
138150286Srwatson		close(sock);
139150286Srwatson		return (-1);
140150286Srwatson	}
141150286Srwatson
142150286Srwatson	if (backlog_retrieved != listen_backlog_assertion) {
143150286Srwatson		warnx("%s: %s: socket_listen: listen backlog is %d not %d",
144150286Srwatson		    testclass, test, backlog_retrieved,
145150286Srwatson		    listen_backlog_assertion);
146150286Srwatson		close(sock);
147150286Srwatson		return (-1);
148150286Srwatson	}
149150286Srwatson
150150286Srwatson	*sockp = sock;
151150286Srwatson	return (0);
152150286Srwatson}
153150286Srwatson
154150286Srwatson/*
155150286Srwatson * This test creates sockets and tests default states before and after
156150286Srwatson * listen().  Specifically, we expect a queue limit of 0 before listen, and
157150286Srwatson * then various settings for after listen().  If the passed backlog was
158150286Srwatson * either < 0 or > somaxconn, it should be set to somaxconn; otherwise, the
159150286Srwatson * passed queue depth.
160150286Srwatson */
161150286Srwatsonstatic void
162150286Srwatsontest_defaults(void)
163150286Srwatson{
164150286Srwatson	int sock;
165150286Srwatson
166150286Srwatson	/*
167150286Srwatson	 * First pass.  Confirm the default is 0.  Listen with a backlog of
168150286Srwatson	 * 0 and confirm it gets set that way.
169150286Srwatson	 */
170150286Srwatson	if (socket_listen(PF_INET, SOCK_STREAM, 0, 0, 0, 0, &sock, "PF_INET",
171150286Srwatson	    "SOCK_STREAM", "test_defaults", "default_0_listen_0") < 0)
172150286Srwatson		exit(-1);
173150286Srwatson	close(sock);
174150286Srwatson
175150286Srwatson	/*
176150286Srwatson	 * Second pass.  Listen with a backlog of -1 and make sure it is set
177150286Srwatson	 * to somaxconn.
178150286Srwatson	 */
179150286Srwatson	if (socket_listen(PF_INET, SOCK_STREAM, 0, -1, 0, somaxconn, &sock,
180150286Srwatson	    "PF_INET", "SOCK_STREAM", "test_defaults", "default_0_listen_-1")
181150286Srwatson	    < 0)
182150286Srwatson		exit(-1);
183150286Srwatson	close(sock);
184150286Srwatson
185150286Srwatson	/*
186150286Srwatson	 * Third pass.  Listen with a backlog of 1 and make sure it is set to
187150286Srwatson	 * 1.
188150286Srwatson	 */
189150286Srwatson	if (socket_listen(PF_INET, SOCK_STREAM, 0, 1, 0, 1, &sock, "PF_INET",
190150286Srwatson	    "SOCK_STREAM", "test_defaults", "default_0_listen_1") < 0)
191150286Srwatson		exit(-1);
192150286Srwatson	close(sock);
193150286Srwatson
194150286Srwatson	/*
195150286Srwatson	 * Fourth pass.  Listen with a backlog of somaxconn and make sure it
196150286Srwatson	 * is set to somaxconn.
197150286Srwatson	 */
198150286Srwatson	if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn, 0, somaxconn,
199150286Srwatson	    &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
200150286Srwatson	    "default_0_listen_somaxconn") < 0)
201150286Srwatson		exit(-1);
202150286Srwatson	close(sock);
203150286Srwatson
204150286Srwatson	/*
205150286Srwatson	 * Fifth pass.  Listen with a backlog of somaxconn+1 and make sure it
206150286Srwatson	 * is set to somaxconn.
207150286Srwatson	 */
208150286Srwatson	if (socket_listen(PF_INET, SOCK_STREAM, 0, somaxconn+1, 0, somaxconn,
209150286Srwatson	    &sock, "PF_INET", "SOCK_STREAM", "test_defaults",
210150286Srwatson	    "default_0_listen_somaxconn+1") < 0)
211150286Srwatson		exit(-1);
212150286Srwatson	close(sock);
213150286Srwatson}
214150286Srwatson
215150286Srwatson/*
216150286Srwatson * Create a socket, set the initial listen() state, then update the queue
217150286Srwatson * depth using listen().  Check that the backlog is as expected after both
218150286Srwatson * the first and second listen().
219150286Srwatson */
220150286Srwatsonstatic int
221281360Sngiesocket_listen_update(int domain __unused, int type __unused,
222281360Sngie    int protocol __unused, int backlog,
223150286Srwatson    int update_backlog, int listen_backlog_assertion,
224150286Srwatson    int update_backlog_assertion, int *sockp, const char *domainstring,
225150286Srwatson    const char *typestring, const char *testclass, const char *test)
226150286Srwatson{
227150286Srwatson	int backlog_retrieved, sock;
228150286Srwatson
229150286Srwatson	sock = socket(PF_INET, SOCK_STREAM, 0);
230150286Srwatson	if (sock < 0) {
231150286Srwatson		warn("%s: %s: socket_listen_update: socket(%s, %s)",
232150286Srwatson		    testclass, test, domainstring, typestring);
233150286Srwatson		return (-1);
234150286Srwatson	}
235150286Srwatson
236150286Srwatson	if (listen(sock, backlog) < 0) {
237150286Srwatson		warn("%s: %s: socket_listen_update: initial listen(, %d)",
238150286Srwatson		    testclass, test, backlog);
239150286Srwatson		close(sock);
240150286Srwatson		return (-1);
241150286Srwatson	}
242150286Srwatson
243150286Srwatson	if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
244150286Srwatson	    "socket_listen_update") < 0) {
245150286Srwatson		close(sock);
246150286Srwatson		return (-1);
247150286Srwatson	}
248150286Srwatson
249150286Srwatson	if (backlog_retrieved != listen_backlog_assertion) {
250150286Srwatson		warnx("%s: %s: socket_listen_update: initial backlog is %d "
251150286Srwatson		    "not %d", testclass, test, backlog_retrieved,
252150286Srwatson		    listen_backlog_assertion);
253150286Srwatson		close(sock);
254150286Srwatson		return (-1);
255150286Srwatson	}
256150286Srwatson
257150286Srwatson	if (listen(sock, update_backlog) < 0) {
258150286Srwatson		warn("%s: %s: socket_listen_update: update listen(, %d)",
259150286Srwatson		    testclass, test, update_backlog);
260150286Srwatson		close(sock);
261150286Srwatson		return (-1);
262150286Srwatson	}
263150286Srwatson
264150286Srwatson	if (socket_get_backlog(sock, &backlog_retrieved, testclass, test,
265150286Srwatson	    "socket_listen_update") < 0) {
266150286Srwatson		close(sock);
267150286Srwatson		return (-1);
268150286Srwatson	}
269150286Srwatson
270150286Srwatson	if (backlog_retrieved != update_backlog_assertion) {
271150286Srwatson		warnx("%s: %s: socket_listen_update: updated backlog is %d "
272150286Srwatson		    "not %d", testclass, test, backlog_retrieved,
273150286Srwatson		    update_backlog_assertion);
274150286Srwatson		close(sock);
275150286Srwatson		return (-1);
276150286Srwatson	}
277150286Srwatson
278150286Srwatson	*sockp = sock;
279150286Srwatson	return (0);
280150286Srwatson}
281150286Srwatson
282150286Srwatson/*
283150286Srwatson * This test tests using listen() to update the queue depth after a socket
284150286Srwatson * has already been marked as listening.  We test several cases: setting the
285150286Srwatson * socket < 0, 0, 1, somaxconn, and somaxconn + 1.
286150286Srwatson */
287150286Srwatsonstatic void
288150286Srwatsontest_listen_update(void)
289150286Srwatson{
290150286Srwatson	int sock;
291150286Srwatson
292150286Srwatson	/*
293150286Srwatson	 * Set to 5, update to -1, which should give somaxconn.
294150286Srwatson	 */
295150286Srwatson	if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, -1, 5, somaxconn,
296150286Srwatson	    &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
297150286Srwatson	    "update_5,-1") < 0)
298150286Srwatson		exit(-1);
299150286Srwatson	close(sock);
300150286Srwatson
301150286Srwatson	/*
302150286Srwatson	 * Set to 5, update to 0, which should give 0.
303150286Srwatson	 */
304150286Srwatson	if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 0, 5, 0, &sock,
305150286Srwatson	    "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,0")
306150286Srwatson	    < 0)
307150286Srwatson		exit(-1);
308150286Srwatson	close(sock);
309150286Srwatson
310150286Srwatson	/*
311150286Srwatson	 * Set to 5, update to 1, which should give 1.
312150286Srwatson	 */
313150286Srwatson	if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, 1, 5, 1, &sock,
314150286Srwatson	    "PF_INET", "SOCK_STREAM", "test_listen_update", "update_5,1")
315150286Srwatson	    < 0)
316150286Srwatson		exit(-1);
317150286Srwatson	close(sock);
318150286Srwatson
319150286Srwatson	/*
320150286Srwatson	 * Set to 5, update to somaxconn, which should give somaxconn.
321150286Srwatson	 */
322150286Srwatson	if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn, 5,
323150286Srwatson	    somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
324150286Srwatson	    "update_5,somaxconn") < 0)
325150286Srwatson		exit(-1);
326150286Srwatson	close(sock);
327150286Srwatson
328150286Srwatson	/*
329150286Srwatson	 * Set to 5, update to somaxconn+1, which should give somaxconn.
330150286Srwatson	 */
331150286Srwatson	if (socket_listen_update(PF_INET, SOCK_STREAM, 0, 5, somaxconn+1, 5,
332150286Srwatson	    somaxconn, &sock, "PF_INET", "SOCK_STREAM", "test_listen_update",
333150286Srwatson	    "update_5,somaxconn+1") < 0)
334150286Srwatson		exit(-1);
335150286Srwatson	close(sock);
336150286Srwatson}
337150286Srwatson
338150286Srwatson/*
339150286Srwatson * SO_LISTENQLIMIT is a read-only socket option, so make sure we get an error
340150286Srwatson * if we try to write it.
341150286Srwatson */
342150286Srwatsonstatic void
343150286Srwatsontest_set_qlimit(void)
344150286Srwatson{
345150286Srwatson	int i, ret, sock;
346150286Srwatson
347150286Srwatson	sock = socket(PF_INET, SOCK_STREAM, 0);
348150286Srwatson	if (sock < 0)
349150286Srwatson		err(-1, "test_set_qlimit: socket(PF_INET, SOCK_STREAM)");
350150286Srwatson
351150286Srwatson	i = 0;
352150286Srwatson	ret = setsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &i, sizeof(i));
353150286Srwatson	if (ret < 0 && errno != ENOPROTOOPT) {
354150286Srwatson		warn("test_set_qlimit: setsockopt(SOL_SOCKET, "
355150286Srwatson		    "SO_LISTENQLIMIT, 0): unexpected error");
356150286Srwatson		close(sock);
357150286Srwatson	}
358150286Srwatson
359150286Srwatson	if (ret == 0) {
360150286Srwatson		warnx("test_set_qlimit: setsockopt(SOL_SOCKET, "
361150286Srwatson		    "SO_LISTENQLIMIT, 0) succeeded");
362150286Srwatson		close(sock);
363150286Srwatson		exit(-1);
364150286Srwatson	}
365150286Srwatson	close(sock);
366150286Srwatson}
367150286Srwatson
368150286Srwatsonint
369281360Sngiemain(void)
370150286Srwatson{
371150286Srwatson	size_t len;
372150286Srwatson
373150286Srwatson	len = sizeof(somaxconn);
374150286Srwatson	if (sysctlbyname("kern.ipc.somaxconn", &somaxconn, &len, NULL, 0)
375150286Srwatson	    < 0)
376150286Srwatson		err(-1, "sysctlbyname(kern.ipc.somaxconn)");
377150286Srwatson
378150286Srwatson	test_defaults();
379150286Srwatson	test_listen_update();
380150286Srwatson	test_set_qlimit();
381150286Srwatson
382150286Srwatson	return (0);
383150286Srwatson}
384