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