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/cdefs.h>
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/stat.h>
38 #include <sys/sysctl.h>
39
40 #include <netinet/in.h>
41
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <netdb.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49
50 #include <atf-c.h>
51
52 #define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.values"
53
54 #define PORT_FIRST 10000 /* normal default */
55 #define PORT_LAST 10003
56 #define LOOPS 10 /* 5 should be enough */
57
58 struct portrange {
59 int first;
60 int last;
61 };
62
63 /*
64 * Set first and last ports in the ipport range. Save the old values
65 * of the sysctls so they can be restored later.
66 */
67 static void
set_portrange(void)68 set_portrange(void)
69 {
70 int error, fd, first_new, last_new;
71 struct portrange save_ports;
72 size_t sysctlsz;
73
74 /*
75 * Pre-emptively unlink our restoration file, so we will do no
76 * restoration on error.
77 */
78 unlink(SYSCTLBAKFILE);
79
80 /*
81 * Set the net.inet.ip.portrange.{first,last} sysctls. Save the
82 * old values so we can restore them.
83 */
84 first_new = PORT_FIRST;
85 sysctlsz = sizeof(save_ports.first);
86 error = sysctlbyname("net.inet.ip.portrange.first", &save_ports.first,
87 &sysctlsz, &first_new, sizeof(first_new));
88 if (error) {
89 warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
90 "failed");
91 atf_tc_skip("Unable to set sysctl");
92 }
93 if (sysctlsz != sizeof(save_ports.first)) {
94 fprintf(stderr, "Error: unexpected sysctl value size "
95 "(expected %zu, actual %zu)\n", sizeof(save_ports.first),
96 sysctlsz);
97 goto restore_sysctl;
98 }
99
100 last_new = PORT_LAST;
101 sysctlsz = sizeof(save_ports.last);
102 error = sysctlbyname("net.inet.ip.portrange.last", &save_ports.last,
103 &sysctlsz, &last_new, sizeof(last_new));
104 if (error) {
105 warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
106 "failed");
107 atf_tc_skip("Unable to set sysctl");
108 }
109 if (sysctlsz != sizeof(save_ports.last)) {
110 fprintf(stderr, "Error: unexpected sysctl value size "
111 "(expected %zu, actual %zu)\n", sizeof(save_ports.last),
112 sysctlsz);
113 goto restore_sysctl;
114 }
115
116 /* Open the backup file, write the contents, and close it. */
117 fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,
118 S_IRUSR|S_IWUSR);
119 if (fd < 0) {
120 warn("error opening sysctl backup file");
121 goto restore_sysctl;
122 }
123 error = write(fd, &save_ports, sizeof(save_ports));
124 if (error < 0) {
125 warn("error writing saved value to sysctl backup file");
126 goto cleanup_and_restore;
127 }
128 if (error != (int)sizeof(save_ports)) {
129 fprintf(stderr,
130 "Error writing saved value to sysctl backup file: "
131 "(expected %zu, actual %d)\n", sizeof(save_ports), error);
132 goto cleanup_and_restore;
133 }
134 error = close(fd);
135 if (error) {
136 warn("error closing sysctl backup file");
137 cleanup_and_restore:
138 (void)close(fd);
139 (void)unlink(SYSCTLBAKFILE);
140 restore_sysctl:
141 sysctlsz = sizeof(save_ports.first);
142 (void)sysctlbyname("net.inet.ip.portrange.first", NULL,
143 NULL, &save_ports.first, sysctlsz);
144 sysctlsz = sizeof(save_ports.last);
145 (void)sysctlbyname("net.inet.ip.portrange.last", NULL,
146 NULL, &save_ports.last, sysctlsz);
147 atf_tc_skip("Error setting sysctl");
148 }
149 }
150
151 /*
152 * Restore the sysctl values from the backup file and delete the backup file.
153 */
154 static void
restore_portrange(void)155 restore_portrange(void)
156 {
157 int error, fd;
158 struct portrange save_ports;
159
160 /* Open the backup file, read the contents, close it, and delete it. */
161 fd = open(SYSCTLBAKFILE, O_RDONLY);
162 if (fd < 0) {
163 warn("error opening sysctl backup file");
164 return;
165 }
166 error = read(fd, &save_ports, sizeof(save_ports));
167 if (error < 0) {
168 warn("error reading saved values from sysctl backup file");
169 return;
170 }
171 if (error != (int)sizeof(save_ports)) {
172 fprintf(stderr,
173 "Error reading saved values from sysctl backup file: "
174 "(expected %zu, actual %d)\n", sizeof(save_ports), error);
175 return;
176 }
177 error = close(fd);
178 if (error)
179 warn("error closing sysctl backup file");
180 error = unlink(SYSCTLBAKFILE);
181 if (error)
182 warn("error removing sysctl backup file");
183
184 /* Restore the saved sysctl values. */
185 error = sysctlbyname("net.inet.ip.portrange.first", NULL, NULL,
186 &save_ports.first, sizeof(save_ports.first));
187 if (error)
188 warn("sysctlbyname(\"net.inet.ip.portrange.first\") "
189 "failed while restoring value");
190 error = sysctlbyname("net.inet.ip.portrange.last", NULL, NULL,
191 &save_ports.last, sizeof(save_ports.last));
192 if (error)
193 warn("sysctlbyname(\"net.inet.ip.portrange.last\") "
194 "failed while restoring value");
195 }
196
197 ATF_TC_WITH_CLEANUP(tcp_v4mapped_bind);
ATF_TC_HEAD(tcp_v4mapped_bind,tc)198 ATF_TC_HEAD(tcp_v4mapped_bind, tc)
199 {
200 /* root is only required for sysctls (setup and cleanup). */
201 atf_tc_set_md_var(tc, "require.user", "root");
202 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
203 atf_tc_set_md_var(tc, "descr",
204 "Check local port assignment with bind and mapped V4 addresses");
205 }
206 /*
207 * Create a listening IPv4 socket, then connect to it repeatedly using a
208 * bound IPv6 socket using a v4 mapped address. With a small port range,
209 * this should fail on a bind() call with EADDRNOTAVAIL. However, in
210 * previous systems, the bind() would succeed, binding a duplicate port,
211 * and then the connect would fail with EADDRINUSE. Make sure we get
212 * the right error.
213 */
ATF_TC_BODY(tcp_v4mapped_bind,tc)214 ATF_TC_BODY(tcp_v4mapped_bind, tc)
215 {
216 union {
217 struct sockaddr saddr;
218 struct sockaddr_in saddr4;
219 struct sockaddr_in6 saddr6;
220 } su_clnt, su_srvr, su_mapped;
221 struct addrinfo ai_hint, *aip;
222 socklen_t salen;
223 int csock, error, i, lsock, off = 0;
224 bool got_bind_error = false;
225
226 /*
227 * Set the net.inet.ip.portrange.{first,last} sysctls to use a small
228 * range, allowing us to generate port exhaustion quickly.
229 */
230 set_portrange();
231
232 /* Setup the listen socket. */
233 lsock = socket(PF_INET, SOCK_STREAM, 0);
234 ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",
235 strerror(errno));
236
237 memset(&su_srvr.saddr4, 0, sizeof(su_srvr.saddr4));
238 su_srvr.saddr4.sin_family = AF_INET;
239 error = bind(lsock, &su_srvr.saddr, sizeof(su_srvr.saddr4));
240 ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
241 error = listen(lsock, LOOPS + 1);
242 ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
243
244 /* Get the address of the listen socket. */
245 salen = sizeof(su_srvr);
246 error = getsockname(lsock, &su_srvr.saddr, &salen);
247 ATF_REQUIRE_MSG(error == 0,
248 "getsockname() for listen socket failed: %s",
249 strerror(errno));
250 ATF_REQUIRE_MSG(salen == sizeof(struct sockaddr_in),
251 "unexpected sockaddr size");
252 ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == sizeof(struct sockaddr_in),
253 "unexpected sa_len size");
254
255 /* Set up destination address for client sockets. */
256 memset(&ai_hint, 0, sizeof(ai_hint));
257 ai_hint.ai_family = AF_INET6;
258 ai_hint.ai_flags = AI_NUMERICHOST | AI_V4MAPPED;
259 error = getaddrinfo("127.0.0.1", NULL, &ai_hint, &aip);
260 ATF_REQUIRE_MSG(error == 0, "getaddrinfo: %s", gai_strerror(error));
261 memcpy(&su_mapped.saddr6, aip->ai_addr, sizeof(su_mapped.saddr6));
262 su_mapped.saddr6.sin6_port = su_srvr.saddr4.sin_port;
263 freeaddrinfo(aip);
264
265 /* Set up address to bind for client sockets (unspecified). */
266 memset(&su_clnt.saddr6, 0, sizeof(su_clnt.saddr6));
267 su_clnt.saddr6.sin6_family = AF_INET6;
268
269 /* Open connections in a loop. */
270 for (i = 0; i < LOOPS; i++) {
271 csock = socket(PF_INET6, SOCK_STREAM, 0);
272 ATF_REQUIRE_MSG(csock >= 0,
273 "socket() for client socket %d failed: %s",
274 i, strerror(errno));
275 error = setsockopt(csock, IPPROTO_IPV6, IPV6_V6ONLY, &off,
276 sizeof(off));
277 ATF_REQUIRE_MSG(error == 0,
278 "setsockopt(IPV6_ONLY = 0) failed: %s", strerror(errno));
279
280 /*
281 * A bind would not be necessary for operation, but
282 * provokes the error.
283 */
284 error = bind(csock, &su_clnt.saddr, sizeof(su_clnt.saddr6));
285 if (error != 0) {
286 if (errno == EADDRNOTAVAIL) { /* Success, expected */
287 got_bind_error = true;
288 break;
289 }
290 ATF_REQUIRE_MSG(error == 0,
291 "client bind %d failed: %s", i, strerror(errno));
292 }
293
294 error = connect(csock, &su_mapped.saddr, su_mapped.saddr.sa_len);
295 if (error != 0 && errno == EADDRINUSE) {
296 /* This is the specific error we were looking for. */
297 ATF_REQUIRE_MSG(error == 0,
298 "client connect %d failed, "
299 " client had duplicate port: %s",
300 i, strerror(errno));
301 }
302 ATF_REQUIRE_MSG(error == 0,
303 "connect() for client socket %d failed: %s",
304 i, strerror(errno));
305
306 /*
307 * We don't accept the new socket from the server socket
308 * or close the client socket, as we want the ports to
309 * remain busy. The range is small enough that this is
310 * not a problem.
311 */
312 }
313 ATF_REQUIRE_MSG(i >= 1, "No successful connections");
314 ATF_REQUIRE_MSG(got_bind_error == true, "No expected bind error");
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
369 ch = 0x42;
370 n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
371 ATF_REQUIRE_ERRNO(EINVAL, n == -1);
372
373 zero = 0;
374 error = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero));
375 ATF_REQUIRE_MSG(error == 0,
376 "setsockopt(IPV6_V6ONLY) failed: %s", strerror(errno));
377
378 ch = 0x42;
379 n = sendto(s, &ch, 1, 0, (struct sockaddr *)&sin6, sizeof(sin6));
380 ATF_REQUIRE_MSG(n == 1, "sendto() failed: %s", strerror(errno));
381
382 ch = 0;
383 n = recv(ls, &ch, 1, 0);
384 ATF_REQUIRE_MSG(n == 1, "recv() failed: %s", strerror(errno));
385 ATF_REQUIRE(ch == 0x42);
386 }
387
ATF_TP_ADD_TCS(tp)388 ATF_TP_ADD_TCS(tp)
389 {
390 ATF_TP_ADD_TC(tp, tcp_v4mapped_bind);
391 ATF_TP_ADD_TC(tp, udp_v4mapped_sendto);
392
393 return (atf_no_error());
394 }
395