]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/netinet/tcpdrop/tcpdrop.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / regression / netinet / tcpdrop / tcpdrop.c
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 }