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