11b2c9809SRobert Watson /*- 21b2c9809SRobert Watson * Copyright (c) 2006 Robert N. M. Watson 3*e8c54602SRobert Watson * Copyright (c) 2011 Juniper Networks, Inc. 41b2c9809SRobert Watson * All rights reserved. 51b2c9809SRobert Watson * 6*e8c54602SRobert Watson * Portions of this software were developed by Robert N. M. Watson under 7*e8c54602SRobert Watson * contract to Juniper Networks, Inc. 8*e8c54602SRobert Watson * 91b2c9809SRobert Watson * Redistribution and use in source and binary forms, with or without 101b2c9809SRobert Watson * modification, are permitted provided that the following conditions 111b2c9809SRobert Watson * are met: 121b2c9809SRobert Watson * 1. Redistributions of source code must retain the above copyright 131b2c9809SRobert Watson * notice, this list of conditions and the following disclaimer. 141b2c9809SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright 151b2c9809SRobert Watson * notice, this list of conditions and the following disclaimer in the 161b2c9809SRobert Watson * documentation and/or other materials provided with the distribution. 171b2c9809SRobert Watson * 181b2c9809SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 191b2c9809SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201b2c9809SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211b2c9809SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 221b2c9809SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231b2c9809SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241b2c9809SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251b2c9809SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261b2c9809SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271b2c9809SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281b2c9809SRobert Watson * SUCH DAMAGE. 291b2c9809SRobert Watson * 301b2c9809SRobert Watson * $FreeBSD$ 311b2c9809SRobert Watson */ 321b2c9809SRobert Watson 331b2c9809SRobert Watson /* 341b2c9809SRobert Watson * TCP regression test for the tcpdrop sysctl; build a loopback TCP 351b2c9809SRobert Watson * connection, drop it, and make sure both endpoints return that the 361b2c9809SRobert Watson * connection has been reset. 371b2c9809SRobert Watson */ 381b2c9809SRobert Watson 391b2c9809SRobert Watson #include <sys/types.h> 401b2c9809SRobert Watson #include <sys/socket.h> 411b2c9809SRobert Watson #include <sys/sysctl.h> 421b2c9809SRobert Watson 431b2c9809SRobert Watson #include <netinet/in.h> 441b2c9809SRobert Watson 451b2c9809SRobert Watson #include <err.h> 461b2c9809SRobert Watson #include <errno.h> 471b2c9809SRobert Watson #include <signal.h> 481b2c9809SRobert Watson #include <stdio.h> 491b2c9809SRobert Watson #include <stdlib.h> 501b2c9809SRobert Watson #include <string.h> 511b2c9809SRobert Watson #include <unistd.h> 521b2c9809SRobert Watson 531b2c9809SRobert Watson static int 541b2c9809SRobert Watson tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote) 551b2c9809SRobert Watson { 561b2c9809SRobert Watson struct sockaddr_storage addrs[2]; 571b2c9809SRobert Watson 581b2c9809SRobert Watson /* 591b2c9809SRobert Watson * Sysctl accepts an array of two sockaddr's, the first being the 601b2c9809SRobert Watson * 'foreign' sockaddr, the second being the 'local' sockaddr. 611b2c9809SRobert Watson */ 621b2c9809SRobert Watson 631b2c9809SRobert Watson bcopy(sin_remote, &addrs[0], sizeof(*sin_remote)); 641b2c9809SRobert Watson bcopy(sin_local, &addrs[1], sizeof(*sin_local)); 651b2c9809SRobert Watson 661b2c9809SRobert Watson return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs, 671b2c9809SRobert Watson sizeof(addrs))); 681b2c9809SRobert Watson } 691b2c9809SRobert Watson 701b2c9809SRobert Watson static void 71*e8c54602SRobert Watson tcp_server(pid_t partner, int listen_fd) 721b2c9809SRobert Watson { 73*e8c54602SRobert Watson int error, accept_fd; 741b2c9809SRobert Watson ssize_t len; 751b2c9809SRobert Watson char ch; 761b2c9809SRobert Watson 771b2c9809SRobert Watson accept_fd = accept(listen_fd, NULL, NULL); 781b2c9809SRobert Watson if (accept_fd < 0) { 791b2c9809SRobert Watson error = errno; 801b2c9809SRobert Watson (void)kill(partner, SIGTERM); 811b2c9809SRobert Watson errno = error; 821b2c9809SRobert Watson err(-1, "tcp_server: accept"); 831b2c9809SRobert Watson } 841b2c9809SRobert Watson 851b2c9809SRobert Watson /* 861b2c9809SRobert Watson * Send one byte, make sure that worked, wait for the drop, and try 871b2c9809SRobert Watson * sending another. By sending small amounts, we avoid blocking 881b2c9809SRobert Watson * waiting on the remote buffer to be drained. 891b2c9809SRobert Watson */ 901b2c9809SRobert Watson ch = 'A'; 911b2c9809SRobert Watson len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 921b2c9809SRobert Watson if (len < 0) { 931b2c9809SRobert Watson error = errno; 941b2c9809SRobert Watson (void)kill(partner, SIGTERM); 951b2c9809SRobert Watson errno = error; 961b2c9809SRobert Watson err(-1, "tcp_server: send (1)"); 971b2c9809SRobert Watson } 981b2c9809SRobert Watson if (len != sizeof(ch)) { 991b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1001b2c9809SRobert Watson errx(-1, "tcp_server: send (1) len"); 1011b2c9809SRobert Watson } 1021b2c9809SRobert Watson 1031b2c9809SRobert Watson sleep (10); 1041b2c9809SRobert Watson 1051b2c9809SRobert Watson ch = 'A'; 1061b2c9809SRobert Watson len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 1071b2c9809SRobert Watson if (len >= 0) { 1081b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1091b2c9809SRobert Watson errx(-1, "tcp_server: send (2): success"); 1101b2c9809SRobert Watson } else if (errno != EPIPE) { 1111b2c9809SRobert Watson error = errno; 1121b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1131b2c9809SRobert Watson errno = error; 1141b2c9809SRobert Watson err(-1, "tcp_server: send (2)"); 1151b2c9809SRobert Watson } 1161b2c9809SRobert Watson 1171b2c9809SRobert Watson close(accept_fd); 1181b2c9809SRobert Watson close(listen_fd); 1191b2c9809SRobert Watson } 1201b2c9809SRobert Watson 1211b2c9809SRobert Watson static void 122*e8c54602SRobert Watson tcp_client(pid_t partner, u_short port) 1231b2c9809SRobert Watson { 1241b2c9809SRobert Watson struct sockaddr_in sin, sin_local; 1251b2c9809SRobert Watson int error, sock; 1261b2c9809SRobert Watson socklen_t slen; 1271b2c9809SRobert Watson ssize_t len; 1281b2c9809SRobert Watson char ch; 1291b2c9809SRobert Watson 1301b2c9809SRobert Watson sleep(1); 1311b2c9809SRobert Watson 1321b2c9809SRobert Watson sock = socket(PF_INET, SOCK_STREAM, 0); 1331b2c9809SRobert Watson if (sock < 0) { 1341b2c9809SRobert Watson error = errno; 1351b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1361b2c9809SRobert Watson errno = error; 1371b2c9809SRobert Watson err(-1, "socket"); 1381b2c9809SRobert Watson } 1391b2c9809SRobert Watson 1401b2c9809SRobert Watson bzero(&sin, sizeof(sin)); 1411b2c9809SRobert Watson sin.sin_family = AF_INET; 1421b2c9809SRobert Watson sin.sin_len = sizeof(sin); 1431b2c9809SRobert Watson sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); 144*e8c54602SRobert Watson sin.sin_port = port; 1451b2c9809SRobert Watson 1461b2c9809SRobert Watson if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 1471b2c9809SRobert Watson error = errno; 1481b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1491b2c9809SRobert Watson errno = error; 1501b2c9809SRobert Watson err(-1, "connect"); 1511b2c9809SRobert Watson } 1521b2c9809SRobert Watson 1531b2c9809SRobert Watson slen = sizeof(sin_local); 1541b2c9809SRobert Watson if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) { 1551b2c9809SRobert Watson error = errno; 1561b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1571b2c9809SRobert Watson errno = error; 1581b2c9809SRobert Watson err(-1, "getsockname"); 1591b2c9809SRobert Watson } 1601b2c9809SRobert Watson 1611b2c9809SRobert Watson /* 1621b2c9809SRobert Watson * Send one byte, make sure that worked, wait for the drop, and try 1631b2c9809SRobert Watson * sending another. By sending small amounts, we avoid blocking 1641b2c9809SRobert Watson * waiting on the remote buffer to be drained. 1651b2c9809SRobert Watson */ 1661b2c9809SRobert Watson ch = 'A'; 1671b2c9809SRobert Watson len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 1681b2c9809SRobert Watson if (len < 0) { 1691b2c9809SRobert Watson error = errno; 1701b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1711b2c9809SRobert Watson errno = error; 1721b2c9809SRobert Watson err(-1, "tcp_client: send (1)"); 1731b2c9809SRobert Watson } 1741b2c9809SRobert Watson if (len != sizeof(ch)) { 1751b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1761b2c9809SRobert Watson errx(-1, "tcp_client: send (1) len"); 1771b2c9809SRobert Watson } 1781b2c9809SRobert Watson 1791b2c9809SRobert Watson sleep(5); 1801b2c9809SRobert Watson if (tcp_drop(&sin_local, &sin) < 0) { 1811b2c9809SRobert Watson error = errno; 1821b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1831b2c9809SRobert Watson errno = error; 1841b2c9809SRobert Watson err(-1, "tcp_client: tcp_drop"); 1851b2c9809SRobert Watson } 1861b2c9809SRobert Watson sleep(5); 1871b2c9809SRobert Watson 1881b2c9809SRobert Watson ch = 'A'; 1891b2c9809SRobert Watson len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 1901b2c9809SRobert Watson if (len >= 0) { 1911b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1921b2c9809SRobert Watson errx(-1, "tcp_client: send (2): success"); 1931b2c9809SRobert Watson } else if (errno != EPIPE) { 1941b2c9809SRobert Watson error = errno; 1951b2c9809SRobert Watson (void)kill(partner, SIGTERM); 1961b2c9809SRobert Watson errno = error; 1971b2c9809SRobert Watson err(-1, "tcp_client: send (2)"); 1981b2c9809SRobert Watson } 1991b2c9809SRobert Watson close(sock); 2001b2c9809SRobert Watson } 2011b2c9809SRobert Watson 2021b2c9809SRobert Watson int 2031b2c9809SRobert Watson main(int argc, char *argv[]) 2041b2c9809SRobert Watson { 2051b2c9809SRobert Watson pid_t child_pid, parent_pid; 206*e8c54602SRobert Watson struct sockaddr_in sin; 207*e8c54602SRobert Watson int listen_fd; 208*e8c54602SRobert Watson u_short port; 209*e8c54602SRobert Watson socklen_t len; 210*e8c54602SRobert Watson 211*e8c54602SRobert Watson listen_fd = socket(PF_INET, SOCK_STREAM, 0); 212*e8c54602SRobert Watson if (listen_fd < 0) 213*e8c54602SRobert Watson err(-1, "socket"); 214*e8c54602SRobert Watson 215*e8c54602SRobert Watson /* 216*e8c54602SRobert Watson * We use the loopback, but let the kernel select a port for the 217*e8c54602SRobert Watson * server socket. 218*e8c54602SRobert Watson */ 219*e8c54602SRobert Watson bzero(&sin, sizeof(sin)); 220*e8c54602SRobert Watson sin.sin_family = AF_INET; 221*e8c54602SRobert Watson sin.sin_len = sizeof(sin); 222*e8c54602SRobert Watson sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 223*e8c54602SRobert Watson 224*e8c54602SRobert Watson if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 225*e8c54602SRobert Watson err(-1, "bind"); 226*e8c54602SRobert Watson 227*e8c54602SRobert Watson if (listen(listen_fd, -1) < 0) 228*e8c54602SRobert Watson err(-1, "listen"); 229*e8c54602SRobert Watson 230*e8c54602SRobert Watson /* 231*e8c54602SRobert Watson * Query the port so that the client can use it. 232*e8c54602SRobert Watson */ 233*e8c54602SRobert Watson bzero(&sin, sizeof(sin)); 234*e8c54602SRobert Watson sin.sin_family = AF_INET; 235*e8c54602SRobert Watson sin.sin_len = sizeof(sin); 236*e8c54602SRobert Watson if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) 237*e8c54602SRobert Watson err(-1, "getsockname"); 238*e8c54602SRobert Watson port = sin.sin_port; 239*e8c54602SRobert Watson printf("Using port %d\n", ntohs(port)); 2401b2c9809SRobert Watson 2411b2c9809SRobert Watson if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) 2421b2c9809SRobert Watson err(-1, "signal"); 2431b2c9809SRobert Watson 2441b2c9809SRobert Watson parent_pid = getpid(); 2451b2c9809SRobert Watson child_pid = fork(); 2461b2c9809SRobert Watson if (child_pid < 0) 2471b2c9809SRobert Watson err(-1, "fork"); 2481b2c9809SRobert Watson if (child_pid == 0) { 2491b2c9809SRobert Watson child_pid = getpid(); 250*e8c54602SRobert Watson tcp_server(parent_pid, listen_fd); 2511b2c9809SRobert Watson } else 252*e8c54602SRobert Watson tcp_client(child_pid, port); 2531b2c9809SRobert Watson 2541b2c9809SRobert Watson return (0); 2551b2c9809SRobert Watson } 256