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