1 /*- 2 * Copyright (c) 2006 Robert N. M. Watson 3 * Copyright (c) 2011 Juniper Networks, Inc. 4 * All rights reserved. 5 * 6 * Portions of this software were developed by Robert N. M. Watson under 7 * contract to Juniper Networks, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * TCP regression test for the tcpdrop sysctl; build a loopback TCP 33 * connection, drop it, and make sure both endpoints return that the 34 * connection has been reset. 35 */ 36 37 #include <sys/types.h> 38 #include <sys/socket.h> 39 #include <sys/sysctl.h> 40 41 #include <netinet/in.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 static int 52 tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote) 53 { 54 struct sockaddr_storage addrs[2]; 55 56 /* 57 * Sysctl accepts an array of two sockaddr's, the first being the 58 * 'foreign' sockaddr, the second being the 'local' sockaddr. 59 */ 60 61 bcopy(sin_remote, &addrs[0], sizeof(*sin_remote)); 62 bcopy(sin_local, &addrs[1], sizeof(*sin_local)); 63 64 return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs, 65 sizeof(addrs))); 66 } 67 68 static void 69 tcp_server(pid_t partner, int listen_fd) 70 { 71 int error, accept_fd; 72 ssize_t len; 73 char ch; 74 75 accept_fd = accept(listen_fd, NULL, NULL); 76 if (accept_fd < 0) { 77 error = errno; 78 (void)kill(partner, SIGTERM); 79 errno = error; 80 err(-1, "tcp_server: accept"); 81 } 82 83 /* 84 * Send one byte, make sure that worked, wait for the drop, and try 85 * sending another. By sending small amounts, we avoid blocking 86 * waiting on the remote buffer to be drained. 87 */ 88 ch = 'A'; 89 len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 90 if (len < 0) { 91 error = errno; 92 (void)kill(partner, SIGTERM); 93 errno = error; 94 err(-1, "tcp_server: send (1)"); 95 } 96 if (len != sizeof(ch)) { 97 (void)kill(partner, SIGTERM); 98 errx(-1, "tcp_server: send (1) len"); 99 } 100 101 sleep (10); 102 103 ch = 'A'; 104 len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 105 if (len >= 0) { 106 (void)kill(partner, SIGTERM); 107 errx(-1, "tcp_server: send (2): success"); 108 } else if (errno != EPIPE) { 109 error = errno; 110 (void)kill(partner, SIGTERM); 111 errno = error; 112 err(-1, "tcp_server: send (2)"); 113 } 114 115 close(accept_fd); 116 close(listen_fd); 117 } 118 119 static void 120 tcp_client(pid_t partner, u_short port) 121 { 122 struct sockaddr_in sin, sin_local; 123 int error, sock; 124 socklen_t slen; 125 ssize_t len; 126 char ch; 127 128 sleep(1); 129 130 sock = socket(PF_INET, SOCK_STREAM, 0); 131 if (sock < 0) { 132 error = errno; 133 (void)kill(partner, SIGTERM); 134 errno = error; 135 err(-1, "socket"); 136 } 137 138 bzero(&sin, sizeof(sin)); 139 sin.sin_family = AF_INET; 140 sin.sin_len = sizeof(sin); 141 sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); 142 sin.sin_port = port; 143 144 if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 145 error = errno; 146 (void)kill(partner, SIGTERM); 147 errno = error; 148 err(-1, "connect"); 149 } 150 151 slen = sizeof(sin_local); 152 if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) { 153 error = errno; 154 (void)kill(partner, SIGTERM); 155 errno = error; 156 err(-1, "getsockname"); 157 } 158 159 /* 160 * Send one byte, make sure that worked, wait for the drop, and try 161 * sending another. By sending small amounts, we avoid blocking 162 * waiting on the remote buffer to be drained. 163 */ 164 ch = 'A'; 165 len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 166 if (len < 0) { 167 error = errno; 168 (void)kill(partner, SIGTERM); 169 errno = error; 170 err(-1, "tcp_client: send (1)"); 171 } 172 if (len != sizeof(ch)) { 173 (void)kill(partner, SIGTERM); 174 errx(-1, "tcp_client: send (1) len"); 175 } 176 177 sleep(5); 178 if (tcp_drop(&sin_local, &sin) < 0) { 179 error = errno; 180 (void)kill(partner, SIGTERM); 181 errno = error; 182 err(-1, "tcp_client: tcp_drop"); 183 } 184 sleep(5); 185 186 ch = 'A'; 187 len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 188 if (len >= 0) { 189 (void)kill(partner, SIGTERM); 190 errx(-1, "tcp_client: send (2): success"); 191 } else if (errno != EPIPE) { 192 error = errno; 193 (void)kill(partner, SIGTERM); 194 errno = error; 195 err(-1, "tcp_client: send (2)"); 196 } 197 close(sock); 198 } 199 200 int 201 main(int argc, char *argv[]) 202 { 203 pid_t child_pid, parent_pid; 204 struct sockaddr_in sin; 205 int listen_fd; 206 u_short port; 207 socklen_t len; 208 209 listen_fd = socket(PF_INET, SOCK_STREAM, 0); 210 if (listen_fd < 0) 211 err(-1, "socket"); 212 213 /* 214 * We use the loopback, but let the kernel select a port for the 215 * server socket. 216 */ 217 bzero(&sin, sizeof(sin)); 218 sin.sin_family = AF_INET; 219 sin.sin_len = sizeof(sin); 220 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 221 222 if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 223 err(-1, "bind"); 224 225 if (listen(listen_fd, -1) < 0) 226 err(-1, "listen"); 227 228 /* 229 * Query the port so that the client can use it. 230 */ 231 bzero(&sin, sizeof(sin)); 232 sin.sin_family = AF_INET; 233 sin.sin_len = sizeof(sin); 234 len = sizeof(sin); 235 if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) 236 err(-1, "getsockname"); 237 port = sin.sin_port; 238 printf("Using port %d\n", ntohs(port)); 239 240 if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) 241 err(-1, "signal"); 242 243 parent_pid = getpid(); 244 child_pid = fork(); 245 if (child_pid < 0) 246 err(-1, "fork"); 247 if (child_pid == 0) { 248 child_pid = getpid(); 249 tcp_server(parent_pid, listen_fd); 250 } else 251 tcp_client(child_pid, port); 252 253 return (0); 254 } 255