Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/netinet/ip6_v4mapped_test.c
39483 views
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
}
399
400