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