1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Netflix, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/socket.h> 34 #include <sys/stat.h> 35 #include <sys/sysctl.h> 36 37 #include <netinet/in.h> 38 39 #include <err.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 46 #include <atf-c.h> 47 48 #define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.randomized" 49 50 /* 51 * Check if port allocation is randomized. If so, update it. Save the old 52 * value of the sysctl so it can be updated later. 53 */ 54 static void 55 disable_random_ports(void) 56 { 57 int error, fd, random_new, random_save; 58 size_t sysctlsz; 59 60 /* 61 * Pre-emptively unlink our restoration file, so we will do no 62 * restoration on error. 63 */ 64 unlink(SYSCTLBAKFILE); 65 66 /* 67 * Disable the net.inet.ip.portrange.randomized sysctl. Save the 68 * old value so we can restore it, if necessary. 69 */ 70 random_new = 0; 71 sysctlsz = sizeof(random_save); 72 error = sysctlbyname("net.inet.ip.portrange.randomized", &random_save, 73 &sysctlsz, &random_new, sizeof(random_new)); 74 if (error) { 75 warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") " 76 "failed"); 77 atf_tc_skip("Unable to set sysctl"); 78 } 79 if (sysctlsz != sizeof(random_save)) { 80 fprintf(stderr, "Error: unexpected sysctl value size " 81 "(expected %zu, actual %zu)\n", sizeof(random_save), 82 sysctlsz); 83 goto restore_sysctl; 84 } 85 86 /* Open the backup file, write the contents, and close it. */ 87 fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 88 S_IRUSR|S_IWUSR); 89 if (fd < 0) { 90 warn("error opening sysctl backup file"); 91 goto restore_sysctl; 92 } 93 error = write(fd, &random_save, sizeof(random_save)); 94 if (error < 0) { 95 warn("error writing saved value to sysctl backup file"); 96 goto cleanup_and_restore; 97 } 98 if (error != (int)sizeof(random_save)) { 99 fprintf(stderr, 100 "Error writing saved value to sysctl backup file: " 101 "(expected %zu, actual %d)\n", sizeof(random_save), error); 102 goto cleanup_and_restore; 103 } 104 error = close(fd); 105 if (error) { 106 warn("error closing sysctl backup file"); 107 cleanup_and_restore: 108 (void)close(fd); 109 (void)unlink(SYSCTLBAKFILE); 110 restore_sysctl: 111 (void)sysctlbyname("net.inet.ip.portrange.randomized", NULL, 112 NULL, &random_save, sysctlsz); 113 atf_tc_skip("Error setting sysctl"); 114 } 115 } 116 117 /* 118 * Restore the sysctl value from the backup file and delete the backup file. 119 */ 120 static void 121 restore_random_ports(void) 122 { 123 int error, fd, random_save; 124 125 /* Open the backup file, read the contents, close it, and delete it. */ 126 fd = open(SYSCTLBAKFILE, O_RDONLY); 127 if (fd < 0) { 128 warn("error opening sysctl backup file"); 129 return; 130 } 131 error = read(fd, &random_save, sizeof(random_save)); 132 if (error < 0) { 133 warn("error reading saved value from sysctl backup file"); 134 return; 135 } 136 if (error != (int)sizeof(random_save)) { 137 fprintf(stderr, 138 "Error reading saved value from sysctl backup file: " 139 "(expected %zu, actual %d)\n", sizeof(random_save), error); 140 return; 141 } 142 error = close(fd); 143 if (error) 144 warn("error closing sysctl backup file"); 145 error = unlink(SYSCTLBAKFILE); 146 if (error) 147 warn("error removing sysctl backup file"); 148 149 /* Restore the saved sysctl value. */ 150 error = sysctlbyname("net.inet.ip.portrange.randomized", NULL, NULL, 151 &random_save, sizeof(random_save)); 152 if (error) 153 warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") " 154 "failed while restoring value"); 155 } 156 157 /* 158 * Given a domain and sockaddr, open a listening socket with automatic port 159 * selection. Then, try to connect 64K times. Ensure the connected socket never 160 * uses an overlapping port. 161 */ 162 static void 163 connect_loop(int domain, const struct sockaddr *addr) 164 { 165 union { 166 struct sockaddr saddr; 167 struct sockaddr_in saddr4; 168 struct sockaddr_in6 saddr6; 169 } su_clnt, su_srvr; 170 socklen_t salen; 171 int asock, csock, error, i, lsock; 172 const struct linger lopt = { 1, 0 }; 173 174 /* 175 * Disable the net.inet.ip.portrange.randomized sysctl. Assuming an 176 * otherwise idle system, this makes the kernel try all possible 177 * ports sequentially and makes it more likely it will try the 178 * port on which we have a listening socket. 179 */ 180 disable_random_ports(); 181 182 /* Setup the listen socket. */ 183 lsock = socket(domain, SOCK_STREAM, 0); 184 ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s", 185 strerror(errno)); 186 error = bind(lsock, addr, addr->sa_len); 187 ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); 188 error = listen(lsock, 1); 189 ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno)); 190 191 /* 192 * Get the address of the listen socket, which will be the destination 193 * address for our connection attempts. 194 */ 195 salen = sizeof(su_srvr); 196 error = getsockname(lsock, &su_srvr.saddr, &salen); 197 ATF_REQUIRE_MSG(error == 0, 198 "getsockname() for listen socket failed: %s", 199 strerror(errno)); 200 ATF_REQUIRE_MSG(salen == (domain == PF_INET ? 201 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 202 "unexpected sockaddr size"); 203 ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == (domain == PF_INET ? 204 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 205 "unexpected sa_len size"); 206 207 /* Open 64K connections in a loop. */ 208 for (i = 0; i < 65536; i++) { 209 csock = socket(domain, SOCK_STREAM, 0); 210 ATF_REQUIRE_MSG(csock >= 0, 211 "socket() for client socket %d failed: %s", 212 i, strerror(errno)); 213 214 error = connect(csock, &su_srvr.saddr, su_srvr.saddr.sa_len); 215 ATF_REQUIRE_MSG(error == 0, 216 "connect() for client socket %d failed: %s", 217 i, strerror(errno)); 218 219 error = setsockopt(csock, SOL_SOCKET, SO_LINGER, &lopt, 220 sizeof(lopt)); 221 ATF_REQUIRE_MSG(error == 0, 222 "Setting linger for client socket %d failed: %s", 223 i, strerror(errno)); 224 225 /* Ascertain the client socket address. */ 226 salen = sizeof(su_clnt); 227 error = getsockname(csock, &su_clnt.saddr, &salen); 228 ATF_REQUIRE_MSG(error == 0, 229 "getsockname() for client socket %d failed: %s", 230 i, strerror(errno)); 231 ATF_REQUIRE_MSG(salen == (domain == PF_INET ? 232 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 233 "unexpected sockaddr size for client socket %d", i); 234 235 /* Ensure the ports do not match. */ 236 switch (domain) { 237 case PF_INET: 238 ATF_REQUIRE_MSG(su_clnt.saddr4.sin_port != 239 su_srvr.saddr4.sin_port, 240 "client socket %d using the same port as server", 241 i); 242 break; 243 case PF_INET6: 244 ATF_REQUIRE_MSG(su_clnt.saddr6.sin6_port != 245 su_srvr.saddr6.sin6_port, 246 "client socket %d using the same port as server", 247 i); 248 break; 249 } 250 251 /* Accept the socket and close both ends. */ 252 asock = accept(lsock, NULL, NULL); 253 ATF_REQUIRE_MSG(asock >= 0, 254 "accept() failed for client socket %d: %s", 255 i, strerror(errno)); 256 257 error = close(asock); 258 ATF_REQUIRE_MSG(error == 0, 259 "close() failed for accepted socket %d: %s", 260 i, strerror(errno)); 261 262 error = close(csock); 263 ATF_REQUIRE_MSG(error == 0, 264 "close() failed for client socket %d: %s", 265 i, strerror(errno)); 266 } 267 } 268 269 ATF_TC_WITH_CLEANUP(basic_ipv4); 270 ATF_TC_HEAD(basic_ipv4, tc) 271 { 272 273 atf_tc_set_md_var(tc, "require.user", "root"); 274 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects"); 275 atf_tc_set_md_var(tc, "descr", 276 "Check automatic local port assignment during TCP connect calls"); 277 } 278 279 ATF_TC_BODY(basic_ipv4, tc) 280 { 281 struct sockaddr_in saddr4; 282 283 memset(&saddr4, 0, sizeof(saddr4)); 284 saddr4.sin_len = sizeof(saddr4); 285 saddr4.sin_family = AF_INET; 286 saddr4.sin_port = htons(0); 287 saddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 288 289 connect_loop(PF_INET, (const struct sockaddr *)&saddr4); 290 } 291 292 ATF_TC_CLEANUP(basic_ipv4, tc) 293 { 294 295 restore_random_ports(); 296 } 297 298 ATF_TC_WITH_CLEANUP(basic_ipv6); 299 ATF_TC_HEAD(basic_ipv6, tc) 300 { 301 302 atf_tc_set_md_var(tc, "require.user", "root"); 303 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects"); 304 atf_tc_set_md_var(tc, "descr", 305 "Check automatic local port assignment during TCP connect calls"); 306 } 307 308 ATF_TC_BODY(basic_ipv6, tc) 309 { 310 struct sockaddr_in6 saddr6; 311 312 memset(&saddr6, 0, sizeof(saddr6)); 313 saddr6.sin6_len = sizeof(saddr6); 314 saddr6.sin6_family = AF_INET6; 315 saddr6.sin6_port = htons(0); 316 saddr6.sin6_addr = in6addr_loopback; 317 318 connect_loop(PF_INET6, (const struct sockaddr *)&saddr6); 319 } 320 321 ATF_TC_CLEANUP(basic_ipv6, tc) 322 { 323 324 restore_random_ports(); 325 } 326 327 ATF_TP_ADD_TCS(tp) 328 { 329 ATF_TP_ADD_TC(tp, basic_ipv4); 330 ATF_TP_ADD_TC(tp, basic_ipv6); 331 332 return (atf_no_error()); 333 } 334 335