Home | History | Annotate | Download | only in src
      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.
     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.
     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.
    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.
    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.
    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.
    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 
    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 
    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 
    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 
    418 static void Usage(char *argv0) {
    419   fprintf(stderr, usage, argv0);
    420   exit(2);
    421 }
    422 
    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