xref: /freebsd/tools/regression/netinet/tcpdrop/tcpdrop.c (revision a7623790fb345e6dc986dfd31df0ace115e6f2e4)
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  * $FreeBSD$
31  */
32 
33 /*
34  * TCP regression test for the tcpdrop sysctl; build a loopback TCP
35  * connection, drop it, and make sure both endpoints return that the
36  * connection has been reset.
37  */
38 
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/sysctl.h>
42 
43 #include <netinet/in.h>
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 
53 static int
54 tcp_drop(struct sockaddr_in *sin_local, struct sockaddr_in *sin_remote)
55 {
56 	struct sockaddr_storage addrs[2];
57 
58 	/*
59 	 * Sysctl accepts an array of two sockaddr's, the first being the
60 	 * 'foreign' sockaddr, the second being the 'local' sockaddr.
61 	 */
62 
63 	bcopy(sin_remote, &addrs[0], sizeof(*sin_remote));
64 	bcopy(sin_local, &addrs[1], sizeof(*sin_local));
65 
66 	return (sysctlbyname("net.inet.tcp.drop", NULL, 0, addrs,
67 	    sizeof(addrs)));
68 }
69 
70 static void
71 tcp_server(pid_t partner, int listen_fd)
72 {
73 	int error, accept_fd;
74 	ssize_t len;
75 	char ch;
76 
77 	accept_fd = accept(listen_fd, NULL, NULL);
78 	if (accept_fd < 0) {
79 		error = errno;
80 		(void)kill(partner, SIGTERM);
81 		errno = error;
82 		err(-1, "tcp_server: accept");
83 	}
84 
85 	/*
86 	 * Send one byte, make sure that worked, wait for the drop, and try
87 	 * sending another.  By sending small amounts, we avoid blocking
88 	 * waiting on the remote buffer to be drained.
89 	 */
90 	ch = 'A';
91 	len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL);
92 	if (len < 0) {
93 		error = errno;
94 		(void)kill(partner, SIGTERM);
95 		errno = error;
96 		err(-1, "tcp_server: send (1)");
97 	}
98 	if (len != sizeof(ch)) {
99 		(void)kill(partner, SIGTERM);
100 		errx(-1, "tcp_server: send (1) len");
101 	}
102 
103 	sleep (10);
104 
105 	ch = 'A';
106 	len = send(accept_fd, &ch, sizeof(ch), MSG_NOSIGNAL);
107 	if (len >= 0) {
108 		(void)kill(partner, SIGTERM);
109 		errx(-1, "tcp_server: send (2): success");
110 	} else if (errno != EPIPE) {
111 		error = errno;
112 		(void)kill(partner, SIGTERM);
113 		errno = error;
114 		err(-1, "tcp_server: send (2)");
115 	}
116 
117 	close(accept_fd);
118 	close(listen_fd);
119 }
120 
121 static void
122 tcp_client(pid_t partner, u_short port)
123 {
124 	struct sockaddr_in sin, sin_local;
125 	int error, sock;
126 	socklen_t slen;
127 	ssize_t len;
128 	char ch;
129 
130 	sleep(1);
131 
132 	sock = socket(PF_INET, SOCK_STREAM, 0);
133 	if (sock < 0) {
134 		error = errno;
135 		(void)kill(partner, SIGTERM);
136 		errno = error;
137 		err(-1, "socket");
138 	}
139 
140 	bzero(&sin, sizeof(sin));
141 	sin.sin_family = AF_INET;
142 	sin.sin_len = sizeof(sin);
143 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
144 	sin.sin_port = port;
145 
146 	if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
147 		error = errno;
148 		(void)kill(partner, SIGTERM);
149 		errno = error;
150 		err(-1, "connect");
151 	}
152 
153 	slen = sizeof(sin_local);
154 	if (getsockname(sock, (struct sockaddr *)&sin_local, &slen) < 0) {
155 		error = errno;
156 		(void)kill(partner, SIGTERM);
157 		errno = error;
158 		err(-1, "getsockname");
159 	}
160 
161 	/*
162 	 * Send one byte, make sure that worked, wait for the drop, and try
163 	 * sending another.  By sending small amounts, we avoid blocking
164 	 * waiting on the remote buffer to be drained.
165 	 */
166 	ch = 'A';
167 	len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL);
168 	if (len < 0) {
169 		error = errno;
170 		(void)kill(partner, SIGTERM);
171 		errno = error;
172 		err(-1, "tcp_client: send (1)");
173 	}
174 	if (len != sizeof(ch)) {
175 		(void)kill(partner, SIGTERM);
176 		errx(-1, "tcp_client: send (1) len");
177 	}
178 
179 	sleep(5);
180 	if (tcp_drop(&sin_local, &sin) < 0) {
181 		error = errno;
182 		(void)kill(partner, SIGTERM);
183 		errno = error;
184 		err(-1, "tcp_client: tcp_drop");
185 	}
186 	sleep(5);
187 
188 	ch = 'A';
189 	len = send(sock, &ch, sizeof(ch), MSG_NOSIGNAL);
190 	if (len >= 0) {
191 		(void)kill(partner, SIGTERM);
192 		errx(-1, "tcp_client: send (2): success");
193 	} else if (errno != EPIPE) {
194 		error = errno;
195 		(void)kill(partner, SIGTERM);
196 		errno = error;
197 		err(-1, "tcp_client: send (2)");
198 	}
199 	close(sock);
200 }
201 
202 int
203 main(int argc, char *argv[])
204 {
205 	pid_t child_pid, parent_pid;
206 	struct sockaddr_in sin;
207 	int listen_fd;
208 	u_short port;
209 	socklen_t len;
210 
211 	listen_fd = socket(PF_INET, SOCK_STREAM, 0);
212 	if (listen_fd < 0)
213 		err(-1, "socket");
214 
215 	/*
216 	 * We use the loopback, but let the kernel select a port for the
217 	 * server socket.
218 	 */
219 	bzero(&sin, sizeof(sin));
220 	sin.sin_family = AF_INET;
221 	sin.sin_len = sizeof(sin);
222 	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
223 
224 	if (bind(listen_fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
225 		err(-1, "bind");
226 
227 	if (listen(listen_fd, -1) < 0)
228 		err(-1, "listen");
229 
230 	/*
231 	 * Query the port so that the client can use it.
232 	 */
233 	bzero(&sin, sizeof(sin));
234 	sin.sin_family = AF_INET;
235 	sin.sin_len = sizeof(sin);
236 	len = sizeof(sin);
237 	if (getsockname(listen_fd, (struct sockaddr *)&sin, &len) < 0)
238 		err(-1, "getsockname");
239 	port = sin.sin_port;
240 	printf("Using port %d\n", ntohs(port));
241 
242 	if (signal(SIGCHLD, SIG_IGN) == SIG_ERR)
243 		err(-1, "signal");
244 
245 	parent_pid = getpid();
246 	child_pid = fork();
247 	if (child_pid < 0)
248 		err(-1, "fork");
249 	if (child_pid == 0) {
250 		child_pid = getpid();
251 		tcp_server(parent_pid, listen_fd);
252 	} else
253 		tcp_client(child_pid, port);
254 
255 	return (0);
256 }
257