Path: blob/main/tests/sys/netinet/tcp_connect_port_test.c
39536 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2020 Netflix, Inc.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions are7* met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in12* the documentation and/or other materials provided with the13* distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/socket.h>30#include <sys/stat.h>31#include <sys/sysctl.h>3233#include <netinet/in.h>3435#include <err.h>36#include <errno.h>37#include <fcntl.h>38#include <stdio.h>39#include <stdlib.h>40#include <unistd.h>4142#include <atf-c.h>4344#define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.randomized"4546/*47* Check if port allocation is randomized. If so, update it. Save the old48* value of the sysctl so it can be updated later.49*/50static void51disable_random_ports(void)52{53int error, fd, random_new, random_save;54size_t sysctlsz;5556/*57* Pre-emptively unlink our restoration file, so we will do no58* restoration on error.59*/60unlink(SYSCTLBAKFILE);6162/*63* Disable the net.inet.ip.portrange.randomized sysctl. Save the64* old value so we can restore it, if necessary.65*/66random_new = 0;67sysctlsz = sizeof(random_save);68error = sysctlbyname("net.inet.ip.portrange.randomized", &random_save,69&sysctlsz, &random_new, sizeof(random_new));70if (error) {71warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "72"failed");73atf_tc_skip("Unable to set sysctl");74}75if (sysctlsz != sizeof(random_save)) {76fprintf(stderr, "Error: unexpected sysctl value size "77"(expected %zu, actual %zu)\n", sizeof(random_save),78sysctlsz);79goto restore_sysctl;80}8182/* Open the backup file, write the contents, and close it. */83fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL,84S_IRUSR|S_IWUSR);85if (fd < 0) {86warn("error opening sysctl backup file");87goto restore_sysctl;88}89error = write(fd, &random_save, sizeof(random_save));90if (error < 0) {91warn("error writing saved value to sysctl backup file");92goto cleanup_and_restore;93}94if (error != (int)sizeof(random_save)) {95fprintf(stderr,96"Error writing saved value to sysctl backup file: "97"(expected %zu, actual %d)\n", sizeof(random_save), error);98goto cleanup_and_restore;99}100error = close(fd);101if (error) {102warn("error closing sysctl backup file");103cleanup_and_restore:104(void)close(fd);105(void)unlink(SYSCTLBAKFILE);106restore_sysctl:107(void)sysctlbyname("net.inet.ip.portrange.randomized", NULL,108NULL, &random_save, sysctlsz);109atf_tc_skip("Error setting sysctl");110}111}112113/*114* Restore the sysctl value from the backup file and delete the backup file.115*/116static void117restore_random_ports(void)118{119int error, fd, random_save;120121/* Open the backup file, read the contents, close it, and delete it. */122fd = open(SYSCTLBAKFILE, O_RDONLY);123if (fd < 0) {124warn("error opening sysctl backup file");125return;126}127error = read(fd, &random_save, sizeof(random_save));128if (error < 0) {129warn("error reading saved value from sysctl backup file");130return;131}132if (error != (int)sizeof(random_save)) {133fprintf(stderr,134"Error reading saved value from sysctl backup file: "135"(expected %zu, actual %d)\n", sizeof(random_save), error);136return;137}138error = close(fd);139if (error)140warn("error closing sysctl backup file");141error = unlink(SYSCTLBAKFILE);142if (error)143warn("error removing sysctl backup file");144145/* Restore the saved sysctl value. */146error = sysctlbyname("net.inet.ip.portrange.randomized", NULL, NULL,147&random_save, sizeof(random_save));148if (error)149warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") "150"failed while restoring value");151}152153/*154* Given a domain and sockaddr, open a listening socket with automatic port155* selection. Then, try to connect 64K times. Ensure the connected socket never156* uses an overlapping port.157*/158static void159connect_loop(int domain, const struct sockaddr *addr)160{161union {162struct sockaddr saddr;163struct sockaddr_in saddr4;164struct sockaddr_in6 saddr6;165} su_clnt, su_srvr;166socklen_t salen;167int asock, csock, error, i, lsock;168const struct linger lopt = { 1, 0 };169170/*171* Disable the net.inet.ip.portrange.randomized sysctl. Assuming an172* otherwise idle system, this makes the kernel try all possible173* ports sequentially and makes it more likely it will try the174* port on which we have a listening socket.175*/176disable_random_ports();177178/* Setup the listen socket. */179lsock = socket(domain, SOCK_STREAM, 0);180ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s",181strerror(errno));182error = bind(lsock, addr, addr->sa_len);183ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));184error = listen(lsock, 1);185ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));186187/*188* Get the address of the listen socket, which will be the destination189* address for our connection attempts.190*/191salen = sizeof(su_srvr);192error = getsockname(lsock, &su_srvr.saddr, &salen);193ATF_REQUIRE_MSG(error == 0,194"getsockname() for listen socket failed: %s",195strerror(errno));196ATF_REQUIRE_MSG(salen == (domain == PF_INET ?197sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),198"unexpected sockaddr size");199ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == (domain == PF_INET ?200sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),201"unexpected sa_len size");202203/* Open 64K connections in a loop. */204for (i = 0; i < 65536; i++) {205csock = socket(domain, SOCK_STREAM, 0);206ATF_REQUIRE_MSG(csock >= 0,207"socket() for client socket %d failed: %s",208i, strerror(errno));209210error = connect(csock, &su_srvr.saddr, su_srvr.saddr.sa_len);211ATF_REQUIRE_MSG(error == 0,212"connect() for client socket %d failed: %s",213i, strerror(errno));214215error = setsockopt(csock, SOL_SOCKET, SO_LINGER, &lopt,216sizeof(lopt));217ATF_REQUIRE_MSG(error == 0,218"Setting linger for client socket %d failed: %s",219i, strerror(errno));220221/* Ascertain the client socket address. */222salen = sizeof(su_clnt);223error = getsockname(csock, &su_clnt.saddr, &salen);224ATF_REQUIRE_MSG(error == 0,225"getsockname() for client socket %d failed: %s",226i, strerror(errno));227ATF_REQUIRE_MSG(salen == (domain == PF_INET ?228sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)),229"unexpected sockaddr size for client socket %d", i);230231/* Ensure the ports do not match. */232switch (domain) {233case PF_INET:234ATF_REQUIRE_MSG(su_clnt.saddr4.sin_port !=235su_srvr.saddr4.sin_port,236"client socket %d using the same port as server",237i);238break;239case PF_INET6:240ATF_REQUIRE_MSG(su_clnt.saddr6.sin6_port !=241su_srvr.saddr6.sin6_port,242"client socket %d using the same port as server",243i);244break;245}246247/* Accept the socket and close both ends. */248asock = accept(lsock, NULL, NULL);249ATF_REQUIRE_MSG(asock >= 0,250"accept() failed for client socket %d: %s",251i, strerror(errno));252253error = close(asock);254ATF_REQUIRE_MSG(error == 0,255"close() failed for accepted socket %d: %s",256i, strerror(errno));257258error = close(csock);259ATF_REQUIRE_MSG(error == 0,260"close() failed for client socket %d: %s",261i, strerror(errno));262}263}264265ATF_TC_WITH_CLEANUP(basic_ipv4);266ATF_TC_HEAD(basic_ipv4, tc)267{268269atf_tc_set_md_var(tc, "require.user", "root");270atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");271atf_tc_set_md_var(tc, "descr",272"Check automatic local port assignment during TCP connect calls");273}274275ATF_TC_BODY(basic_ipv4, tc)276{277struct sockaddr_in saddr4;278279memset(&saddr4, 0, sizeof(saddr4));280saddr4.sin_len = sizeof(saddr4);281saddr4.sin_family = AF_INET;282saddr4.sin_port = htons(0);283saddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);284285connect_loop(PF_INET, (const struct sockaddr *)&saddr4);286}287288ATF_TC_CLEANUP(basic_ipv4, tc)289{290291restore_random_ports();292}293294ATF_TC_WITH_CLEANUP(basic_ipv6);295ATF_TC_HEAD(basic_ipv6, tc)296{297298atf_tc_set_md_var(tc, "require.user", "root");299atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects");300atf_tc_set_md_var(tc, "descr",301"Check automatic local port assignment during TCP connect calls");302}303304ATF_TC_BODY(basic_ipv6, tc)305{306struct sockaddr_in6 saddr6;307308memset(&saddr6, 0, sizeof(saddr6));309saddr6.sin6_len = sizeof(saddr6);310saddr6.sin6_family = AF_INET6;311saddr6.sin6_port = htons(0);312saddr6.sin6_addr = in6addr_loopback;313314connect_loop(PF_INET6, (const struct sockaddr *)&saddr6);315}316317ATF_TC_CLEANUP(basic_ipv6, tc)318{319320restore_random_ports();321}322323ATF_TP_ADD_TCS(tp)324{325ATF_TP_ADD_TC(tp, basic_ipv4);326ATF_TP_ADD_TC(tp, basic_ipv6);327328return (atf_no_error());329}330331332333