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
tcp_drop(struct sockaddr_in * sin_local,struct sockaddr_in * sin_remote)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
tcp_server(pid_t partner,int listen_fd)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
tcp_client(pid_t partner,u_short port)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
main(int argc,char * argv[])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