/client/tests/ipv6connect/src/ipv6connect.c
C | 474 lines | 392 code | 41 blank | 41 comment | 117 complexity | c59c73f41c12080b238774cc6f9b0ae5 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0
- // Copyright 2008 Google Inc. Released under the GPL v2.
- //
- // This test performs numerous connects (with auto-binding), to a server
- // listening on all local addresses using an IPv6 socket, by connecting to
- // 127.0.0.1, ::ffff:127.0.0.1 and ::1.
- //
- // The code is really three tests:
- //
- // - RunWithOneServer, using CreateServer and ConnectAndAccept,
- // uses one server socket and repeatedly connects to it.
- //
- // - RunWithOneShotServers, using CreateServerConnectAndAccept,
- // creates servers, connects to them and then discards them.
- //
- // - RunMultiThreaded, using ThreadedCreateServerConnectAndAccept,
- // ThreadedStartServer and ThreadedGetServerFD, is equivalent to
- // RunWithOneShotServers but uses multiple threads, one for the
- // server and one for the client.
- //
- // Each of these tests triggers error conditions on different kernels
- // to a different extent.
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <pthread.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <time.h>
- #include <unistd.h>
- // Which loopback address to connect to.
- enum LoopbackAddr { V4_LOOPBACK, V6_LOOPBACK, V6_MAPPED_V4_LOOPBACK };
- // Connect to a listening TCP socket, and accept the connection.
- static void ConnectAndAccept(enum LoopbackAddr addr, int server_fd, int port) {
- struct sockaddr_in6 sa;
- socklen_t addr_len;
- int client_fd, accepted_fd;
- if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
- char buf[INET6_ADDRSTRLEN];
- memset(&sa, 0, sizeof(sa));
- if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- if (addr == V6_LOOPBACK) {
- inet_pton(AF_INET6, "::1", &sa.sin6_addr);
- } else if (addr == V6_MAPPED_V4_LOOPBACK) {
- inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
- }
- if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
- perror("inet_ntop");
- exit(1);
- }
- addr_len = sizeof(sa);
- sa.sin6_family = AF_INET6;
- sa.sin6_port = port;
- if (connect(client_fd, (struct sockaddr*)(&sa),
- sizeof(struct sockaddr_in6)) == -1) {
- perror("connect");
- exit(1);
- }
- write(2, (addr == V6_LOOPBACK) ? "+" : "-", 1);
- } else {
- struct sockaddr_in sa4;
- if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- memset(&sa4, 0, sizeof(sa4));
- sa4.sin_family = AF_INET;
- inet_pton(AF_INET, "127.0.0.1", &sa4.sin_addr);
- sa4.sin_port = port;
- if (connect(client_fd, (struct sockaddr*)(&sa4),
- sizeof(struct sockaddr_in)) == -1) {
- perror("connect");
- exit(1);
- }
- write(2, ".", 1);
- }
- addr_len = sizeof(sa);
- if ((accepted_fd = accept(server_fd,
- (struct sockaddr*)(&sa), &addr_len)) == -1) {
- perror("accept");
- exit(1);
- }
- close(client_fd);
- close(accepted_fd);
- }
- // Create a listening TCP socket.
- static void CreateServer(int* server_fd, int* port) {
- struct sockaddr_in6 sa;
- socklen_t addr_len;
- memset(&sa, 0, sizeof(sa));
- if ((*server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- addr_len = sizeof(sa);
- sa.sin6_family = AF_INET6;
- sa.sin6_addr = in6addr_any;
- sa.sin6_port = 0;
- if (bind(*server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
- perror("bind");
- exit(1);
- }
- if (getsockname(*server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
- perror("getsockname");
- exit(1);
- }
- if (listen(*server_fd, 10) == -1) {
- perror("listen");
- exit(1);
- }
- *port = sa.sin6_port;
- }
- // Create a socket, connect to it, accept, and discard both.
- static void CreateServerConnectAndAccept(enum LoopbackAddr addr) {
- struct sockaddr_in6 sa;
- socklen_t addr_len;
- int server_fd, client_fd, accepted_fd, connect_rc;
- if ((server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- addr_len = sizeof(sa);
- memset(&sa, 0, sizeof(sa));
- sa.sin6_family = AF_INET6;
- sa.sin6_addr = in6addr_any;
- sa.sin6_port = 0;
- if (bind(server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
- perror("bind");
- exit(1);
- }
- if (getsockname(server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
- perror("getsockname");
- exit(1);
- }
- if (listen(server_fd, 10) == -1) {
- perror("listen");
- exit(1);
- }
- if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
- char buf[INET6_ADDRSTRLEN];
- if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- if (addr == V6_LOOPBACK) {
- inet_pton(AF_INET6, "::1", &sa.sin6_addr);
- } else if (addr == V6_MAPPED_V4_LOOPBACK) {
- inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
- }
- if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
- perror("inet_ntop");
- exit(1);
- }
- connect_rc = connect(client_fd, (struct sockaddr*)(&sa),
- sizeof(struct sockaddr_in6));
- write(2, (addr == V6_MAPPED_V4_LOOPBACK) ? "-" : "+", 1);
- } else {
- struct sockaddr_in sa4;
- if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- memset(&sa4, 0, sizeof(sa4));
- sa4.sin_family = AF_INET;
- inet_pton(AF_INET, "127.0.0.1", &sa4.sin_addr);
- sa4.sin_port = sa.sin6_port;
- connect_rc = connect(client_fd, (struct sockaddr*)(&sa4),
- sizeof(struct sockaddr_in));
- write(2, ".", 1);
- }
- if (connect_rc == -1) {
- perror("connect");
- exit(1);
- }
- addr_len = sizeof(sa);
- if ((accepted_fd = accept(server_fd,
- (struct sockaddr*)(&sa), &addr_len)) == -1) {
- perror("accept");
- exit(1);
- }
- close(accepted_fd);
- close(client_fd);
- close(server_fd);
- }
- // Globals for threaded version.
- static volatile int threaded_listening = 0;
- static int threaded_server_fd;
- static pthread_mutex_t threaded_mutex = PTHREAD_MUTEX_INITIALIZER;
- static pthread_cond_t threaded_cond = PTHREAD_COND_INITIALIZER;
- // Block until listening, then return server address.
- static int ThreadedGetServerFD() {
- pthread_mutex_lock(&threaded_mutex);
- while (!threaded_listening) {
- pthread_cond_wait(&threaded_cond, &threaded_mutex);
- }
- pthread_mutex_unlock(&threaded_mutex);
- return threaded_server_fd;
- }
- // Start a server which accepts one connection.
- static void* ThreadedStartServer(void* unused) {
- struct sockaddr_in6 sa;
- socklen_t addr_len = sizeof(sa);
- int accept_fd;
- if ((threaded_server_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- // Any IP, unused port.
- memset(&sa, 0, sizeof(sa));
- sa.sin6_family = AF_INET6;
- sa.sin6_addr = in6addr_any;
- sa.sin6_port = 0;
- // Bind.
- if (bind(threaded_server_fd, (struct sockaddr*)(&sa), sizeof(sa)) == -1) {
- perror("bind");
- exit(1);
- }
- // Listen.
- if (listen(threaded_server_fd, 10) == -1) {
- perror("listen");
- exit(1);
- }
- pthread_mutex_lock(&threaded_mutex);
- threaded_listening = 1;
- pthread_cond_signal(&threaded_cond);
- pthread_mutex_unlock(&threaded_mutex);
- // Try to accept.
- if ((accept_fd = accept(threaded_server_fd, (struct sockaddr*)(&sa),
- &addr_len)) == -1) {
- perror("accept");
- exit(1);
- }
- // All done.
- close(threaded_server_fd);
- close(accept_fd);
- threaded_listening = 0;
- return NULL;
- }
- // Start a server thread, then connect to it via TCP.
- static void ThreadedCreateServerConnectAndAccept(enum LoopbackAddr addr) {
- pthread_t pthread;
- int server_fd, client_fd;
- struct sockaddr_in6 sa;
- socklen_t addr_len = sizeof(sa);
- pthread_create(&pthread, NULL, ThreadedStartServer, NULL);
- // Get the server address information -- this call will block until
- // the server is listening.
- server_fd = ThreadedGetServerFD();
- memset(&sa, 0, sizeof(sa));
- if (getsockname(server_fd, (struct sockaddr*)(&sa), &addr_len) == -1) {
- perror("getsockname");
- exit(1);
- }
- if (addr == V6_LOOPBACK || addr == V6_MAPPED_V4_LOOPBACK) {
- char buf[INET6_ADDRSTRLEN];
- if ((client_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- // Check that we are listening on ::
- if (!inet_ntop(AF_INET6, &sa.sin6_addr, buf, INET6_ADDRSTRLEN)) {
- fprintf(stderr, "inet_ntop failed\n");
- exit(1);
- }
- if (strlen(buf) != 2) {
- fprintf(stderr, "Expected to listen on ::, instead listening on %s", buf);
- exit(1);
- }
- if (addr == V6_LOOPBACK) {
- inet_pton(AF_INET6, "::1", &sa.sin6_addr);
- } else if (addr == V6_MAPPED_V4_LOOPBACK) {
- inet_pton(AF_INET6, "::ffff:127.0.0.1", &sa.sin6_addr);
- }
- if (connect(client_fd, (struct sockaddr*)(&sa),
- sizeof(struct sockaddr_in6)) == -1) {
- perror("connect");
- exit(1);
- }
- } else {
- struct sockaddr_in sa4;
- if ((client_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
- perror("socket");
- exit(1);
- }
- memset(&sa4, 0, sizeof(sa4));
- sa4.sin_family = AF_INET;
- inet_aton("127.0.0.1", &sa4.sin_addr);
- sa4.sin_port = sa.sin6_port;
- if (connect(client_fd, (struct sockaddr*)(&sa4),
- sizeof(struct sockaddr_in)) == -1) {
- perror("connect");
- exit(1);
- }
- }
- // Update progress.
- switch (addr) {
- case V4_LOOPBACK:
- write(2, ".", 1);
- break;
- case V6_MAPPED_V4_LOOPBACK:
- write(2, "-", 1);
- break;
- case V6_LOOPBACK:
- write(2, "+", 1);
- break;
- }
- // Close our connection and wait for the server thread to shutdown.
- close(client_fd);
- pthread_join(pthread, NULL);
- }
- static void RunWithOneServer(int outer, int inner) {
- int i, j, server_fd, port;
- fprintf(stderr, "Starting test with one server port for all connects\n");
- for (i = 0; i < outer; ++i) {
- CreateServer(&server_fd, &port);
- for (j = 0; j < inner; ++j) {
- ConnectAndAccept(V4_LOOPBACK, server_fd, port);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- ConnectAndAccept(V6_MAPPED_V4_LOOPBACK, server_fd, port);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- ConnectAndAccept(V6_LOOPBACK, server_fd, port);
- }
- write(2, "\n", 1);
- close(server_fd);
- }
- }
- static void RunWithOneShotServers(int outer, int inner) {
- int i, j;
- fprintf(stderr, "Starting test with one server port per connect\n");
- for (i = 0; i < outer; ++i) {
- for (j = 0; j < inner; ++j) {
- CreateServerConnectAndAccept(V4_LOOPBACK);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- CreateServerConnectAndAccept(V6_MAPPED_V4_LOOPBACK);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- CreateServerConnectAndAccept(V6_LOOPBACK);
- }
- write(2, "\n", 1);
- }
- }
- static void RunMultiThreaded(int outer, int inner) {
- int i, j;
- fprintf(stderr, "Starting multi-threaded test\n");
- for (i = 0; i < outer; ++i) {
- for (j = 0; j < inner; ++j) {
- ThreadedCreateServerConnectAndAccept(V4_LOOPBACK);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- ThreadedCreateServerConnectAndAccept(V6_MAPPED_V4_LOOPBACK);
- }
- write(2, "\n", 1);
- for (j = 0; j < inner; ++j) {
- ThreadedCreateServerConnectAndAccept(V6_LOOPBACK);
- }
- write(2, "\n", 1);
- }
- }
- static const char* usage =
- "Usage: %s [types [outer [inner]]]\n"
- "Arguments:\n"
- "\ttypes: String consisting of [OMT], for the test types to run\n"
- "\t O: One server, multiple connects\n"
- "\t M: One server per connect (multiple server ports)\n"
- "\t T: Multi-threaded version of \'M\'\n"
- "\touter: Number of passes through the outer loops, default 10\n"
- "\tinner: Number of passes through the inner loops, default 75\n";
- static void Usage(char *argv0) {
- fprintf(stderr, usage, argv0);
- exit(2);
- }
- int main(int argc, char** argv) {
- char *types = "OMT";
- int i, inner = 75, outer = 10, timediff;
- struct timeval tv0, tv1;
- // Parse the options.
- if (argc == 4) {
- inner = atoi(argv[3]);
- if (inner <= 0) {
- Usage(argv[0]);
- }
- argc--;
- }
- if (argc == 3) {
- outer = atoi(argv[2]);
- if (outer <= 0) {
- Usage(argv[0]);
- }
- argc--;
- }
- if (argc == 2) {
- types = argv[1];
- if (strspn(types, "OMT") != strlen(types)) {
- Usage(argv[0]);
- }
- argc--;
- }
- if (argc != 1) {
- Usage(argv[0]);
- }
- // Run the tests.
- gettimeofday(&tv0, NULL);
- for (i = 0; i < strlen(types); ++i) {
- switch (types[i]) {
- case 'O':
- RunWithOneServer(outer, inner);
- break;
- case 'M':
- RunWithOneShotServers(outer, inner);
- break;
- case 'T':
- RunMultiThreaded(outer, inner);
- break;
- }
- }
- gettimeofday(&tv1, NULL);
- timediff = (tv1.tv_sec - tv0.tv_sec) * 1000000 + tv1.tv_usec - tv0.tv_usec;
- fprintf(stderr, "Total time = %d.%06ds\n", timediff / 1000000,
- timediff % 1000000);
- exit(0);
- }