1 // Copyright 2008 Google Inc. Released under the GPL v2.
2 //
3 // This test performs numerous connects (with auto-binding), to a server
4 // listening on all local addresses using an IPv6 socket, by connecting to
5 // 127.0.0.1, ::ffff:127.0.0.1 and ::1.
6 //
7 // The code is really three tests:
8 //
9 // - RunWithOneServer, using CreateServer and ConnectAndAccept,
10 // uses one server socket and repeatedly connects to it.
11 //
12 // - RunWithOneShotServers, using CreateServerConnectAndAccept,
13 // creates servers, connects to them and then discards them.
14 //
15 // - RunMultiThreaded, using ThreadedCreateServerConnectAndAccept,
16 // ThreadedStartServer and ThreadedGetServerFD, is equivalent to
17 // RunWithOneShotServers but uses multiple threads, one for the
18 // server and one for the client.
19 //
20 // Each of these tests triggers error conditions on different kernels
21 // to a different extent.
22
23 #include <arpa/inet.h>
24 #include <netinet/in.h>
25 #include <pthread.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/socket.h>
30 #include <sys/time.h>
31 #include <time.h>
32 #include <unistd.h>
33
34 // Which loopback address to connect to.
35 enum LoopbackAddr { V4_LOOPBACK, V6_LOOPBACK, V6_MAPPED_V4_LOOPBACK };
36
37 // Connect to a listening TCP socket, and accept the connection.
ConnectAndAccept(enum LoopbackAddr addr,int server_fd,int port)38 static void ConnectAndAccept(enum LoopbackAddr addr, int server_fd, int port) {
39 struct sockaddr_in6 sa;
40 socklen_t addr_len;
41 int client_fd, accepted_fd;
42
43 if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
44 char buf[INET6_ADDRSTRLEN];
45
46 memset(&sa, 0, sizeof(sa));
47 if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
48 perror("socket");
49 exit(1);
50 }
51 if (addr == V6_LOOPBACK) {
52 inet_pton(AF_INET6, "::1", &sa.sin6_addr);
53 } else if (addr == V6_MAPPED_V4_LOOPBACK) {
54 inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
55 }
56 if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
57 perror("inet_ntop");
58 exit(1);
59 }
60 addr_len = sizeof(sa);
61 sa.sin6_family = AF_INET6;
62 sa.sin6_port = port;
63 if (connect(client_fd, (struct sockaddr*)(&sa),
64 sizeof(struct sockaddr_in6)) == -1) {
65 perror("connect");
66 exit(1);
67 }
68 write(2, (addr == V6_LOOPBACK) ? "+" : "-", 1);
69 } else {
70 struct sockaddr_in sa4;
71
72 if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
73 perror("socket");
74 exit(1);
75 }
76 memset(&sa4, 0, sizeof(sa4));
77 sa4.sin_family = AF_INET;
78 inet_pton(AF_INET, "127.0.0.1", &sa4.sin_addr);
79 sa4.sin_port = port;
80 if (connect(client_fd, (struct sockaddr*)(&sa4),
81 sizeof(struct sockaddr_in)) == -1) {
82 perror("connect");
83 exit(1);
84 }
85 write(2, ".", 1);
86 }
87 addr_len = sizeof(sa);
88 if ((accepted_fd = accept(server_fd,
89 (struct sockaddr*)(&sa), &addr_len)) == -1) {
90 perror("accept");
91 exit(1);
92 }
93 close(client_fd);
94 close(accepted_fd);
95 }
96
97 // Create a listening TCP socket.
CreateServer(int * server_fd,int * port)98 static void CreateServer(int* server_fd, int* port) {
99 struct sockaddr_in6 sa;
100 socklen_t addr_len;
101
102 memset(&sa, 0, sizeof(sa));
103 if ((*server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
104 perror("socket");
105 exit(1);
106 }
107 addr_len = sizeof(sa);
108 sa.sin6_family = AF_INET6;
109 sa.sin6_addr = in6addr_any;
110 sa.sin6_port = 0;
111 if (bind(*server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
112 perror("bind");
113 exit(1);
114 }
115 if (getsockname(*server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
116 perror("getsockname");
117 exit(1);
118 }
119 if (listen(*server_fd, 10) == -1) {
120 perror("listen");
121 exit(1);
122 }
123 *port = sa.sin6_port;
124 }
125
126 // Create a socket, connect to it, accept, and discard both.
CreateServerConnectAndAccept(enum LoopbackAddr addr)127 static void CreateServerConnectAndAccept(enum LoopbackAddr addr) {
128 struct sockaddr_in6 sa;
129 socklen_t addr_len;
130 int server_fd, client_fd, accepted_fd, connect_rc;
131
132 if ((server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
133 perror("socket");
134 exit(1);
135 }
136 addr_len = sizeof(sa);
137 memset(&sa, 0, sizeof(sa));
138 sa.sin6_family = AF_INET6;
139 sa.sin6_addr = in6addr_any;
140 sa.sin6_port = 0;
141 if (bind(server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
142 perror("bind");
143 exit(1);
144 }
145 if (getsockname(server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
146 perror("getsockname");
147 exit(1);
148 }
149 if (listen(server_fd, 10) == -1) {
150 perror("listen");
151 exit(1);
152 }
153 if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
154 char buf[INET6_ADDRSTRLEN];
155
156 if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
157 perror("socket");
158 exit(1);
159 }
160 if (addr == V6_LOOPBACK) {
161 inet_pton(AF_INET6, "::1", &sa.sin6_addr);
162 } else if (addr == V6_MAPPED_V4_LOOPBACK) {
163 inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
164 }
165 if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
166 perror("inet_ntop");
167 exit(1);
168 }
169 connect_rc = connect(client_fd, (struct sockaddr*)(&sa),
170 sizeof(struct sockaddr_in6));
171 write(2, (addr == V6_MAPPED_V4_LOOPBACK) ? "-" : "+", 1);
172 } else {
173 struct sockaddr_in sa4;
174
175 if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
176 perror("socket");
177 exit(1);
178 }
179 memset(&sa4, 0, sizeof(sa4));
180 sa4.sin_family = AF_INET;
181 inet_pton(AF_INET, "127.0.0.1", &sa4.sin_addr);
182 sa4.sin_port = sa.sin6_port;
183 connect_rc = connect(client_fd, (struct sockaddr*)(&sa4),
184 sizeof(struct sockaddr_in));
185 write(2, ".", 1);
186 }
187 if (connect_rc == -1) {
188 perror("connect");
189 exit(1);
190 }
191 addr_len = sizeof(sa);
192 if ((accepted_fd = accept(server_fd,
193 (struct sockaddr*)(&sa), &addr_len)) == -1) {
194 perror("accept");
195 exit(1);
196 }
197 close(accepted_fd);
198 close(client_fd);
199 close(server_fd);
200 }
201
202 // Globals for threaded version.
203 static volatile int threaded_listening = 0;
204 static int threaded_server_fd;
205 static pthread_mutex_t threaded_mutex = PTHREAD_MUTEX_INITIALIZER;
206 static pthread_cond_t threaded_cond = PTHREAD_COND_INITIALIZER;
207
208 // Block until listening, then return server address.
ThreadedGetServerFD()209 static int ThreadedGetServerFD() {
210 pthread_mutex_lock(&threaded_mutex);
211 while (!threaded_listening) {
212 pthread_cond_wait(&threaded_cond, &threaded_mutex);
213 }
214 pthread_mutex_unlock(&threaded_mutex);
215 return threaded_server_fd;
216 }
217
218 // Start a server which accepts one connection.
ThreadedStartServer(void * unused)219 static void* ThreadedStartServer(void* unused) {
220 struct sockaddr_in6 sa;
221 socklen_t addr_len = sizeof(sa);
222 int accept_fd;
223
224 if ((threaded_server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
225 perror("socket");
226 exit(1);
227 }
228
229 // Any IP, unused port.
230 memset(&sa, 0, sizeof(sa));
231 sa.sin6_family = AF_INET6;
232 sa.sin6_addr = in6addr_any;
233 sa.sin6_port = 0;
234
235 // Bind.
236 if (bind(threaded_server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
237 perror("bind");
238 exit(1);
239 }
240
241 // Listen.
242 if (listen(threaded_server_fd, 10) == -1) {
243 perror("listen");
244 exit(1);
245 }
246 pthread_mutex_lock(&threaded_mutex);
247 threaded_listening = 1;
248 pthread_cond_signal(&threaded_cond);
249 pthread_mutex_unlock(&threaded_mutex);
250
251 // Try to accept.
252 if ((accept_fd = accept(threaded_server_fd, (struct sockaddr*)(&sa),
253 &addr_len)) == -1) {
254 perror("accept");
255 exit(1);
256 }
257
258 // All done.
259 close(threaded_server_fd);
260 close(accept_fd);
261 threaded_listening = 0;
262 return NULL;
263 }
264
265 // Start a server thread, then connect to it via TCP.
ThreadedCreateServerConnectAndAccept(enum LoopbackAddr addr)266 static void ThreadedCreateServerConnectAndAccept(enum LoopbackAddr addr) {
267 pthread_t pthread;
268 int server_fd, client_fd;
269 struct sockaddr_in6 sa;
270 socklen_t addr_len = sizeof(sa);
271
272 pthread_create(&pthread, NULL, ThreadedStartServer, NULL);
273
274 // Get the server address information -- this call will block until
275 // the server is listening.
276 server_fd = ThreadedGetServerFD();
277 memset(&sa, 0, sizeof(sa));
278 if (getsockname(server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
279 perror("getsockname");
280 exit(1);
281 }
282
283 if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
284 char buf[INET6_ADDRSTRLEN];
285
286 if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
287 perror("socket");
288 exit(1);
289 }
290
291 // Check that we are listening on ::
292 if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
293 fprintf(stderr, "inet_ntop failed\n");
294 exit(1);
295 }
296 if (strlen(buf) != 2) {
297 fprintf(stderr, "Expected to listen on ::, instead listening on %s", buf);
298 exit(1);
299 }
300
301 if (addr == V6_LOOPBACK) {
302 inet_pton(AF_INET6, "::1", &sa.sin6_addr);
303 } else if (addr == V6_MAPPED_V4_LOOPBACK) {
304 inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
305 }
306 if (connect(client_fd, (struct sockaddr*)(&sa),
307 sizeof(struct sockaddr_in6)) == -1) {
308 perror("connect");
309 exit(1);
310 }
311 } else {
312 struct sockaddr_in sa4;
313
314 if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
315 perror("socket");
316 exit(1);
317 }
318
319 memset(&sa4, 0, sizeof(sa4));
320 sa4.sin_family = AF_INET;
321 inet_aton("127.0.0.1", &sa4.sin_addr);
322 sa4.sin_port = sa.sin6_port;
323
324 if (connect(client_fd, (struct sockaddr*)(&sa4),
325 sizeof(struct sockaddr_in)) == -1) {
326 perror("connect");
327 exit(1);
328 }
329 }
330
331 // Update progress.
332 switch (addr) {
333 case V4_LOOPBACK:
334 write(2, ".", 1);
335 break;
336 case V6_MAPPED_V4_LOOPBACK:
337 write(2, "-", 1);
338 break;
339 case V6_LOOPBACK:
340 write(2, "+", 1);
341 break;
342 }
343
344 // Close our connection and wait for the server thread to shutdown.
345 close(client_fd);
346 pthread_join(pthread, NULL);
347 }
348
RunWithOneServer(int outer,int inner)349 static void RunWithOneServer(int outer, int inner) {
350 int i, j, server_fd, port;
351 fprintf(stderr, "Starting test with one server port for all connects\n");
352 for (i = 0; i < outer; ++i) {
353 CreateServer(&server_fd, &port);
354 for (j = 0; j < inner; ++j) {
355 ConnectAndAccept(V4_LOOPBACK, server_fd, port);
356 }
357 write(2, "\n", 1);
358 for (j = 0; j < inner; ++j) {
359 ConnectAndAccept(V6_MAPPED_V4_LOOPBACK, server_fd, port);
360 }
361 write(2, "\n", 1);
362 for (j = 0; j < inner; ++j) {
363 ConnectAndAccept(V6_LOOPBACK, server_fd, port);
364 }
365 write(2, "\n", 1);
366 close(server_fd);
367 }
368 }
369
RunWithOneShotServers(int outer,int inner)370 static void RunWithOneShotServers(int outer, int inner) {
371 int i, j;
372 fprintf(stderr, "Starting test with one server port per connect\n");
373 for (i = 0; i < outer; ++i) {
374 for (j = 0; j < inner; ++j) {
375 CreateServerConnectAndAccept(V4_LOOPBACK);
376 }
377 write(2, "\n", 1);
378 for (j = 0; j < inner; ++j) {
379 CreateServerConnectAndAccept(V6_MAPPED_V4_LOOPBACK);
380 }
381 write(2, "\n", 1);
382 for (j = 0; j < inner; ++j) {
383 CreateServerConnectAndAccept(V6_LOOPBACK);
384 }
385 write(2, "\n", 1);
386 }
387 }
388
RunMultiThreaded(int outer,int inner)389 static void RunMultiThreaded(int outer, int inner) {
390 int i, j;
391 fprintf(stderr, "Starting multi-threaded test\n");
392 for (i = 0; i < outer; ++i) {
393 for (j = 0; j < inner; ++j) {
394 ThreadedCreateServerConnectAndAccept(V4_LOOPBACK);
395 }
396 write(2, "\n", 1);
397 for (j = 0; j < inner; ++j) {
398 ThreadedCreateServerConnectAndAccept(V6_MAPPED_V4_LOOPBACK);
399 }
400 write(2, "\n", 1);
401 for (j = 0; j < inner; ++j) {
402 ThreadedCreateServerConnectAndAccept(V6_LOOPBACK);
403 }
404 write(2, "\n", 1);
405 }
406 }
407
408 static const char* usage =
409 "Usage: %s [types [outer [inner]]]\n"
410 "Arguments:\n"
411 "\ttypes: String consisting of [OMT], for the test types to run\n"
412 "\t O: One server, multiple connects\n"
413 "\t M: One server per connect (multiple server ports)\n"
414 "\t T: Multi-threaded version of \'M\'\n"
415 "\touter: Number of passes through the outer loops, default 10\n"
416 "\tinner: Number of passes through the inner loops, default 75\n";
417
Usage(char * argv0)418 static void Usage(char *argv0) {
419 fprintf(stderr, usage, argv0);
420 exit(2);
421 }
422
main(int argc,char ** argv)423 int main(int argc, char** argv) {
424 char *types = "OMT";
425 int i, inner = 75, outer = 10, timediff;
426 struct timeval tv0, tv1;
427
428 // Parse the options.
429 if (argc == 4) {
430 inner = atoi(argv[3]);
431 if (inner <= 0) {
432 Usage(argv[0]);
433 }
434 argc--;
435 }
436 if (argc == 3) {
437 outer = atoi(argv[2]);
438 if (outer <= 0) {
439 Usage(argv[0]);
440 }
441 argc--;
442 }
443 if (argc == 2) {
444 types = argv[1];
445 if (strspn(types, "OMT") != strlen(types)) {
446 Usage(argv[0]);
447 }
448 argc--;
449 }
450 if (argc != 1) {
451 Usage(argv[0]);
452 }
453
454 // Run the tests.
455 gettimeofday(&tv0, NULL);
456 for (i = 0; i < strlen(types); ++i) {
457 switch (types[i]) {
458 case 'O':
459 RunWithOneServer(outer, inner);
460 break;
461 case 'M':
462 RunWithOneShotServers(outer, inner);
463 break;
464 case 'T':
465 RunMultiThreaded(outer, inner);
466 break;
467 }
468 }
469 gettimeofday(&tv1, NULL);
470 timediff = (tv1.tv_sec - tv0.tv_sec) * 1000000 + tv1.tv_usec - tv0.tv_usec;
471 fprintf(stderr, "Total time = %d.%06ds\n", timediff / 1000000,
472 timediff % 1000000);
473 exit(0);
474 }
475