1157426Srwatson/*- 2157426Srwatson * Copyright (c) 2006 Robert N. M. Watson 3222484Srwatson * Copyright (c) 2011 Juniper Networks, Inc. 4157426Srwatson * All rights reserved. 5157426Srwatson * 6222484Srwatson * Portions of this software were developed by Robert N. M. Watson under 7222484Srwatson * contract to Juniper Networks, Inc. 8222484Srwatson * 9157426Srwatson * Redistribution and use in source and binary forms, with or without 10157426Srwatson * modification, are permitted provided that the following conditions 11157426Srwatson * are met: 12157426Srwatson * 1. Redistributions of source code must retain the above copyright 13157426Srwatson * notice, this list of conditions and the following disclaimer. 14157426Srwatson * 2. Redistributions in binary form must reproduce the above copyright 15157426Srwatson * notice, this list of conditions and the following disclaimer in the 16157426Srwatson * documentation and/or other materials provided with the distribution. 17157426Srwatson * 18157426Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19157426Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20157426Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21157426Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22157426Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23157426Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24157426Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25157426Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26157426Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27157426Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28157426Srwatson * SUCH DAMAGE. 29157426Srwatson * 30157426Srwatson * $FreeBSD$ 31157426Srwatson */ 32157426Srwatson 33157426Srwatson/* 34157426Srwatson * TCP regression test for the tcpdrop sysctl; build a loopback TCP 35157426Srwatson * connection, drop it, and make sure both endpoints return that the 36157426Srwatson * connection has been reset. 37157426Srwatson */ 38157426Srwatson 39157426Srwatson#include <sys/types.h> 40157426Srwatson#include <sys/socket.h> 41157426Srwatson#include <sys/sysctl.h> 42157426Srwatson 43157426Srwatson#include <netinet/in.h> 44157426Srwatson 45157426Srwatson#include <err.h> 46157426Srwatson#include <errno.h> 47157426Srwatson#include <signal.h> 48157426Srwatson#include <stdio.h> 49157426Srwatson#include <stdlib.h> 50157426Srwatson#include <string.h> 51157426Srwatson#include <unistd.h> 52157426Srwatson 53157426Srwatsonstatic int 54157426Srwatsontcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote) 55157426Srwatson{ 56157426Srwatson struct sockaddr_storage addrs[2]; 57157426Srwatson 58157426Srwatson /* 59157426Srwatson * Sysctl accepts an array of two sockaddr's, the first being the 60157426Srwatson * 'foreign' sockaddr, the second being the 'local' sockaddr. 61157426Srwatson */ 62157426Srwatson 63157426Srwatson bcopy(sin_remote, &addrs[0], sizeof(*sin_remote)); 64157426Srwatson bcopy(sin_local, &addrs[1], sizeof(*sin_local)); 65157426Srwatson 66157426Srwatson return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs, 67157426Srwatson sizeof(addrs))); 68157426Srwatson} 69157426Srwatson 70157426Srwatsonstatic void 71222484Srwatsontcp_server(pid_t partner, int listen_fd) 72157426Srwatson{ 73222484Srwatson int error, accept_fd; 74157426Srwatson ssize_t len; 75157426Srwatson char ch; 76157426Srwatson 77157426Srwatson accept_fd = accept(listen_fd, NULL, NULL); 78157426Srwatson if (accept_fd < 0) { 79157426Srwatson error = errno; 80157426Srwatson (void)kill(partner, SIGTERM); 81157426Srwatson errno = error; 82157426Srwatson err(-1, "tcp_server: accept"); 83157426Srwatson } 84157426Srwatson 85157426Srwatson /* 86157426Srwatson * Send one byte, make sure that worked, wait for the drop, and try 87157426Srwatson * sending another. By sending small amounts, we avoid blocking 88157426Srwatson * waiting on the remote buffer to be drained. 89157426Srwatson */ 90157426Srwatson ch = 'A'; 91157426Srwatson len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 92157426Srwatson if (len < 0) { 93157426Srwatson error = errno; 94157426Srwatson (void)kill(partner, SIGTERM); 95157426Srwatson errno = error; 96157426Srwatson err(-1, "tcp_server: send (1)"); 97157426Srwatson } 98157426Srwatson if (len != sizeof(ch)) { 99157426Srwatson (void)kill(partner, SIGTERM); 100157426Srwatson errx(-1, "tcp_server: send (1) len"); 101157426Srwatson } 102157426Srwatson 103157426Srwatson sleep (10); 104157426Srwatson 105157426Srwatson ch = 'A'; 106157426Srwatson len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL); 107157426Srwatson if (len >= 0) { 108157426Srwatson (void)kill(partner, SIGTERM); 109157426Srwatson errx(-1, "tcp_server: send (2): success"); 110157426Srwatson } else if (errno != EPIPE) { 111157426Srwatson error = errno; 112157426Srwatson (void)kill(partner, SIGTERM); 113157426Srwatson errno = error; 114157426Srwatson err(-1, "tcp_server: send (2)"); 115157426Srwatson } 116157426Srwatson 117157426Srwatson close(accept_fd); 118157426Srwatson close(listen_fd); 119157426Srwatson} 120157426Srwatson 121157426Srwatsonstatic void 122222484Srwatsontcp_client(pid_t partner, u_short port) 123157426Srwatson{ 124157426Srwatson struct sockaddr_in sin, sin_local; 125157426Srwatson int error, sock; 126157426Srwatson socklen_t slen; 127157426Srwatson ssize_t len; 128157426Srwatson char ch; 129157426Srwatson 130157426Srwatson sleep(1); 131157426Srwatson 132157426Srwatson sock = socket(PF_INET, SOCK_STREAM, 0); 133157426Srwatson if (sock < 0) { 134157426Srwatson error = errno; 135157426Srwatson (void)kill(partner, SIGTERM); 136157426Srwatson errno = error; 137157426Srwatson err(-1, "socket"); 138157426Srwatson } 139157426Srwatson 140157426Srwatson bzero(&sin, sizeof(sin)); 141157426Srwatson sin.sin_family = AF_INET; 142157426Srwatson sin.sin_len = sizeof(sin); 143157426Srwatson sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK); 144222484Srwatson sin.sin_port = port; 145157426Srwatson 146157426Srwatson if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 147157426Srwatson error = errno; 148157426Srwatson (void)kill(partner, SIGTERM); 149157426Srwatson errno = error; 150157426Srwatson err(-1, "connect"); 151157426Srwatson } 152157426Srwatson 153157426Srwatson slen = sizeof(sin_local); 154157426Srwatson if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) { 155157426Srwatson error = errno; 156157426Srwatson (void)kill(partner, SIGTERM); 157157426Srwatson errno = error; 158157426Srwatson err(-1, "getsockname"); 159157426Srwatson } 160157426Srwatson 161157426Srwatson /* 162157426Srwatson * Send one byte, make sure that worked, wait for the drop, and try 163157426Srwatson * sending another. By sending small amounts, we avoid blocking 164157426Srwatson * waiting on the remote buffer to be drained. 165157426Srwatson */ 166157426Srwatson ch = 'A'; 167157426Srwatson len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 168157426Srwatson if (len < 0) { 169157426Srwatson error = errno; 170157426Srwatson (void)kill(partner, SIGTERM); 171157426Srwatson errno = error; 172157426Srwatson err(-1, "tcp_client: send (1)"); 173157426Srwatson } 174157426Srwatson if (len != sizeof(ch)) { 175157426Srwatson (void)kill(partner, SIGTERM); 176157426Srwatson errx(-1, "tcp_client: send (1) len"); 177157426Srwatson } 178157426Srwatson 179157426Srwatson sleep(5); 180157426Srwatson if (tcp_drop(&sin_local, &sin) < 0) { 181157426Srwatson error = errno; 182157426Srwatson (void)kill(partner, SIGTERM); 183157426Srwatson errno = error; 184157426Srwatson err(-1, "tcp_client: tcp_drop"); 185157426Srwatson } 186157426Srwatson sleep(5); 187157426Srwatson 188157426Srwatson ch = 'A'; 189157426Srwatson len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL); 190157426Srwatson if (len >= 0) { 191157426Srwatson (void)kill(partner, SIGTERM); 192157426Srwatson errx(-1, "tcp_client: send (2): success"); 193157426Srwatson } else if (errno != EPIPE) { 194157426Srwatson error = errno; 195157426Srwatson (void)kill(partner, SIGTERM); 196157426Srwatson errno = error; 197157426Srwatson err(-1, "tcp_client: send (2)"); 198157426Srwatson } 199157426Srwatson close(sock); 200157426Srwatson} 201157426Srwatson 202157426Srwatsonint 203157426Srwatsonmain(int argc, char *argv[]) 204157426Srwatson{ 205157426Srwatson pid_t child_pid, parent_pid; 206222484Srwatson struct sockaddr_in sin; 207222484Srwatson int listen_fd; 208222484Srwatson u_short port; 209222484Srwatson socklen_t len; 210157426Srwatson 211222484Srwatson listen_fd = socket(PF_INET, SOCK_STREAM, 0); 212222484Srwatson if (listen_fd < 0) 213222484Srwatson err(-1, "socket"); 214222484Srwatson 215222484Srwatson /* 216222484Srwatson * We use the loopback, but let the kernel select a port for the 217222484Srwatson * server socket. 218222484Srwatson */ 219222484Srwatson bzero(&sin, sizeof(sin)); 220222484Srwatson sin.sin_family = AF_INET; 221222484Srwatson sin.sin_len = sizeof(sin); 222222484Srwatson sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 223222484Srwatson 224222484Srwatson if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 225222484Srwatson err(-1, "bind"); 226222484Srwatson 227222484Srwatson if (listen(listen_fd, -1) < 0) 228222484Srwatson err(-1, "listen"); 229222484Srwatson 230222484Srwatson /* 231222484Srwatson * Query the port so that the client can use it. 232222484Srwatson */ 233222484Srwatson bzero(&sin, sizeof(sin)); 234222484Srwatson sin.sin_family = AF_INET; 235222484Srwatson sin.sin_len = sizeof(sin); 236224688Srwatson len = sizeof(sin); 237222484Srwatson if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0) 238222484Srwatson err(-1, "getsockname"); 239222484Srwatson port = sin.sin_port; 240222484Srwatson printf("Using port %d\n", ntohs(port)); 241222484Srwatson 242157426Srwatson if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) 243157426Srwatson err(-1, "signal"); 244157426Srwatson 245157426Srwatson parent_pid = getpid(); 246157426Srwatson child_pid = fork(); 247157426Srwatson if (child_pid < 0) 248157426Srwatson err(-1, "fork"); 249157426Srwatson if (child_pid == 0) { 250157426Srwatson child_pid = getpid(); 251222484Srwatson tcp_server(parent_pid, listen_fd); 252157426Srwatson } else 253222484Srwatson tcp_client(child_pid, port); 254157426Srwatson 255157426Srwatson return (0); 256157426Srwatson} 257