136c52a52SJonathan T. Looney /*-
236c52a52SJonathan T. Looney * SPDX-License-Identifier: BSD-2-Clause
336c52a52SJonathan T. Looney *
436c52a52SJonathan T. Looney * Copyright (c) 2020 Netflix, Inc.
536c52a52SJonathan T. Looney *
636c52a52SJonathan T. Looney * Redistribution and use in source and binary forms, with or without
736c52a52SJonathan T. Looney * modification, are permitted provided that the following conditions are
836c52a52SJonathan T. Looney * met:
936c52a52SJonathan T. Looney * 1. Redistributions of source code must retain the above copyright
1036c52a52SJonathan T. Looney * notice, this list of conditions and the following disclaimer.
1136c52a52SJonathan T. Looney * 2. Redistributions in binary form must reproduce the above copyright
1236c52a52SJonathan T. Looney * notice, this list of conditions and the following disclaimer in
1336c52a52SJonathan T. Looney * the documentation and/or other materials provided with the
1436c52a52SJonathan T. Looney * distribution.
1536c52a52SJonathan T. Looney *
1636c52a52SJonathan T. Looney * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1736c52a52SJonathan T. Looney * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1836c52a52SJonathan T. Looney * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1936c52a52SJonathan T. Looney * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2036c52a52SJonathan T. Looney * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2136c52a52SJonathan T. Looney * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2236c52a52SJonathan T. Looney * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2336c52a52SJonathan T. Looney * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2436c52a52SJonathan T. Looney * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2536c52a52SJonathan T. Looney * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2636c52a52SJonathan T. Looney * SUCH DAMAGE.
2736c52a52SJonathan T. Looney */
2836c52a52SJonathan T. Looney
2936c52a52SJonathan T. Looney #include <sys/param.h>
3036c52a52SJonathan T. Looney #include <sys/socket.h>
3136c52a52SJonathan T. Looney #include <sys/stat.h>
3236c52a52SJonathan T. Looney #include <sys/sysctl.h>
3336c52a52SJonathan T. Looney
3436c52a52SJonathan T. Looney #include <netinet/in.h>
3536c52a52SJonathan T. Looney
3636c52a52SJonathan T. Looney #include <err.h>
3736c52a52SJonathan T. Looney #include <errno.h>
3836c52a52SJonathan T. Looney #include <fcntl.h>
3936c52a52SJonathan T. Looney #include <stdio.h>
4036c52a52SJonathan T. Looney #include <stdlib.h>
4136c52a52SJonathan T. Looney #include <unistd.h>
4236c52a52SJonathan T. Looney
4336c52a52SJonathan T. Looney #include <atf-c.h>
4436c52a52SJonathan T. Looney
4536c52a52SJonathan T. Looney #define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.randomized"
4636c52a52SJonathan T. Looney
4736c52a52SJonathan T. Looney /*
4836c52a52SJonathan T. Looney * Check if port allocation is randomized. If so, update it. Save the old
4936c52a52SJonathan T. Looney * value of the sysctl so it can be updated later.
5036c52a52SJonathan T. Looney */
5136c52a52SJonathan T. Looney static void
disable_random_ports(void)5236c52a52SJonathan T. Looney disable_random_ports(void)
5336c52a52SJonathan T. Looney {
5436c52a52SJonathan T. Looney int error, fd, random_new, random_save;
5536c52a52SJonathan T. Looney size_t sysctlsz;
5636c52a52SJonathan T. Looney
5736c52a52SJonathan T. Looney /*
5836c52a52SJonathan T. Looney * Pre-emptively unlink our restoration file, so we will do no
5936c52a52SJonathan T. Looney * restoration on error.
6036c52a52SJonathan T. Looney */
6136c52a52SJonathan T. Looney unlink(SYSCTLBAKFILE);
6236c52a52SJonathan T. Looney
6336c52a52SJonathan T. Looney /*
6436c52a52SJonathan T. Looney * Disable the net.inet.ip.portrange.randomized sysctl. Save the
6536c52a52SJonathan T. Looney * old value so we can restore it, if necessary.
6636c52a52SJonathan T. Looney */
6736c52a52SJonathan T. Looney random_new = 0;
6836c52a52SJonathan T. Looney sysctlsz = sizeof(random_save);
6936c52a52SJonathan T. Looney error = sysctlbyname("net.inet.ip.portrange.randomized", &random_save,
7036c52a52SJonathan T. Looney &sysctlsz, &random_new, sizeof(random_new));
7136c52a52SJonathan T. Looney if (error) {
7236c52a52SJonathan T. Looney warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "
7336c52a52SJonathan T. Looney "failed");
7436c52a52SJonathan T. Looney atf_tc_skip("Unable to set sysctl");
7536c52a52SJonathan T. Looney }
7636c52a52SJonathan T. Looney if (sysctlsz != sizeof(random_save)) {
7736c52a52SJonathan T. Looney fprintf(stderr, "Error: unexpected sysctl value size "
7836c52a52SJonathan T. Looney "(expected %zu, actual %zu)\n", sizeof(random_save),
7936c52a52SJonathan T. Looney sysctlsz);
8036c52a52SJonathan T. Looney goto restore_sysctl;
8136c52a52SJonathan T. Looney }
8236c52a52SJonathan T. Looney
8336c52a52SJonathan T. Looney /* Open the backup file, write the contents, and close it. */
8436c52a52SJonathan T. Looney fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,
8536c52a52SJonathan T. Looney S_IRUSR|S_IWUSR);
8636c52a52SJonathan T. Looney if (fd < 0) {
8736c52a52SJonathan T. Looney warn("error opening sysctl backup file");
8836c52a52SJonathan T. Looney goto restore_sysctl;
8936c52a52SJonathan T. Looney }
9036c52a52SJonathan T. Looney error = write(fd, &random_save, sizeof(random_save));
9136c52a52SJonathan T. Looney if (error < 0) {
9236c52a52SJonathan T. Looney warn("error writing saved value to sysctl backup file");
9336c52a52SJonathan T. Looney goto cleanup_and_restore;
9436c52a52SJonathan T. Looney }
9536c52a52SJonathan T. Looney if (error != (int)sizeof(random_save)) {
9636c52a52SJonathan T. Looney fprintf(stderr,
9736c52a52SJonathan T. Looney "Error writing saved value to sysctl backup file: "
9836c52a52SJonathan T. Looney "(expected %zu, actual %d)\n", sizeof(random_save), error);
9936c52a52SJonathan T. Looney goto cleanup_and_restore;
10036c52a52SJonathan T. Looney }
10136c52a52SJonathan T. Looney error = close(fd);
10236c52a52SJonathan T. Looney if (error) {
10336c52a52SJonathan T. Looney warn("error closing sysctl backup file");
10436c52a52SJonathan T. Looney cleanup_and_restore:
10536c52a52SJonathan T. Looney (void)close(fd);
10636c52a52SJonathan T. Looney (void)unlink(SYSCTLBAKFILE);
10736c52a52SJonathan T. Looney restore_sysctl:
10836c52a52SJonathan T. Looney (void)sysctlbyname("net.inet.ip.portrange.randomized", NULL,
10936c52a52SJonathan T. Looney NULL, &random_save, sysctlsz);
11036c52a52SJonathan T. Looney atf_tc_skip("Error setting sysctl");
11136c52a52SJonathan T. Looney }
11236c52a52SJonathan T. Looney }
11336c52a52SJonathan T. Looney
11436c52a52SJonathan T. Looney /*
11536c52a52SJonathan T. Looney * Restore the sysctl value from the backup file and delete the backup file.
11636c52a52SJonathan T. Looney */
11736c52a52SJonathan T. Looney static void
restore_random_ports(void)11836c52a52SJonathan T. Looney restore_random_ports(void)
11936c52a52SJonathan T. Looney {
12036c52a52SJonathan T. Looney int error, fd, random_save;
12136c52a52SJonathan T. Looney
12236c52a52SJonathan T. Looney /* Open the backup file, read the contents, close it, and delete it. */
12336c52a52SJonathan T. Looney fd = open(SYSCTLBAKFILE, O_RDONLY);
12436c52a52SJonathan T. Looney if (fd < 0) {
12536c52a52SJonathan T. Looney warn("error opening sysctl backup file");
12636c52a52SJonathan T. Looney return;
12736c52a52SJonathan T. Looney }
12836c52a52SJonathan T. Looney error = read(fd, &random_save, sizeof(random_save));
12936c52a52SJonathan T. Looney if (error < 0) {
13036c52a52SJonathan T. Looney warn("error reading saved value from sysctl backup file");
13136c52a52SJonathan T. Looney return;
13236c52a52SJonathan T. Looney }
13336c52a52SJonathan T. Looney if (error != (int)sizeof(random_save)) {
13436c52a52SJonathan T. Looney fprintf(stderr,
13536c52a52SJonathan T. Looney "Error reading saved value from sysctl backup file: "
13636c52a52SJonathan T. Looney "(expected %zu, actual %d)\n", sizeof(random_save), error);
13736c52a52SJonathan T. Looney return;
13836c52a52SJonathan T. Looney }
13936c52a52SJonathan T. Looney error = close(fd);
14036c52a52SJonathan T. Looney if (error)
14136c52a52SJonathan T. Looney warn("error closing sysctl backup file");
14236c52a52SJonathan T. Looney error = unlink(SYSCTLBAKFILE);
14336c52a52SJonathan T. Looney if (error)
14436c52a52SJonathan T. Looney warn("error removing sysctl backup file");
14536c52a52SJonathan T. Looney
14636c52a52SJonathan T. Looney /* Restore the saved sysctl value. */
14736c52a52SJonathan T. Looney error = sysctlbyname("net.inet.ip.portrange.randomized", NULL, NULL,
14836c52a52SJonathan T. Looney &random_save, sizeof(random_save));
14936c52a52SJonathan T. Looney if (error)
15036c52a52SJonathan T. Looney warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "
15136c52a52SJonathan T. Looney "failed while restoring value");
15236c52a52SJonathan T. Looney }
15336c52a52SJonathan T. Looney
15436c52a52SJonathan T. Looney /*
15536c52a52SJonathan T. Looney * Given a domain and sockaddr, open a listening socket with automatic port
15636c52a52SJonathan T. Looney * selection. Then, try to connect 64K times. Ensure the connected socket never
15736c52a52SJonathan T. Looney * uses an overlapping port.
15836c52a52SJonathan T. Looney */
15936c52a52SJonathan T. Looney static void
connect_loop(int domain,const struct sockaddr * addr)16036c52a52SJonathan T. Looney connect_loop(int domain, const struct sockaddr *addr)
16136c52a52SJonathan T. Looney {
16236c52a52SJonathan T. Looney union {
16336c52a52SJonathan T. Looney struct sockaddr saddr;
16436c52a52SJonathan T. Looney struct sockaddr_in saddr4;
16536c52a52SJonathan T. Looney struct sockaddr_in6 saddr6;
16636c52a52SJonathan T. Looney } su_clnt, su_srvr;
16736c52a52SJonathan T. Looney socklen_t salen;
16836c52a52SJonathan T. Looney int asock, csock, error, i, lsock;
16936c52a52SJonathan T. Looney const struct linger lopt = { 1, 0 };
17036c52a52SJonathan T. Looney
17136c52a52SJonathan T. Looney /*
17236c52a52SJonathan T. Looney * Disable the net.inet.ip.portrange.randomized sysctl. Assuming an
17336c52a52SJonathan T. Looney * otherwise idle system, this makes the kernel try all possible
17436c52a52SJonathan T. Looney * ports sequentially and makes it more likely it will try the
17536c52a52SJonathan T. Looney * port on which we have a listening socket.
17636c52a52SJonathan T. Looney */
17736c52a52SJonathan T. Looney disable_random_ports();
17836c52a52SJonathan T. Looney
17936c52a52SJonathan T. Looney /* Setup the listen socket. */
18036c52a52SJonathan T. Looney lsock = socket(domain, SOCK_STREAM, 0);
18136c52a52SJonathan T. Looney ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",
18236c52a52SJonathan T. Looney strerror(errno));
18336c52a52SJonathan T. Looney error = bind(lsock, addr, addr->sa_len);
18436c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
18536c52a52SJonathan T. Looney error = listen(lsock, 1);
18636c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));
18736c52a52SJonathan T. Looney
18836c52a52SJonathan T. Looney /*
18936c52a52SJonathan T. Looney * Get the address of the listen socket, which will be the destination
19036c52a52SJonathan T. Looney * address for our connection attempts.
19136c52a52SJonathan T. Looney */
19236c52a52SJonathan T. Looney salen = sizeof(su_srvr);
19336c52a52SJonathan T. Looney error = getsockname(lsock, &su_srvr.saddr, &salen);
19436c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
19536c52a52SJonathan T. Looney "getsockname() for listen socket failed: %s",
19636c52a52SJonathan T. Looney strerror(errno));
19736c52a52SJonathan T. Looney ATF_REQUIRE_MSG(salen == (domain == PF_INET ?
19836c52a52SJonathan T. Looney sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
19936c52a52SJonathan T. Looney "unexpected sockaddr size");
20036c52a52SJonathan T. Looney ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == (domain == PF_INET ?
20136c52a52SJonathan T. Looney sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
20236c52a52SJonathan T. Looney "unexpected sa_len size");
20336c52a52SJonathan T. Looney
20436c52a52SJonathan T. Looney /* Open 64K connections in a loop. */
20536c52a52SJonathan T. Looney for (i = 0; i < 65536; i++) {
20636c52a52SJonathan T. Looney csock = socket(domain, SOCK_STREAM, 0);
20736c52a52SJonathan T. Looney ATF_REQUIRE_MSG(csock >= 0,
20836c52a52SJonathan T. Looney "socket() for client socket %d failed: %s",
20936c52a52SJonathan T. Looney i, strerror(errno));
21036c52a52SJonathan T. Looney
21136c52a52SJonathan T. Looney error = connect(csock, &su_srvr.saddr, su_srvr.saddr.sa_len);
21236c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
21336c52a52SJonathan T. Looney "connect() for client socket %d failed: %s",
21436c52a52SJonathan T. Looney i, strerror(errno));
21536c52a52SJonathan T. Looney
21636c52a52SJonathan T. Looney error = setsockopt(csock, SOL_SOCKET, SO_LINGER, &lopt,
21736c52a52SJonathan T. Looney sizeof(lopt));
21836c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
21936c52a52SJonathan T. Looney "Setting linger for client socket %d failed: %s",
22036c52a52SJonathan T. Looney i, strerror(errno));
22136c52a52SJonathan T. Looney
22236c52a52SJonathan T. Looney /* Ascertain the client socket address. */
22336c52a52SJonathan T. Looney salen = sizeof(su_clnt);
22436c52a52SJonathan T. Looney error = getsockname(csock, &su_clnt.saddr, &salen);
22536c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
22636c52a52SJonathan T. Looney "getsockname() for client socket %d failed: %s",
22736c52a52SJonathan T. Looney i, strerror(errno));
22836c52a52SJonathan T. Looney ATF_REQUIRE_MSG(salen == (domain == PF_INET ?
22936c52a52SJonathan T. Looney sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),
23036c52a52SJonathan T. Looney "unexpected sockaddr size for client socket %d", i);
23136c52a52SJonathan T. Looney
23236c52a52SJonathan T. Looney /* Ensure the ports do not match. */
23336c52a52SJonathan T. Looney switch (domain) {
23436c52a52SJonathan T. Looney case PF_INET:
23536c52a52SJonathan T. Looney ATF_REQUIRE_MSG(su_clnt.saddr4.sin_port !=
23636c52a52SJonathan T. Looney su_srvr.saddr4.sin_port,
23736c52a52SJonathan T. Looney "client socket %d using the same port as server",
23836c52a52SJonathan T. Looney i);
23936c52a52SJonathan T. Looney break;
24036c52a52SJonathan T. Looney case PF_INET6:
24136c52a52SJonathan T. Looney ATF_REQUIRE_MSG(su_clnt.saddr6.sin6_port !=
24236c52a52SJonathan T. Looney su_srvr.saddr6.sin6_port,
24336c52a52SJonathan T. Looney "client socket %d using the same port as server",
24436c52a52SJonathan T. Looney i);
24536c52a52SJonathan T. Looney break;
24636c52a52SJonathan T. Looney }
24736c52a52SJonathan T. Looney
24836c52a52SJonathan T. Looney /* Accept the socket and close both ends. */
24936c52a52SJonathan T. Looney asock = accept(lsock, NULL, NULL);
25036c52a52SJonathan T. Looney ATF_REQUIRE_MSG(asock >= 0,
25136c52a52SJonathan T. Looney "accept() failed for client socket %d: %s",
25236c52a52SJonathan T. Looney i, strerror(errno));
25336c52a52SJonathan T. Looney
25436c52a52SJonathan T. Looney error = close(asock);
25536c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
25636c52a52SJonathan T. Looney "close() failed for accepted socket %d: %s",
25736c52a52SJonathan T. Looney i, strerror(errno));
25836c52a52SJonathan T. Looney
25936c52a52SJonathan T. Looney error = close(csock);
26036c52a52SJonathan T. Looney ATF_REQUIRE_MSG(error == 0,
26136c52a52SJonathan T. Looney "close() failed for client socket %d: %s",
26236c52a52SJonathan T. Looney i, strerror(errno));
26336c52a52SJonathan T. Looney }
26436c52a52SJonathan T. Looney }
26536c52a52SJonathan T. Looney
26636c52a52SJonathan T. Looney ATF_TC_WITH_CLEANUP(basic_ipv4);
ATF_TC_HEAD(basic_ipv4,tc)26736c52a52SJonathan T. Looney ATF_TC_HEAD(basic_ipv4, tc)
26836c52a52SJonathan T. Looney {
26936c52a52SJonathan T. Looney
27036c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "require.user", "root");
27136c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
27236c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "descr",
27336c52a52SJonathan T. Looney "Check automatic local port assignment during TCP connect calls");
27436c52a52SJonathan T. Looney }
27536c52a52SJonathan T. Looney
ATF_TC_BODY(basic_ipv4,tc)27636c52a52SJonathan T. Looney ATF_TC_BODY(basic_ipv4, tc)
27736c52a52SJonathan T. Looney {
27836c52a52SJonathan T. Looney struct sockaddr_in saddr4;
27936c52a52SJonathan T. Looney
28036c52a52SJonathan T. Looney memset(&saddr4, 0, sizeof(saddr4));
28136c52a52SJonathan T. Looney saddr4.sin_len = sizeof(saddr4);
28236c52a52SJonathan T. Looney saddr4.sin_family = AF_INET;
28336c52a52SJonathan T. Looney saddr4.sin_port = htons(0);
28436c52a52SJonathan T. Looney saddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
28536c52a52SJonathan T. Looney
28636c52a52SJonathan T. Looney connect_loop(PF_INET, (const struct sockaddr *)&saddr4);
28736c52a52SJonathan T. Looney }
28836c52a52SJonathan T. Looney
ATF_TC_CLEANUP(basic_ipv4,tc)28936c52a52SJonathan T. Looney ATF_TC_CLEANUP(basic_ipv4, tc)
29036c52a52SJonathan T. Looney {
29136c52a52SJonathan T. Looney
29236c52a52SJonathan T. Looney restore_random_ports();
29336c52a52SJonathan T. Looney }
29436c52a52SJonathan T. Looney
29536c52a52SJonathan T. Looney ATF_TC_WITH_CLEANUP(basic_ipv6);
ATF_TC_HEAD(basic_ipv6,tc)29636c52a52SJonathan T. Looney ATF_TC_HEAD(basic_ipv6, tc)
29736c52a52SJonathan T. Looney {
29836c52a52SJonathan T. Looney
29936c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "require.user", "root");
30036c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");
30136c52a52SJonathan T. Looney atf_tc_set_md_var(tc, "descr",
30236c52a52SJonathan T. Looney "Check automatic local port assignment during TCP connect calls");
30336c52a52SJonathan T. Looney }
30436c52a52SJonathan T. Looney
ATF_TC_BODY(basic_ipv6,tc)30536c52a52SJonathan T. Looney ATF_TC_BODY(basic_ipv6, tc)
30636c52a52SJonathan T. Looney {
30736c52a52SJonathan T. Looney struct sockaddr_in6 saddr6;
30836c52a52SJonathan T. Looney
30936c52a52SJonathan T. Looney memset(&saddr6, 0, sizeof(saddr6));
31036c52a52SJonathan T. Looney saddr6.sin6_len = sizeof(saddr6);
31136c52a52SJonathan T. Looney saddr6.sin6_family = AF_INET6;
31236c52a52SJonathan T. Looney saddr6.sin6_port = htons(0);
31336c52a52SJonathan T. Looney saddr6.sin6_addr = in6addr_loopback;
31436c52a52SJonathan T. Looney
31536c52a52SJonathan T. Looney connect_loop(PF_INET6, (const struct sockaddr *)&saddr6);
31636c52a52SJonathan T. Looney }
31736c52a52SJonathan T. Looney
ATF_TC_CLEANUP(basic_ipv6,tc)31836c52a52SJonathan T. Looney ATF_TC_CLEANUP(basic_ipv6, tc)
31936c52a52SJonathan T. Looney {
32036c52a52SJonathan T. Looney
32136c52a52SJonathan T. Looney restore_random_ports();
32236c52a52SJonathan T. Looney }
32336c52a52SJonathan T. Looney
ATF_TP_ADD_TCS(tp)32436c52a52SJonathan T. Looney ATF_TP_ADD_TCS(tp)
32536c52a52SJonathan T. Looney {
32636c52a52SJonathan T. Looney ATF_TP_ADD_TC(tp, basic_ipv4);
32736c52a52SJonathan T. Looney ATF_TP_ADD_TC(tp, basic_ipv6);
32836c52a52SJonathan T. Looney
32936c52a52SJonathan T. Looney return (atf_no_error());
33036c52a52SJonathan T. Looney }
33136c52a52SJonathan T. Looney
332