xref: /freebsd/tools/regression/netinet/tcpdrop/tcpdrop.c (revision b3e7694832e81d7a904a10f525f8797b753bf0d3)
11b2c9809SRobert Watson /*-
21b2c9809SRobert Watson  * Copyright (c) 2006 Robert N. M. Watson
3e8c54602SRobert Watson  * Copyright (c) 2011 Juniper Networks, Inc.
41b2c9809SRobert Watson  * All rights reserved.
51b2c9809SRobert Watson  *
6e8c54602SRobert Watson  * Portions of this software were developed by Robert N. M. Watson under
7e8c54602SRobert Watson  * contract to Juniper Networks, Inc.
8e8c54602SRobert 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 
311b2c9809SRobert Watson /*
321b2c9809SRobert Watson  * TCP regression test for the tcpdrop sysctl; build a loopback TCP
331b2c9809SRobert Watson  * connection, drop it, and make sure both endpoints return that the
341b2c9809SRobert Watson  * connection has been reset.
351b2c9809SRobert Watson  */
361b2c9809SRobert Watson 
371b2c9809SRobert Watson #include <sys/types.h>
381b2c9809SRobert Watson #include <sys/socket.h>
391b2c9809SRobert Watson #include <sys/sysctl.h>
401b2c9809SRobert Watson 
411b2c9809SRobert Watson #include <netinet/in.h>
421b2c9809SRobert Watson 
431b2c9809SRobert Watson #include <err.h>
441b2c9809SRobert Watson #include <errno.h>
451b2c9809SRobert Watson #include <signal.h>
461b2c9809SRobert Watson #include <stdio.h>
471b2c9809SRobert Watson #include <stdlib.h>
481b2c9809SRobert Watson #include <string.h>
491b2c9809SRobert Watson #include <unistd.h>
501b2c9809SRobert Watson 
511b2c9809SRobert Watson static int
tcp_drop(struct sockaddr_in * sin_local,struct sockaddr_in * sin_remote)521b2c9809SRobert Watson tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote)
531b2c9809SRobert Watson {
541b2c9809SRobert Watson 	struct sockaddr_storage addrs[2];
551b2c9809SRobert Watson 
561b2c9809SRobert Watson 	/*
571b2c9809SRobert Watson 	 * Sysctl accepts an array of two sockaddr's, the first being the
581b2c9809SRobert Watson 	 * 'foreign' sockaddr, the second being the 'local' sockaddr.
591b2c9809SRobert Watson 	 */
601b2c9809SRobert Watson 
611b2c9809SRobert Watson 	bcopy(sin_remote, &addrs[0], sizeof(*sin_remote));
621b2c9809SRobert Watson 	bcopy(sin_local, &addrs[1], sizeof(*sin_local));
631b2c9809SRobert Watson 
641b2c9809SRobert Watson 	return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs,
651b2c9809SRobert Watson 	    sizeof(addrs)));
661b2c9809SRobert Watson }
671b2c9809SRobert Watson 
681b2c9809SRobert Watson static void
tcp_server(pid_t partner,int listen_fd)69e8c54602SRobert Watson tcp_server(pid_t partner, int listen_fd)
701b2c9809SRobert Watson {
71e8c54602SRobert Watson 	int error, accept_fd;
721b2c9809SRobert Watson 	ssize_t len;
731b2c9809SRobert Watson 	char ch;
741b2c9809SRobert Watson 
751b2c9809SRobert Watson 	accept_fd = accept(listen_fd, NULL, NULL);
761b2c9809SRobert Watson 	if (accept_fd < 0) {
771b2c9809SRobert Watson 		error = errno;
781b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
791b2c9809SRobert Watson 		errno = error;
801b2c9809SRobert Watson 		err(-1, "tcp_server: accept");
811b2c9809SRobert Watson 	}
821b2c9809SRobert Watson 
831b2c9809SRobert Watson 	/*
841b2c9809SRobert Watson 	 * Send one byte, make sure that worked, wait for the drop, and try
851b2c9809SRobert Watson 	 * sending another.  By sending small amounts, we avoid blocking
861b2c9809SRobert Watson 	 * waiting on the remote buffer to be drained.
871b2c9809SRobert Watson 	 */
881b2c9809SRobert Watson 	ch = 'A';
891b2c9809SRobert Watson 	len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL);
901b2c9809SRobert Watson 	if (len < 0) {
911b2c9809SRobert Watson 		error = errno;
921b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
931b2c9809SRobert Watson 		errno = error;
941b2c9809SRobert Watson 		err(-1, "tcp_server: send (1)");
951b2c9809SRobert Watson 	}
961b2c9809SRobert Watson 	if (len != sizeof(ch)) {
971b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
981b2c9809SRobert Watson 		errx(-1, "tcp_server: send (1) len");
991b2c9809SRobert Watson 	}
1001b2c9809SRobert Watson 
1011b2c9809SRobert Watson 	sleep (10);
1021b2c9809SRobert Watson 
1031b2c9809SRobert Watson 	ch = 'A';
1041b2c9809SRobert Watson 	len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL);
1051b2c9809SRobert Watson 	if (len >= 0) {
1061b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1071b2c9809SRobert Watson 		errx(-1, "tcp_server: send (2): success");
1081b2c9809SRobert Watson 	} else if (errno != EPIPE) {
1091b2c9809SRobert Watson 		error = errno;
1101b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1111b2c9809SRobert Watson 		errno = error;
1121b2c9809SRobert Watson 		err(-1, "tcp_server: send (2)");
1131b2c9809SRobert Watson 	}
1141b2c9809SRobert Watson 
1151b2c9809SRobert Watson 	close(accept_fd);
1161b2c9809SRobert Watson 	close(listen_fd);
1171b2c9809SRobert Watson }
1181b2c9809SRobert Watson 
1191b2c9809SRobert Watson static void
tcp_client(pid_t partner,u_short port)120e8c54602SRobert Watson tcp_client(pid_t partner, u_short port)
1211b2c9809SRobert Watson {
1221b2c9809SRobert Watson 	struct sockaddr_in sin, sin_local;
1231b2c9809SRobert Watson 	int error, sock;
1241b2c9809SRobert Watson 	socklen_t slen;
1251b2c9809SRobert Watson 	ssize_t len;
1261b2c9809SRobert Watson 	char ch;
1271b2c9809SRobert Watson 
1281b2c9809SRobert Watson 	sleep(1);
1291b2c9809SRobert Watson 
1301b2c9809SRobert Watson 	sock = socket(PF_INET, SOCK_STREAM, 0);
1311b2c9809SRobert Watson 	if (sock < 0) {
1321b2c9809SRobert Watson 		error = errno;
1331b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1341b2c9809SRobert Watson 		errno = error;
1351b2c9809SRobert Watson 		err(-1, "socket");
1361b2c9809SRobert Watson 	}
1371b2c9809SRobert Watson 
1381b2c9809SRobert Watson 	bzero(&sin, sizeof(sin));
1391b2c9809SRobert Watson 	sin.sin_family = AF_INET;
1401b2c9809SRobert Watson 	sin.sin_len = sizeof(sin);
1411b2c9809SRobert Watson 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
142e8c54602SRobert Watson 	sin.sin_port = port;
1431b2c9809SRobert Watson 
1441b2c9809SRobert Watson 	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
1451b2c9809SRobert Watson 		error = errno;
1461b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1471b2c9809SRobert Watson 		errno = error;
1481b2c9809SRobert Watson 		err(-1, "connect");
1491b2c9809SRobert Watson 	}
1501b2c9809SRobert Watson 
1511b2c9809SRobert Watson 	slen = sizeof(sin_local);
1521b2c9809SRobert Watson 	if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) {
1531b2c9809SRobert Watson 		error = errno;
1541b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1551b2c9809SRobert Watson 		errno = error;
1561b2c9809SRobert Watson 		err(-1, "getsockname");
1571b2c9809SRobert Watson 	}
1581b2c9809SRobert Watson 
1591b2c9809SRobert Watson 	/*
1601b2c9809SRobert Watson 	 * Send one byte, make sure that worked, wait for the drop, and try
1611b2c9809SRobert Watson 	 * sending another.  By sending small amounts, we avoid blocking
1621b2c9809SRobert Watson 	 * waiting on the remote buffer to be drained.
1631b2c9809SRobert Watson 	 */
1641b2c9809SRobert Watson 	ch = 'A';
1651b2c9809SRobert Watson 	len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL);
1661b2c9809SRobert Watson 	if (len < 0) {
1671b2c9809SRobert Watson 		error = errno;
1681b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1691b2c9809SRobert Watson 		errno = error;
1701b2c9809SRobert Watson 		err(-1, "tcp_client: send (1)");
1711b2c9809SRobert Watson 	}
1721b2c9809SRobert Watson 	if (len != sizeof(ch)) {
1731b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1741b2c9809SRobert Watson 		errx(-1, "tcp_client: send (1) len");
1751b2c9809SRobert Watson 	}
1761b2c9809SRobert Watson 
1771b2c9809SRobert Watson 	sleep(5);
1781b2c9809SRobert Watson 	if (tcp_drop(&sin_local, &sin) < 0) {
1791b2c9809SRobert Watson 		error = errno;
1801b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1811b2c9809SRobert Watson 		errno = error;
1821b2c9809SRobert Watson 		err(-1, "tcp_client: tcp_drop");
1831b2c9809SRobert Watson 	}
1841b2c9809SRobert Watson 	sleep(5);
1851b2c9809SRobert Watson 
1861b2c9809SRobert Watson 	ch = 'A';
1871b2c9809SRobert Watson 	len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL);
1881b2c9809SRobert Watson 	if (len >= 0) {
1891b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1901b2c9809SRobert Watson 		errx(-1, "tcp_client: send (2): success");
1911b2c9809SRobert Watson 	} else if (errno != EPIPE) {
1921b2c9809SRobert Watson 		error = errno;
1931b2c9809SRobert Watson 		(void)kill(partner, SIGTERM);
1941b2c9809SRobert Watson 		errno = error;
1951b2c9809SRobert Watson 		err(-1, "tcp_client: send (2)");
1961b2c9809SRobert Watson 	}
1971b2c9809SRobert Watson 	close(sock);
1981b2c9809SRobert Watson }
1991b2c9809SRobert Watson 
2001b2c9809SRobert Watson int
main(int argc,char * argv[])2011b2c9809SRobert Watson main(int argc, char *argv[])
2021b2c9809SRobert Watson {
2031b2c9809SRobert Watson 	pid_t child_pid, parent_pid;
204e8c54602SRobert Watson 	struct sockaddr_in sin;
205e8c54602SRobert Watson 	int listen_fd;
206e8c54602SRobert Watson 	u_short port;
207e8c54602SRobert Watson 	socklen_t len;
208e8c54602SRobert Watson 
209e8c54602SRobert Watson 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
210e8c54602SRobert Watson 	if (listen_fd < 0)
211e8c54602SRobert Watson 		err(-1, "socket");
212e8c54602SRobert Watson 
213e8c54602SRobert Watson 	/*
214e8c54602SRobert Watson 	 * We use the loopback, but let the kernel select a port for the
215e8c54602SRobert Watson 	 * server socket.
216e8c54602SRobert Watson 	 */
217e8c54602SRobert Watson 	bzero(&sin, sizeof(sin));
218e8c54602SRobert Watson 	sin.sin_family = AF_INET;
219e8c54602SRobert Watson 	sin.sin_len = sizeof(sin);
220e8c54602SRobert Watson 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
221e8c54602SRobert Watson 
222e8c54602SRobert Watson 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
223e8c54602SRobert Watson 		err(-1, "bind");
224e8c54602SRobert Watson 
225e8c54602SRobert Watson 	if (listen(listen_fd, -1) < 0)
226e8c54602SRobert Watson 		err(-1, "listen");
227e8c54602SRobert Watson 
228e8c54602SRobert Watson 	/*
229e8c54602SRobert Watson 	 * Query the port so that the client can use it.
230e8c54602SRobert Watson 	 */
231e8c54602SRobert Watson 	bzero(&sin, sizeof(sin));
232e8c54602SRobert Watson 	sin.sin_family = AF_INET;
233e8c54602SRobert Watson 	sin.sin_len = sizeof(sin);
234*e397f116SRobert Watson 	len = sizeof(sin);
235e8c54602SRobert Watson 	if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)
236e8c54602SRobert Watson 		err(-1, "getsockname");
237e8c54602SRobert Watson 	port = sin.sin_port;
238e8c54602SRobert Watson 	printf("Using port %d\n", ntohs(port));
2391b2c9809SRobert Watson 
2401b2c9809SRobert Watson 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
2411b2c9809SRobert Watson 		err(-1, "signal");
2421b2c9809SRobert Watson 
2431b2c9809SRobert Watson 	parent_pid = getpid();
2441b2c9809SRobert Watson 	child_pid = fork();
2451b2c9809SRobert Watson 	if (child_pid < 0)
2461b2c9809SRobert Watson 		err(-1, "fork");
2471b2c9809SRobert Watson 	if (child_pid == 0) {
2481b2c9809SRobert Watson 		child_pid = getpid();
249e8c54602SRobert Watson 		tcp_server(parent_pid, listen_fd);
2501b2c9809SRobert Watson 	} else
251e8c54602SRobert Watson 		tcp_client(child_pid, port);
2521b2c9809SRobert Watson 
2531b2c9809SRobert Watson 	return (0);
2541b2c9809SRobert Watson }
255