]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/sys/netinet/ip6_v4mapped_test.c
cdefs: Remove __GNUCLIKE_CTOR_SECTION_HANDLING, it's unused
[FreeBSD/FreeBSD.git] / tests / sys / netinet / ip6_v4mapped_test.c
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
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
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);
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  */
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 }
316 ATF_TC_CLEANUP(tcp_v4mapped_bind, tc)
317 {
318         restore_portrange();
319 }
320
321 ATF_TC(udp_v4mapped_sendto);
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 }
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
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 }