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 #include <sys/param.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/sysctl.h> 34 35 #include <netinet/in.h> 36 37 #include <err.h> 38 #include <errno.h> 39 #include <fcntl.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <unistd.h> 43 44 #include <atf-c.h> 45 46 #define SYSCTLBAKFILE "tmp.net.inet.ip.portrange.randomized" 47 48 /* 49 * Check if port allocation is randomized. If so, update it. Save the old 50 * value of the sysctl so it can be updated later. 51 */ 52 static void 53 disable_random_ports(void) 54 { 55 int error, fd, random_new, random_save; 56 size_t sysctlsz; 57 58 /* 59 * Pre-emptively unlink our restoration file, so we will do no 60 * restoration on error. 61 */ 62 unlink(SYSCTLBAKFILE); 63 64 /* 65 * Disable the net.inet.ip.portrange.randomized sysctl. Save the 66 * old value so we can restore it, if necessary. 67 */ 68 random_new = 0; 69 sysctlsz = sizeof(random_save); 70 error = sysctlbyname("net.inet.ip.portrange.randomized", &random_save, 71 &sysctlsz, &random_new, sizeof(random_new)); 72 if (error) { 73 warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") " 74 "failed"); 75 atf_tc_skip("Unable to set sysctl"); 76 } 77 if (sysctlsz != sizeof(random_save)) { 78 fprintf(stderr, "Error: unexpected sysctl value size " 79 "(expected %zu, actual %zu)\n", sizeof(random_save), 80 sysctlsz); 81 goto restore_sysctl; 82 } 83 84 /* Open the backup file, write the contents, and close it. */ 85 fd = open(SYSCTLBAKFILE, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 86 S_IRUSR|S_IWUSR); 87 if (fd < 0) { 88 warn("error opening sysctl backup file"); 89 goto restore_sysctl; 90 } 91 error = write(fd, &random_save, sizeof(random_save)); 92 if (error < 0) { 93 warn("error writing saved value to sysctl backup file"); 94 goto cleanup_and_restore; 95 } 96 if (error != (int)sizeof(random_save)) { 97 fprintf(stderr, 98 "Error writing saved value to sysctl backup file: " 99 "(expected %zu, actual %d)\n", sizeof(random_save), error); 100 goto cleanup_and_restore; 101 } 102 error = close(fd); 103 if (error) { 104 warn("error closing sysctl backup file"); 105 cleanup_and_restore: 106 (void)close(fd); 107 (void)unlink(SYSCTLBAKFILE); 108 restore_sysctl: 109 (void)sysctlbyname("net.inet.ip.portrange.randomized", NULL, 110 NULL, &random_save, sysctlsz); 111 atf_tc_skip("Error setting sysctl"); 112 } 113 } 114 115 /* 116 * Restore the sysctl value from the backup file and delete the backup file. 117 */ 118 static void 119 restore_random_ports(void) 120 { 121 int error, fd, random_save; 122 123 /* Open the backup file, read the contents, close it, and delete it. */ 124 fd = open(SYSCTLBAKFILE, O_RDONLY); 125 if (fd < 0) { 126 warn("error opening sysctl backup file"); 127 return; 128 } 129 error = read(fd, &random_save, sizeof(random_save)); 130 if (error < 0) { 131 warn("error reading saved value from sysctl backup file"); 132 return; 133 } 134 if (error != (int)sizeof(random_save)) { 135 fprintf(stderr, 136 "Error reading saved value from sysctl backup file: " 137 "(expected %zu, actual %d)\n", sizeof(random_save), error); 138 return; 139 } 140 error = close(fd); 141 if (error) 142 warn("error closing sysctl backup file"); 143 error = unlink(SYSCTLBAKFILE); 144 if (error) 145 warn("error removing sysctl backup file"); 146 147 /* Restore the saved sysctl value. */ 148 error = sysctlbyname("net.inet.ip.portrange.randomized", NULL, NULL, 149 &random_save, sizeof(random_save)); 150 if (error) 151 warn("sysctlbyname(\"net.inet.ip.portrange.randomized\") " 152 "failed while restoring value"); 153 } 154 155 /* 156 * Given a domain and sockaddr, open a listening socket with automatic port 157 * selection. Then, try to connect 64K times. Ensure the connected socket never 158 * uses an overlapping port. 159 */ 160 static void 161 connect_loop(int domain, const struct sockaddr *addr) 162 { 163 union { 164 struct sockaddr saddr; 165 struct sockaddr_in saddr4; 166 struct sockaddr_in6 saddr6; 167 } su_clnt, su_srvr; 168 socklen_t salen; 169 int asock, csock, error, i, lsock; 170 const struct linger lopt = { 1, 0 }; 171 172 /* 173 * Disable the net.inet.ip.portrange.randomized sysctl. Assuming an 174 * otherwise idle system, this makes the kernel try all possible 175 * ports sequentially and makes it more likely it will try the 176 * port on which we have a listening socket. 177 */ 178 disable_random_ports(); 179 180 /* Setup the listen socket. */ 181 lsock = socket(domain, SOCK_STREAM, 0); 182 ATF_REQUIRE_MSG(lsock >= 0, "socket() for listen socket failed: %s", 183 strerror(errno)); 184 error = bind(lsock, addr, addr->sa_len); 185 ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno)); 186 error = listen(lsock, 1); 187 ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno)); 188 189 /* 190 * Get the address of the listen socket, which will be the destination 191 * address for our connection attempts. 192 */ 193 salen = sizeof(su_srvr); 194 error = getsockname(lsock, &su_srvr.saddr, &salen); 195 ATF_REQUIRE_MSG(error == 0, 196 "getsockname() for listen socket failed: %s", 197 strerror(errno)); 198 ATF_REQUIRE_MSG(salen == (domain == PF_INET ? 199 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 200 "unexpected sockaddr size"); 201 ATF_REQUIRE_MSG(su_srvr.saddr.sa_len == (domain == PF_INET ? 202 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 203 "unexpected sa_len size"); 204 205 /* Open 64K connections in a loop. */ 206 for (i = 0; i < 65536; i++) { 207 csock = socket(domain, SOCK_STREAM, 0); 208 ATF_REQUIRE_MSG(csock >= 0, 209 "socket() for client socket %d failed: %s", 210 i, strerror(errno)); 211 212 error = connect(csock, &su_srvr.saddr, su_srvr.saddr.sa_len); 213 ATF_REQUIRE_MSG(error == 0, 214 "connect() for client socket %d failed: %s", 215 i, strerror(errno)); 216 217 error = setsockopt(csock, SOL_SOCKET, SO_LINGER, &lopt, 218 sizeof(lopt)); 219 ATF_REQUIRE_MSG(error == 0, 220 "Setting linger for client socket %d failed: %s", 221 i, strerror(errno)); 222 223 /* Ascertain the client socket address. */ 224 salen = sizeof(su_clnt); 225 error = getsockname(csock, &su_clnt.saddr, &salen); 226 ATF_REQUIRE_MSG(error == 0, 227 "getsockname() for client socket %d failed: %s", 228 i, strerror(errno)); 229 ATF_REQUIRE_MSG(salen == (domain == PF_INET ? 230 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), 231 "unexpected sockaddr size for client socket %d", i); 232 233 /* Ensure the ports do not match. */ 234 switch (domain) { 235 case PF_INET: 236 ATF_REQUIRE_MSG(su_clnt.saddr4.sin_port != 237 su_srvr.saddr4.sin_port, 238 "client socket %d using the same port as server", 239 i); 240 break; 241 case PF_INET6: 242 ATF_REQUIRE_MSG(su_clnt.saddr6.sin6_port != 243 su_srvr.saddr6.sin6_port, 244 "client socket %d using the same port as server", 245 i); 246 break; 247 } 248 249 /* Accept the socket and close both ends. */ 250 asock = accept(lsock, NULL, NULL); 251 ATF_REQUIRE_MSG(asock >= 0, 252 "accept() failed for client socket %d: %s", 253 i, strerror(errno)); 254 255 error = close(asock); 256 ATF_REQUIRE_MSG(error == 0, 257 "close() failed for accepted socket %d: %s", 258 i, strerror(errno)); 259 260 error = close(csock); 261 ATF_REQUIRE_MSG(error == 0, 262 "close() failed for client socket %d: %s", 263 i, strerror(errno)); 264 } 265 } 266 267 ATF_TC_WITH_CLEANUP(basic_ipv4); 268 ATF_TC_HEAD(basic_ipv4, tc) 269 { 270 271 atf_tc_set_md_var(tc, "require.user", "root"); 272 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects"); 273 atf_tc_set_md_var(tc, "descr", 274 "Check automatic local port assignment during TCP connect calls"); 275 } 276 277 ATF_TC_BODY(basic_ipv4, tc) 278 { 279 struct sockaddr_in saddr4; 280 281 memset(&saddr4, 0, sizeof(saddr4)); 282 saddr4.sin_len = sizeof(saddr4); 283 saddr4.sin_family = AF_INET; 284 saddr4.sin_port = htons(0); 285 saddr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 286 287 connect_loop(PF_INET, (const struct sockaddr *)&saddr4); 288 } 289 290 ATF_TC_CLEANUP(basic_ipv4, tc) 291 { 292 293 restore_random_ports(); 294 } 295 296 ATF_TC_WITH_CLEANUP(basic_ipv6); 297 ATF_TC_HEAD(basic_ipv6, tc) 298 { 299 300 atf_tc_set_md_var(tc, "require.user", "root"); 301 atf_tc_set_md_var(tc, "require.config", "allow_sysctl_side_effects"); 302 atf_tc_set_md_var(tc, "descr", 303 "Check automatic local port assignment during TCP connect calls"); 304 } 305 306 ATF_TC_BODY(basic_ipv6, tc) 307 { 308 struct sockaddr_in6 saddr6; 309 310 memset(&saddr6, 0, sizeof(saddr6)); 311 saddr6.sin6_len = sizeof(saddr6); 312 saddr6.sin6_family = AF_INET6; 313 saddr6.sin6_port = htons(0); 314 saddr6.sin6_addr = in6addr_loopback; 315 316 connect_loop(PF_INET6, (const struct sockaddr *)&saddr6); 317 } 318 319 ATF_TC_CLEANUP(basic_ipv6, tc) 320 { 321 322 restore_random_ports(); 323 } 324 325 ATF_TP_ADD_TCS(tp) 326 { 327 ATF_TP_ADD_TC(tp, basic_ipv4); 328 ATF_TP_ADD_TC(tp, basic_ipv6); 329 330 return (atf_no_error()); 331 } 332 333