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