1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 Michael J. Karels.
5 * Copyright (c) 2020 Netflix, Inc.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * This test is derived from tcp_connect_port_test.c.
32 */
33
34 #include <sys/param.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <sys/sysctl.h>
38
39 #include <netinet/in.h>
40
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <netdb.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48
49 #include <atf-c.h>
50
51 #define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.values"
52
53 #define PORT_FIRST 10000 /* normal default */
54 #define PORT_LAST 10003
55 #define LOOPS 10 /* 5 should be enough */
56
57 struct portrange {
58 int first;
59 int last;
60 };
61
62 /*
63 * Set first and last ports in the ipport range. Save the old values
64 * of the sysctls so they can be restored later.
65 */
66 static void
set_portrange(void)67 set_portrange(void)
68 {
69 int error, fd, first_new, last_new;
70 struct portrange save_ports;
71 size_t sysctlsz;
72
73 /*
74 * Pre-emptively unlink our restoration file, so we will do no
75 * restoration on error.
76 */
77 unlink(SYSCTLBAKFILE);
78
79 /*
80 * Set the net.inet.ip.portrange.{first,last} sysctls. Save the
81 * old values so we can restore them.
82 */
83 first_new = PORT_FIRST;
84 sysctlsz = sizeof(save_ports.first);
85 error = sysctlbyname("net.inet.ip.portrange.first", &save_ports.first,
86 &sysctlsz, &first_new, sizeof(first_new));
87 if (error) {
88 warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
89 "failed");
90 atf_tc_skip("Unable to set sysctl");
91 }
92 if (sysctlsz != sizeof(save_ports.first)) {
93 fprintf(stderr, "Error: unexpected sysctl value size "
94 "(expected %zu, actual %zu)\n", sizeof(save_ports.first),
95 sysctlsz);
96 goto restore_sysctl;
97 }
98
99 last_new = PORT_LAST;
100 sysctlsz = sizeof(save_ports.last);
101 error = sysctlbyname("net.inet.ip.portrange.last", &save_ports.last,
102 &sysctlsz, &last_new, sizeof(last_new));
103 if (error) {
104 warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
105 "failed");
106 atf_tc_skip("Unable to set sysctl");
107 }
108 if (sysctlsz != sizeof(save_ports.last)) {
109 fprintf(stderr, "Error: unexpected sysctl value size "
110 "(expected %zu, actual %zu)\n", sizeof(save_ports.last),
111 sysctlsz);
112 goto restore_sysctl;
113 }
114
115 /* Open the backup file, write the contents, and close it. */
116 fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,
117 S_IRUSR|S_IWUSR);
118 if (fd < 0) {
119 warn("error opening sysctl backup file");
120 goto restore_sysctl;
121 }
122 error = write(fd, &save_ports, sizeof(save_ports));
123 if (error < 0) {
124 warn("error writing saved value to sysctl backup file");
125 goto cleanup_and_restore;
126 }
127 if (error != (int)sizeof(save_ports)) {
128 fprintf(stderr,
129 "Error writing saved value to sysctl backup file: "
130 "(expected %zu, actual %d)\n", sizeof(save_ports), error);
131 goto cleanup_and_restore;
132 }
133 error = close(fd);
134 if (error) {
135 warn("error closing sysctl backup file");
136 cleanup_and_restore:
137 (void)close(fd);
138 (void)unlink(SYSCTLBAKFILE);
139 restore_sysctl:
140 sysctlsz = sizeof(save_ports.first);
141 (void)sysctlbyname("net.inet.ip.portrange.first", NULL,
142 NULL, &save_ports.first, sysctlsz);
143 sysctlsz = sizeof(save_ports.last);
144 (void)sysctlbyname("net.inet.ip.portrange.last", NULL,
145 NULL, &save_ports.last, sysctlsz);
146 atf_tc_skip("Error setting sysctl");
147 }
148 }
149
150 /*
151 * Restore the sysctl values from the backup file and delete the backup file.
152 */
153 static void
restore_portrange(void)154 restore_portrange(void)
155 {
156 int error, fd;
157 struct portrange save_ports;
158
159 /* Open the backup file, read the contents, close it, and delete it. */
160 fd = open(SYSCTLBAKFILE, O_RDONLY);
161 if (fd < 0) {
162 warn("error opening sysctl backup file");
163 return;
164 }
165 error = read(fd, &save_ports, sizeof(save_ports));
166 if (error < 0) {
167 warn("error reading saved values from sysctl backup file");
168 return;
169 }
170 if (error != (int)sizeof(save_ports)) {
171 fprintf(stderr,
172 "Error reading saved values from sysctl backup file: "
173 "(expected %zu, actual %d)\n", sizeof(save_ports), error);
174 return;
175 }
176 error = close(fd);
177 if (error)
178 warn("error closing sysctl backup file");
179 error = unlink(SYSCTLBAKFILE);
180 if (error)
181 warn("error removing sysctl backup file");
182
183 /* Restore the saved sysctl values. */
184 error = sysctlbyname("net.inet.ip.portrange.first", NULL, NULL,
185 &save_ports.first, sizeof(save_ports.first));
186 if (error)
187 warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
188 "failed while restoring value");
189 error = sysctlbyname("net.inet.ip.portrange.last", NULL, NULL,
190 &save_ports.last, sizeof(save_ports.last));
191 if (error)
192 warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
193 "failed while restoring value");
194 }
195
196 ATF_TC_WITH_CLEANUP(tcp_v4mapped_bind);
ATF_TC_HEAD(tcp_v4mapped_bind,tc)197 ATF_TC_HEAD(tcp_v4mapped_bind, tc)
198 {
199 /* root is only required for sysctls (setup and cleanup). */
200 atf_tc_set_md_var(tc, "require.user", "root");
201 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
202 atf_tc_set_md_var(tc, "descr",
203 "Check local port assignment with bind and mapped V4 addresses");
204 }
205 /*
206 * Create a listening IPv4 socket, then connect to it repeatedly using a
207 * bound IPv6 socket using a v4 mapped address. With a small port range,
208 * this should fail on a bind() call with EADDRNOTAVAIL. However, in
209 * previous systems, the bind() would succeed, binding a duplicate port,
210 * and then the connect would fail with EADDRINUSE. Make sure we get
211 * the right error.
212 */
ATF_TC_BODY(tcp_v4mapped_bind,tc)213 ATF_TC_BODY(tcp_v4mapped_bind, tc)
214 {
215 union {
216 struct sockaddr saddr;
217 struct sockaddr_in saddr4;
218 struct sockaddr_in6 saddr6;
219 } su_clnt, su_srvr, su_mapped;
220 struct addrinfo ai_hint, *aip;
221 socklen_t salen;
222 int csock, error, i, lsock, off = 0;
223 bool got_bind_error = false;
224
225 /*
226 * Set the net.inet.ip.portrange.{first,last} sysctls to use a small
227 * range, allowing us to generate port exhaustion quickly.
228 */
229 set_portrange();
230
231 /* Setup the listen socket. */
232 lsock = socket(PF_INET, SOCK_STREAM, 0);
233 ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",
234 strerror(errno));
235
236 memset(&su_srvr.saddr4, 0, sizeof(su_srvr.saddr4));
237 su_srvr.saddr4.sin_family = AF_INET;
238 error = bind(lsock, &su_srvr.saddr, sizeof(su_srvr.saddr4));
239 ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
240 error = listen(lsock, LOOPS + 1);
241 ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
242
243 /* Get the address of the listen socket. */
244 salen = sizeof(su_srvr);
245 error = getsockname(lsock, &su_srvr.saddr, &salen);
246 ATF_REQUIRE_MSG(error == 0,
247 "getsockname() for listen socket failed: %s",
248 strerror(errno));
249 ATF_REQUIRE_MSG(salen == sizeof(struct sockaddr_in),
250 "unexpected sockaddr size");
251 ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == sizeof(struct sockaddr_in),
252 "unexpected sa_len size");
253
254 /* Set up destination address for client sockets. */
255 memset(&ai_hint, 0, sizeof(ai_hint));
256 ai_hint.ai_family = AF_INET6;
257 ai_hint.ai_flags = AI_NUMERICHOST | AI_V4MAPPED;
258 error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
259 ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
260 memcpy(&su_mapped.saddr6, aip->ai_addr, sizeof(su_mapped.saddr6));
261 su_mapped.saddr6.sin6_port = su_srvr.saddr4.sin_port;
262 freeaddrinfo(aip);
263
264 /* Set up address to bind for client sockets (unspecified). */
265 memset(&su_clnt.saddr6, 0, sizeof(su_clnt.saddr6));
266 su_clnt.saddr6.sin6_family = AF_INET6;
267
268 /* Open connections in a loop. */
269 for (i = 0; i < LOOPS; i++) {
270 csock = socket(PF_INET6, SOCK_STREAM, 0);
271 ATF_REQUIRE_MSG(csock >= 0,
272 "socket() for client socket %d failed: %s",
273 i, strerror(errno));
274 error = setsockopt(csock, IPPROTO_IPV6, IPV6_V6ONLY, &off,
275 sizeof(off));
276 ATF_REQUIRE_MSG(error == 0,
277 "setsockopt(IPV6_ONLY = 0) failed: %s", strerror(errno));
278
279 /*
280 * A bind would not be necessary for operation, but
281 * provokes the error.
282 */
283 error = bind(csock, &su_clnt.saddr, sizeof(su_clnt.saddr6));
284 if (error != 0) {
285 if (errno == EADDRNOTAVAIL) { /* Success, expected */
286 got_bind_error = true;
287 break;
288 }
289 ATF_REQUIRE_MSG(error == 0,
290 "client bind %d failed: %s", i, strerror(errno));
291 }
292
293 error = connect(csock, &su_mapped.saddr, su_mapped.saddr.sa_len);
294 if (error != 0 && errno == EADDRINUSE) {
295 /* This is the specific error we were looking for. */
296 atf_tc_fail("client connect %d failed, "
297 " client had duplicate port: %s",
298 i, strerror(errno));
299 }
300 ATF_REQUIRE_MSG(error == 0,
301 "connect() for client socket %d failed: %s",
302 i, strerror(errno));
303
304 /*
305 * We don't accept the new socket from the server socket
306 * or close the client socket, as we want the ports to
307 * remain busy. The range is small enough that this is
308 * not a problem.
309 */
310 }
311 ATF_REQUIRE_MSG(i >= 1, "No successful connections");
312 ATF_REQUIRE_MSG(got_bind_error == true, "No expected bind error");
313
314 ATF_REQUIRE(close(lsock) == 0);
315 }
ATF_TC_CLEANUP(tcp_v4mapped_bind,tc)316 ATF_TC_CLEANUP(tcp_v4mapped_bind, tc)
317 {
318 restore_portrange();
319 }
320
321 ATF_TC(udp_v4mapped_sendto);
ATF_TC_HEAD(udp_v4mapped_sendto,tc)322 ATF_TC_HEAD(udp_v4mapped_sendto, tc)
323 {
324 atf_tc_set_md_var(tc, "descr",
325 "Validate sendto() with a v4-mapped address and a v6-only socket");
326 }
ATF_TC_BODY(udp_v4mapped_sendto,tc)327 ATF_TC_BODY(udp_v4mapped_sendto, tc)
328 {
329 struct addrinfo ai_hint, *aip;
330 struct sockaddr_in sin;
331 struct sockaddr_in6 sin6;
332 ssize_t n;
333 socklen_t salen;
334 int error, ls, s, zero;
335 short port;
336 char ch;
337
338 ls = socket(PF_INET, SOCK_DGRAM, 0);
339 ATF_REQUIRE(ls >= 0);
340
341 memset(&ai_hint, 0, sizeof(ai_hint));
342 ai_hint.ai_family = AF_INET;
343 ai_hint.ai_flags = AI_NUMERICHOST;
344 error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
345 ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
346 memcpy(&sin, aip->ai_addr, sizeof(sin));
347
348 error = bind(ls, (struct sockaddr *)&sin, sizeof(sin));
349 ATF_REQUIRE_MSG(error == 0, "bind: %s", strerror(errno));
350 salen = sizeof(sin);
351 error = getsockname(ls, (struct sockaddr *)&sin, &salen);
352 ATF_REQUIRE_MSG(error == 0,
353 "getsockname() for listen socket failed: %s", strerror(errno));
354 ATF_REQUIRE_MSG(salen == sizeof(struct sockaddr_in),
355 "unexpected sockaddr size");
356 port = sin.sin_port;
357
358 s = socket(PF_INET6, SOCK_DGRAM, 0);
359 ATF_REQUIRE(s >= 0);
360
361 memset(&ai_hint, 0, sizeof(ai_hint));
362 ai_hint.ai_family = AF_INET6;
363 ai_hint.ai_flags = AI_NUMERICHOST | AI_V4MAPPED;
364 error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
365 ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
366 memcpy(&sin6, aip->ai_addr, sizeof(sin6));
367 sin6.sin6_port = port;
368 freeaddrinfo(aip);
369
370 ch = 0x42;
371 n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
372 ATF_REQUIRE_ERRNO(EINVAL, n == -1);
373
374 zero = 0;
375 error = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));
376 ATF_REQUIRE_MSG(error == 0,
377 "setsockopt(IPV6_V6ONLY) failed: %s", strerror(errno));
378
379 ch = 0x42;
380 n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
381 ATF_REQUIRE_MSG(n == 1, "sendto() failed: %s", strerror(errno));
382
383 ch = 0;
384 n = recv(ls, &ch, 1, 0);
385 ATF_REQUIRE_MSG(n == 1, "recv() failed: %s", strerror(errno));
386 ATF_REQUIRE(ch == 0x42);
387
388 ATF_REQUIRE(close(s) == 0);
389 ATF_REQUIRE(close(ls) == 0);
390 }
391
ATF_TP_ADD_TCS(tp)392 ATF_TP_ADD_TCS(tp)
393 {
394 ATF_TP_ADD_TC(tp, tcp_v4mapped_bind);
395 ATF_TP_ADD_TC(tp, udp_v4mapped_sendto);
396
397 return (atf_no_error());
398 }
399