xref: /freebsd/tools/regression/netinet/tcpdrop/tcpdrop.c (revision e8c546028ba24e1dc196c0771869ef9ad5dcbd19)
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