1 /*-
2  * Copyright (c) 2004 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/module.h>
31 #include <sys/socket.h>
32 
33 #include <netinet/in.h>
34 
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #define	ACCF_NAME	"dataready"
44 
45 /*
46  * A number of small tests to confirm that attaching ACCF_DATA accept filters
47  * to inet4 ports works as expected.  We test:
48  *
49  * - That no accept filter is attached on a newly created socket.
50  * - That bind() has no affect on the accept filter state.
51  * - That we can't attach an accept filter to a socket that isn't in the
52  *   listen state.
53  * - That after we fail to attach the filter, querying the kernel shows no
54  *   filter attached.
55  * - That we can attach an accept filter to a socket that is in the listen
56  *   state.
57  * - That once an accept filter is attached, we can query to make sure it is
58  *   attached.
59  * - That once an accept filter is attached, we can remove it and query to
60  *   make sure it is removed.
61  */
62 int
63 main(void)
64 {
65 	struct accept_filter_arg afa;
66 	struct sockaddr_in sin;
67 	socklen_t len;
68 	int lso, so, i, ret;
69 
70 	/* XXX: PLAIN_TEST_REQUIRE_MODULE "backport" for stable/9 */
71 	const char *_mod_name = "accf_data";
72 
73 	if (modfind(_mod_name) == -1) {
74 		printf("1..0 # SKIP - module %s could not be resolved: %s\n",
75 		    _mod_name, strerror(errno));
76 		_exit(0);
77 	}
78 	/* XXX: PLAIN_TEST_REQUIRE_MODULE for stable/9 */
79 
80 	printf("1..12\n");
81 
82 	/*
83 	 * Step 0. Open socket().
84 	 */
85 	lso = socket(PF_INET, SOCK_STREAM, 0);
86 	if (lso == -1)
87 		errx(-1, "not ok 1 - socket: %s", strerror(errno));
88 	printf("ok 1 - socket\n");
89 
90 	/*
91 	 * Step 1. After socket().  Should return EINVAL, since no accept
92 	 * filter should be attached.
93 	 */
94 	bzero(&afa, sizeof(afa));
95 	len = sizeof(afa);
96 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
97 	if (ret != -1)
98 		errx(-1, "not ok 2 - getsockopt() after socket() succeeded");
99 	if (errno != EINVAL)
100 		errx(-1, "not ok 2 - getsockopt() after socket() failed with "
101 		    "%d (%s)", errno, strerror(errno));
102 	printf("ok 2 - getsockopt\n");
103 
104 	/*
105 	 * Step 2. Bind().  Ideally this will succeed.
106 	 */
107 	setsockopt(lso, SOL_SOCKET, SO_REUSEADDR, &lso, sizeof(lso));
108 	bzero(&sin, sizeof(sin));
109 	sin.sin_len = sizeof(sin);
110 	sin.sin_family = AF_INET;
111 	sin.sin_port = htons(8080);
112 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
113 	if (bind(lso, (struct sockaddr *)&sin, sizeof(sin)) < 0)
114 		errx(-1, "not ok 3 - bind %s", strerror(errno));
115 	printf("ok 3 - bind\n");
116 
117 	/*
118 	 * Step 3: After bind().  getsockopt() should return EINVAL, since no
119 	 *  accept filter should be attached.
120 	 */
121 	len = sizeof(afa);
122 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
123 	if (ret != -1)
124 		errx(-1, "not ok 4 - getsockopt() after bind() succeeded");
125 	if (errno != EINVAL)
126 		errx(-1, "not ok 4 -  getsockopt() after bind() failed with %d (%s)",
127 		    errno, strerror(errno));
128 	printf("ok 4 - getsockopt\n");
129 
130 	/*
131 	 * Step 4: Setsockopt() before listen().  Should fail, since it's not
132 	 * yet a listen() socket.
133 	 */
134 	bzero(&afa, sizeof(afa));
135 	strncpy(afa.af_name, ACCF_NAME, sizeof(afa.af_name));
136 	ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
137 	if (ret == 0)
138 		errx(-1, "not ok 5 - setsockopt() before listen() succeeded");
139 	printf("ok 5 - setsockopt\n");
140 
141 	/*
142 	 * Step 5: Getsockopt() after pre-listen() setsockopt().  Should
143 	 * fail with EINVAL, since setsockopt() should have failed.
144 	 */
145 	len = sizeof(afa);
146 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
147 	if (ret == 0)
148 		errx(-1, "not ok 6 - getsockopt() after pre-listen() setsockopt() "
149 		    "succeeded");
150 	if (errno != EINVAL)
151 		errx(-1, "not ok 6 - pre-listen() getsockopt() failed with %d (%s)",
152 		    errno, strerror(errno));
153 	printf("ok 6 - getsockopt\n");
154 
155 	/*
156 	 * Step 6: listen().
157 	 */
158 	if (listen(lso, -1) < 0)
159 		errx(-1, "not ok 7 - listen: %s", strerror(errno));
160 	printf("ok 7 - listen\n");
161 
162 	/*
163 	 * Step 7: Getsockopt() after listen().  Should fail with EINVAL,
164 	 * since we have not installed accept filter yet.
165 	 */
166 	len = sizeof(afa);
167 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
168 	if (ret == 0)
169 		errx(-1, "not ok 8 - getsockopt() after listen() but before "
170 		    "setsockopt() succeeded");
171 	if (errno != EINVAL)
172 		errx(-1, "not ok 8 - getsockopt() after listen() but before "
173 		    "setsockopt() failed with %d (%s)", errno, strerror(errno));
174 	printf("ok 8 - getsockopt\n");
175 
176 	/*
177 	 * Step 8: After listen().  This call to setsockopt() should succeed.
178 	 */
179 	bzero(&afa, sizeof(afa));
180 	strncpy(afa.af_name, ACCF_NAME, sizeof(afa.af_name));
181 	ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
182 	if (ret != 0)
183 		errx(-1, "not ok 9 - setsockopt() after listen() failed with %d "
184 		    "(%s)", errno, strerror(errno));
185 	printf("ok 9 - setsockopt\n");
186 
187 	/*
188 	 * Step 9: After setsockopt().  Should succeed and identify
189 	 * ACCF_NAME.
190 	 */
191 	bzero(&afa, sizeof(afa));
192 	len = sizeof(afa);
193 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
194 	if (ret != 0)
195 		errx(-1, "not ok 10 - getsockopt() after listen() setsockopt() "
196 		    "failed with %d (%s)", errno, strerror(errno));
197 	if (len != sizeof(afa))
198 		errx(-1, "not ok 10 - getsockopt() after setsockopet()  after "
199 		    "listen() returned wrong size (got %d expected %zd)", len,
200 		    sizeof(afa));
201 	if (strcmp(afa.af_name, ACCF_NAME) != 0)
202 		errx(-1, "not ok 10 - getsockopt() after setsockopt() after "
203 		    "listen() mismatch (got %s expected %s)", afa.af_name,
204 		    ACCF_NAME);
205 	printf("ok 10 - getsockopt\n");
206 
207 	/*
208 	 * Step 10: Set listening socket to non blocking mode.  Open
209 	 * connection to our listening socket and try to accept.  Should
210 	 * no succeed.  Write a byte of data and try again.  Should accept.
211 	 */
212 	i = fcntl(lso, F_GETFL);
213 	if (i < 0)
214 		errx(-1, "not ok 11 - ioctl(F_GETFL): %s", strerror(errno));
215 	i |= O_NONBLOCK;
216 	if (fcntl(lso, F_SETFL, i) != 0)
217 		errx(-1, "not ok 11 - ioctl(F_SETFL): %s", strerror(errno));
218 	so = socket(PF_INET, SOCK_STREAM, 0);
219 	if (so == -1)
220 		errx(-1, "not ok 11 - socket: %s", strerror(errno));
221 	if (connect(so, (struct sockaddr *)&sin, sizeof(sin)) < 0)
222 		errx(-1, "not ok 11 - connect %s", strerror(errno));
223 	if (accept(lso, NULL, 0) != -1 && errno != EWOULDBLOCK)
224 		errx(-1, "not ok 11 - accept #1 %s", strerror(errno));
225 	if (write(so, "0", 1) != 1)
226 		errx(-1, "not ok 11 - write %s", strerror(errno));
227 	/*
228 	 * XXXGL: ugly, but we need to make sure that our write reaches
229 	 * remote side of the socket.
230 	 */
231 	usleep(10000);
232 	if (accept(lso, NULL, 0) < 1)
233 		errx(-1, "not ok 11 - accept #2 %s", strerror(errno));
234 	printf("ok 11 - accept\n");
235 
236 #if 1
237 	/*
238 	 * XXXGL: this doesn't belong to the test itself, but is known
239 	 * to examine rarely examined paths in the kernel.  Intentionally
240 	 * leave a socket on the incomplete queue, before the program
241 	 * exits.
242 	 */
243 	so = socket(PF_INET, SOCK_STREAM, 0);
244 	if (so == -1)
245 		errx(-1, "not ok 12 - socket: %s", strerror(errno));
246 	if (connect(so, (struct sockaddr *)&sin, sizeof(sin)) < 0)
247 		errx(-1, "not ok 12 - connect %s", strerror(errno));
248 #endif
249 
250 	/*
251 	 * Step 11: Remove accept filter.  After removing the accept filter
252 	 * getsockopt() should fail with EINVAL.
253 	 */
254 	ret = setsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0);
255 	if (ret != 0)
256 		errx(-1, "not ok 12 - setsockopt() after listen() "
257 		    "failed with %d (%s)", errno, strerror(errno));
258 	bzero(&afa, sizeof(afa));
259 	len = sizeof(afa);
260 	ret = getsockopt(lso, SOL_SOCKET, SO_ACCEPTFILTER, &afa, &len);
261 	if (ret == 0)
262 		errx(-1, "not ok 12 - getsockopt() after removing "
263 		    "the accept filter returns valid accept filter %s",
264 		    afa.af_name);
265 	if (errno != EINVAL)
266 		errx(-1, "not ok 12 - getsockopt() after removing the accept"
267 		    "filter failed with %d (%s)", errno, strerror(errno));
268 	printf("ok 12 - setsockopt\n");
269 
270 	close(lso);
271 	return (0);
272 }
273