1 /* $NetBSD: perftcpdns.c,v 1.3 2014/12/10 04:37:56 christos Exp $ */ 2 3 /* 4 * Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC") 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * TCP DNS perf tool 21 * 22 * main parameters are -r<rate> and <server> 23 * standard options are 4|6 (IPv4|IPv6), rate computations, terminaisons, 24 * EDNS0, NOERROR|NXDOMAIN, template (for your own query), diags, 25 * alternate server port and UDP version. 26 * 27 * To help to crush kernels (unfortunately both client and server :-) 28 * this version of the tool is multi-threaded: 29 * - the master thread inits, monitors the activity each millisecond, 30 * and report results when finished 31 * - the connecting thread computes the date of the next connection, 32 * creates a socket, makes it non blocking, binds it if wanted, 33 * connects it and pushes it on the output epoll queue 34 * - the sending thread gets by epoll connected sockets, timeouts 35 * embryonic connections, sends queries and pushes sockets on 36 * the input epoll queue 37 * - the receiving thread gets by epoll sockets with a pending 38 * response, receives responses, timeouts unanswered queries, 39 * and recycles (by closing them) all sockets. 40 * 41 * Rate computation details: 42 * - the target rate is in query+response per second. 43 * - rating is done by the connecting thread. 44 * - of course the tool is always late so the target rate is never 45 * reached. BTW there is no attempt to internally adjust the 46 * effective rate to the target one: this must be by tuning 47 * the rate related parameters, first the -r<rate> itself. 48 * - at the beginning of the connecting thread iteration loop 49 * (second "loops" counter) the date of the due (aka next) connect() 50 * call is computed from the last one with 101% of the rate. 51 * - the due date is compared with the current date (aka now). 52 * - if the due is before, lateconn counter is incremented, else 53 * the thread sleeps for the difference, 54 * - the next step is to reget the current date, if it is still 55 * before the due date (e.g., because the sleep was interrupted) 56 * the first shortwait counter is incremented. 57 * - if it is after (common case) the number of connect calls is 58 * computed from the difference between now and due divided by rate, 59 * rounded to the next number, 60 * - this number of connect() calls is bounded by the -a<aggressiveness> 61 * parameter to avoid too many back to back new connection attempts. 62 * - the compconn counter is incremented, errors (other than EINPROGRESS 63 * from not blocking connect()) are printed. When an error is 64 * related to a local limit (e.g., EMFILE, EADDRNOTAVAIL or the 65 * internal ENOMEM) the locallimit counter is incremented. 66 */ 67 68 #ifdef __linux__ 69 #define _GNU_SOURCE 70 #endif 71 72 #include <sys/types.h> 73 #include <sys/epoll.h> 74 #include <sys/prctl.h> 75 #include <sys/select.h> 76 #include <sys/socket.h> 77 #include <sys/wait.h> 78 79 #include <netinet/in.h> 80 #include <arpa/inet.h> 81 82 #include <ctype.h> 83 #include <errno.h> 84 #include <fcntl.h> 85 #include <math.h> 86 #include <netdb.h> 87 #include <pthread.h> 88 #include <signal.h> 89 #include <stdint.h> 90 #include <stdio.h> 91 #include <stdlib.h> 92 #include <string.h> 93 #include <time.h> 94 #include <unistd.h> 95 96 /* DNS defines */ 97 98 #define NS_TYPE_A 1 99 #define NS_TYPE_NS 2 100 #define NS_TYPE_CNAME 5 101 #define NS_TYPE_SOA 6 102 #define NS_TYPE_NULL 10 103 #define NS_TYPE_PTR 12 104 #define NS_TYPE_MX 15 105 #define NS_TYPE_TXT 16 106 #define NS_TYPE_AAAA 28 107 #define NS_TYPE_OPT 41 108 #define NS_TYPE_DS 43 109 #define NS_TYPE_RRSIG 46 110 #define NS_TYPE_NSEC 47 111 #define NS_TYPE_DNSKEY 48 112 #define NS_TYPE_NSEC3 50 113 #define NS_TYPE_NSEC3PARAM 51 114 #define NS_TYPE_TSIG 250 115 #define NS_TYPE_IXFR 251 116 #define NS_TYPE_AXFR 252 117 #define NS_TYPE_ANY 255 118 119 #define NS_CLASS_IN 1 120 #define NS_CLASS_ANY 255 121 122 #define NS_OFF_ID 0 123 #define NS_OFF_FLAGS 2 124 #define NS_OFF_QDCOUNT 4 125 #define NS_OFF_ANCOUNT 6 126 #define NS_OFF_NSCOUNT 8 127 #define NS_OFF_ARCOUNT 10 128 #define NS_OFF_QUESTION 12 129 130 #define NS_FLAG_QR 0x8000U 131 #define NS_FLAG_AA 0x0400U 132 #define NS_FLAG_TC 0x0200U 133 #define NS_FLAG_RD 0x0100U 134 #define NS_FLAG_RA 0x0080U 135 #define NS_FLAG_AD 0x0020U 136 #define NS_FLAG_CD 0x0010U 137 138 #define NS_XFLAG_DO 0x8000U 139 140 #define NS_OPCODE_MASK 0x7000U 141 #define NS_OPCODE_QUERY 0 142 143 #define NS_RCODE_MASK 0x000fU 144 #define NS_RCODE_NOERROR 0 145 #define NS_RCODE_FORMERR 1 146 #define NS_RCODE_SERVFAIL 2 147 #define NS_RCODE_NXDOMAIN 3 148 #define NS_RCODE_NOIMP 4 149 #define NS_RCODE_REFUSED 5 150 #define NS_RCODE_LAST 6 151 152 /* chaining macros */ 153 154 #define ISC_INIT(head, headl) do { \ 155 (head) = -1; \ 156 (headl) = &(head); \ 157 } while (/*CONSTCOND*/0) 158 159 #define ISC_INSERT(head, headl, elm) do { \ 160 (elm)->next = -1; \ 161 (elm)->prev = (headl); \ 162 *(headl) = (elm) - xlist; \ 163 (headl) = &((elm)->next); \ 164 } while (/*CONSTCOND*/0) 165 166 #define ISC_REMOVE(headl, elm) do { \ 167 if ((elm)->next != -1) \ 168 xlist[(elm)->next].prev = (elm)->prev; \ 169 else \ 170 (headl) = (elm)->prev; \ 171 *(elm)->prev = (elm)->next; \ 172 } while (/*CONSTCOND*/0) 173 174 /* 175 * Data structures 176 */ 177 178 /* 179 * exchange: 180 * - per exchange values: 181 * * order (for debugging) 182 * * id 183 * * random (for debugging) 184 * * time-stamps 185 * 186 * sent/rcvd chain, "next to be received" on entry cache. 187 */ 188 189 struct exchange { /* per exchange structure */ 190 int sock; /* socket descriptor */ 191 int next, *prev; /* chaining */ 192 #define X_FREE 0 193 #define X_CONN 1 194 #define X_READY 2 195 #define X_SENT 3 196 int state; /* state */ 197 uint16_t id; /* ID */ 198 uint64_t order; /* number of this exchange */ 199 struct timespec ts0, ts1, ts2, ts3; /* timespecs */ 200 }; 201 struct exchange *xlist; /* exchange list */ 202 int xlast; /* number of exchanges */ 203 int xconn, *xconnl; /* connecting list */ 204 int xready, *xreadyl; /* connected list */ 205 int xsent, *xsentl; /* sent list */ 206 int xfree, *xfreel; /* free list */ 207 int xused; /* next to be used list */ 208 pthread_mutex_t mtxconn, mtxsent, mtxfree; /* mutexes */ 209 uint64_t xccount; /* connected counters */ 210 uint64_t xscount; /* sent counters */ 211 uint64_t xrcount; /* received counters */ 212 213 /* 214 * statictics counters and accumulators 215 */ 216 217 uint64_t recverr, tooshort, locallimit; /* error counters */ 218 uint64_t loops[4], shortwait[3]; /* rate stats */ 219 uint64_t lateconn, compconn; /* rate stats (cont) */ 220 uint64_t badconn, collconn, badsent, collsent; /* rate stats (cont) */ 221 uint64_t badid, notresp; /* bad response counters */ 222 uint64_t rcodes[NS_RCODE_LAST + 1]; /* rcode counters */ 223 double dmin = 999999999.; /* minimum delay */ 224 double dmax = 0.; /* maximum delay */ 225 double dsum = 0.; /* delay sum */ 226 double dsumsq = 0.; /* square delay sum */ 227 228 /* 229 * command line parameters 230 */ 231 232 int edns0; /* EDNS0 DO flag */ 233 int ipversion = 0; /* IP version */ 234 int rate; /* rate in connections per second */ 235 int noreport; /* disable auto reporting */ 236 int report; /* delay between two reports */ 237 uint32_t range; /* randomization range */ 238 uint32_t maxrandom; /* maximum random value */ 239 int basecnt; /* base count */ 240 char *base[2]; /* bases */ 241 int gotnumreq = -1; /* numreq[0] was set */ 242 int numreq[2]; /* number of exchanges */ 243 int period; /* test period */ 244 int gotlosttime = -1; /* losttime[0] was set */ 245 double losttime[2] = {.5, 1.}; /* delay for a timeout */ 246 int gotmaxloss = -1; /* max{p}loss[0] was set */ 247 int maxloss[2]; /* maximum number of losses */ 248 double maxploss[2] = {0., 0.}; /* maximum percentage */ 249 char *localname; /* local address or interface */ 250 int aggressiveness = 1; /* back to back connections */ 251 int seeded; /* is a seed provided */ 252 unsigned int seed; /* randomization seed */ 253 char *templatefile; /* template file name */ 254 int rndoffset = -1; /* template offset (random) */ 255 char *diags; /* diagnostic selectors */ 256 char *servername; /* server */ 257 int ixann; /* ixann NXDOMAIN */ 258 int udp; /* use UDP in place of TCP */ 259 int minport, maxport, curport; /* port range */ 260 261 /* 262 * global variables 263 */ 264 265 struct sockaddr_storage localaddr; /* local socket address */ 266 struct sockaddr_storage serveraddr; /* server socket address */ 267 in_port_t port = 53; /* server socket port */ 268 269 int epoll_ifd, epoll_ofd; /* epoll file descriptors */ 270 #ifndef EVENTS_CNT 271 #define EVENTS_CNT 16 272 #endif 273 struct epoll_event ievents[EVENTS_CNT]; /* polled input events */ 274 struct epoll_event oevents[EVENTS_CNT]; /* polled output events */ 275 int interrupted, fatal; /* to finish flags */ 276 277 uint8_t obuf[4098], ibuf[4098]; /* I/O buffers */ 278 char tbuf[4098]; /* template buffer */ 279 280 struct timespec boot; /* the date of boot */ 281 struct timespec last; /* the date of last connect */ 282 struct timespec due; /* the date of next connect */ 283 struct timespec dreport; /* the date of next reporting */ 284 struct timespec finished; /* the date of finish */ 285 286 /* 287 * template 288 */ 289 290 size_t length_query; 291 uint8_t template_query[4096]; 292 size_t random_query; 293 294 /* 295 * threads 296 */ 297 298 pthread_t master, connector, sender, receiver; 299 300 /* 301 * initialize data structures handling exchanges 302 */ 303 304 void 305 inits(void) 306 { 307 int idx; 308 309 ISC_INIT(xconn, xconnl); 310 ISC_INIT(xready, xreadyl); 311 ISC_INIT(xsent, xsentl); 312 ISC_INIT(xfree, xfreel); 313 314 if ((pthread_mutex_init(&mtxconn, NULL) != 0) || 315 (pthread_mutex_init(&mtxsent, NULL) != 0) || 316 (pthread_mutex_init(&mtxfree, NULL) != 0)) { 317 fprintf(stderr, "pthread_mutex_init failed\n"); 318 exit(1); 319 } 320 321 epoll_ifd = epoll_create(EVENTS_CNT); 322 if (epoll_ifd < 0) { 323 perror("epoll_create(input)"); 324 exit(1); 325 } 326 epoll_ofd = epoll_create(EVENTS_CNT); 327 if (epoll_ofd < 0) { 328 perror("epoll_create(output)"); 329 exit(1); 330 } 331 332 xlist = (struct exchange *) malloc(xlast * sizeof(struct exchange)); 333 if (xlist == NULL) { 334 perror("malloc(exchanges)"); 335 exit(1); 336 } 337 memset(xlist, 0, xlast * sizeof(struct exchange)); 338 339 for (idx = 0; idx < xlast; idx++) 340 xlist[idx].sock = xlist[idx].next = -1; 341 } 342 343 /* 344 * build a TCP DNS QUERY 345 */ 346 347 void 348 build_template_query(void) 349 { 350 uint8_t *p = template_query; 351 uint16_t v; 352 353 /* flags */ 354 p += NS_OFF_FLAGS; 355 v = NS_FLAG_RD; 356 *p++ = v >> 8; 357 *p++ = v & 0xff; 358 /* qdcount */ 359 v = 1; 360 *p++ = v >> 8; 361 *p++ = v & 0xff; 362 /* ancount */ 363 v = 0; 364 *p++ = v >> 8; 365 *p++ = v & 0xff; 366 /* nscount */ 367 v = 0; 368 *p++ = v >> 8; 369 *p++ = v & 0xff; 370 /* arcount */ 371 v = edns0; 372 *p++ = v >> 8; 373 *p++ = v & 0xff; 374 /* icann.link (or ixann.link) */ 375 *p++ = 5; 376 *p++ = 'i'; 377 if (ixann == 0) 378 *p++ = 'c'; 379 else 380 *p++ = 'x'; 381 *p++ = 'a'; 382 *p++ = 'n'; 383 *p++ = 'n'; 384 *p++ = 4; 385 *p++ = 'l'; 386 *p++ = 'i'; 387 *p++ = 'n'; 388 *p++ = 'k'; 389 *p++ = 0; 390 /* type A/AAAA */ 391 if (ipversion == 4) 392 v = NS_TYPE_A; 393 else 394 v = NS_TYPE_AAAA; 395 *p++ = v >> 8; 396 *p++ = v & 0xff; 397 /* class IN */ 398 v = NS_CLASS_IN; 399 *p++ = v >> 8; 400 *p++ = v & 0xff; 401 /* EDNS0 OPT with DO */ 402 if (edns0) { 403 /* root name */ 404 *p++ = 0; 405 /* type OPT */ 406 v = NS_TYPE_OPT; 407 *p++ = v >> 8; 408 *p++ = v & 0xff; 409 /* class UDP length */ 410 v = 4096; 411 *p++ = v >> 8; 412 *p++ = v & 0xff; 413 /* extended rcode 0 */ 414 *p++ = 0; 415 /* version 0 */ 416 *p++ = 0; 417 /* extended flags DO */ 418 v = NS_XFLAG_DO; 419 *p++ = v >> 8; 420 *p++ = v & 0xff; 421 /* rdlength */ 422 v = 0; 423 *p++ = v >> 8; 424 *p++ = v & 0xff; 425 } 426 /* length */ 427 length_query = p - template_query; 428 } 429 430 /* 431 * get a TCP DNS client QUERY template 432 * from the file given in the command line (-T<template-file>) 433 * and rnd offset (-O<random-offset>) 434 */ 435 436 void 437 get_template_query(void) 438 { 439 uint8_t *p = template_query; 440 int fd, cc, i, j; 441 442 fd = open(templatefile, O_RDONLY); 443 if (fd < 0) { 444 fprintf(stderr, "open(%s): %s\n", 445 templatefile, strerror(errno)); 446 exit(2); 447 } 448 cc = read(fd, tbuf, sizeof(tbuf)); 449 (void) close(fd); 450 if (cc < 0) { 451 fprintf(stderr, "read(%s): %s\n", 452 templatefile, strerror(errno)); 453 exit(1); 454 } 455 if (cc < NS_OFF_QUESTION + 6) { 456 fprintf(stderr,"file '%s' too small\n", templatefile); 457 exit(2); 458 } 459 if (cc > 4096) { 460 fprintf(stderr,"file '%s' too large\n", templatefile); 461 exit(2); 462 } 463 j = 0; 464 for (i = 0; i < cc; i++) { 465 if (isspace((int) tbuf[i])) 466 continue; 467 if (!isxdigit((int) tbuf[i])) { 468 fprintf(stderr, 469 "illegal char[%d]='%c' in file '%s'\n", 470 i, (int) tbuf[i], templatefile); 471 exit(2); 472 } 473 tbuf[j] = tbuf[i]; 474 j++; 475 } 476 cc = j; 477 if ((cc & 1) != 0) { 478 fprintf(stderr, 479 "odd number of hexadecimal digits in file '%s'\n", 480 templatefile); 481 exit(2); 482 } 483 length_query = cc >> 1; 484 for (i = 0; i < cc; i += 2) 485 (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]); 486 if (rndoffset >= 0) 487 random_query = (size_t) rndoffset; 488 if (random_query > length_query) { 489 fprintf(stderr, 490 "random (at %zu) outside the template (length %zu)?\n", 491 random_query, length_query); 492 exit(2); 493 } 494 } 495 496 #if 0 497 /* 498 * randomize the value of the given field: 499 * - offset of the field 500 * - random seed (used as it when suitable) 501 * - returns the random value which was used 502 */ 503 504 uint32_t 505 randomize(size_t offset, uint32_t r) 506 { 507 uint32_t v; 508 509 if (range == 0) 510 return 0; 511 if (range == UINT32_MAX) 512 return r; 513 if (maxrandom != 0) 514 while (r >= maxrandom) 515 r = (uint32_t) random(); 516 r %= range + 1; 517 v = r; 518 v += obuf[offset]; 519 obuf[offset] = v; 520 if (v < 256) 521 return r; 522 v >>= 8; 523 v += obuf[offset - 1]; 524 obuf[offset - 1] = v; 525 if (v < 256) 526 return r; 527 v >>= 8; 528 v += obuf[offset - 2]; 529 obuf[offset - 2] = v; 530 if (v < 256) 531 return r; 532 v >>= 8; 533 v += obuf[offset - 3]; 534 obuf[offset - 3] = v; 535 return r; 536 } 537 #endif 538 539 /* 540 * flush/timeout connect 541 */ 542 543 void 544 flushconnect(void) 545 { 546 struct exchange *x; 547 struct timespec now; 548 int idx = xconn; 549 int cnt = 10; 550 double waited; 551 552 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 553 perror("clock_gettime(flushconnect)"); 554 fatal = 1; 555 (void) pthread_kill(master, SIGTERM); 556 return; 557 } 558 559 while (--cnt >= 0) { 560 if (idx < 0) 561 return; 562 x = xlist + idx; 563 idx = x->next; 564 if (x->state != X_CONN) 565 abort(); 566 /* check for a timed-out connection */ 567 waited = now.tv_sec - x->ts0.tv_sec; 568 waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9; 569 if (waited < losttime[0]) 570 return; 571 /* garbage collect timed-out connections */ 572 if (pthread_mutex_lock(&mtxconn) != 0) { 573 fprintf(stderr, "pthread_mutex_lock(flushconnect)"); 574 fatal = 1; 575 (void) pthread_kill(master, SIGTERM); 576 return; 577 } 578 ISC_REMOVE(xconnl, x); 579 if (pthread_mutex_unlock(&mtxconn) != 0) { 580 fprintf(stderr, "pthread_mutex_unlock(flushconnect)"); 581 fatal = 1; 582 (void) pthread_kill(master, SIGTERM); 583 return; 584 } 585 (void) close(x->sock); 586 x->sock = -1; 587 collconn++; 588 if (pthread_mutex_lock(&mtxfree) != 0) { 589 fprintf(stderr, "pthread_mutex_lock(flushconnect)"); 590 fatal = 1; 591 (void) pthread_kill(master, SIGTERM); 592 return; 593 } 594 x->state = X_FREE; 595 ISC_INSERT(xfree, xfreel, x); 596 if (pthread_mutex_unlock(&mtxfree) != 0) { 597 fprintf(stderr, "pthread_mutex_unlock(flushconnect)"); 598 fatal = 1; 599 (void) pthread_kill(master, SIGTERM); 600 return; 601 } 602 } 603 } 604 605 /* 606 * poll connected 607 */ 608 609 void 610 pollconnect(int topoll) 611 { 612 struct exchange *x; 613 int evn, idx, err; 614 socklen_t len = sizeof(int); 615 616 for (evn = 0; evn < topoll; evn++) { 617 idx = oevents[evn].data.fd; 618 x = xlist + idx; 619 if (x->state != X_CONN) 620 continue; 621 if (oevents[evn].events == 0) 622 continue; 623 if (pthread_mutex_lock(&mtxconn) != 0) { 624 fprintf(stderr, "pthread_mutex_lock(pollconnect)"); 625 fatal = 1; 626 (void) pthread_kill(master, SIGTERM); 627 return; 628 } 629 ISC_REMOVE(xconnl, x); 630 if (pthread_mutex_unlock(&mtxconn) != 0) { 631 fprintf(stderr, "pthread_mutex_unlock(pollconnect)"); 632 fatal = 1; 633 (void) pthread_kill(master, SIGTERM); 634 return; 635 } 636 oevents[evn].events = 0; 637 if ((getsockopt(x->sock, SOL_SOCKET, SO_ERROR, 638 &err, &len) < 0) || 639 (err != 0)) { 640 (void) close(x->sock); 641 x->sock = -1; 642 badconn++; 643 if (pthread_mutex_lock(&mtxfree) != 0) { 644 fprintf(stderr, 645 "pthread_mutex_lock(pollconnect)"); 646 fatal = 1; 647 (void) pthread_kill(master, SIGTERM); 648 return; 649 } 650 x->state = X_FREE; 651 ISC_INSERT(xfree, xfreel, x); 652 if (pthread_mutex_unlock(&mtxfree) != 0) { 653 fprintf(stderr, 654 "pthread_mutex_unlock(pollconnect)"); 655 fatal = 1; 656 (void) pthread_kill(master, SIGTERM); 657 return; 658 } 659 continue; 660 } 661 x->state = X_READY; 662 ISC_INSERT(xready, xreadyl, x); 663 } 664 } 665 666 /* 667 * send the TCP DNS QUERY 668 */ 669 670 int 671 sendquery(struct exchange *x) 672 { 673 ssize_t ret; 674 size_t off; 675 676 if (udp) 677 off = 0; 678 else { 679 off = 2; 680 /* message length */ 681 obuf[0] = length_query >> 8; 682 obuf[1]= length_query & 0xff; 683 } 684 /* message from template */ 685 memcpy(obuf + off, template_query, length_query); 686 /* ID */ 687 memcpy(obuf + off + NS_OFF_ID, &x->id, 2); 688 #if 0 689 /* random */ 690 if (random_query > 0) 691 x->rnd = randomize(random_query + off, x->rnd); 692 #endif 693 /* timestamp */ 694 errno = 0; 695 ret = clock_gettime(CLOCK_REALTIME, &x->ts2); 696 if (ret < 0) { 697 perror("clock_gettime(send)"); 698 fatal = 1; 699 (void) pthread_kill(master, SIGTERM); 700 return -errno; 701 } 702 ret = send(x->sock, obuf, length_query + off, 0); 703 if ((size_t) ret == length_query + off) 704 return 0; 705 return -errno; 706 } 707 708 /* 709 * poll ready and send 710 */ 711 712 void 713 pollsend(void) 714 { 715 struct exchange *x; 716 int idx = xready; 717 struct epoll_event ev; 718 719 memset(&ev, 0, sizeof(ev)); 720 ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT; 721 for (;;) { 722 if (idx < 0) 723 return; 724 x = xlist + idx; 725 ev.data.fd = idx; 726 idx = x->next; 727 ISC_REMOVE(xreadyl, x); 728 if (sendquery(x) < 0) { 729 (void) close(x->sock); 730 x->sock = -1; 731 badsent++; 732 if (pthread_mutex_lock(&mtxfree) != 0) { 733 fprintf(stderr, 734 "pthread_mutex_lock(pollsend)"); 735 fatal = 1; 736 (void) pthread_kill(master, SIGTERM); 737 return; 738 } 739 x->state = X_FREE; 740 ISC_INSERT(xfree, xfreel, x); 741 if (pthread_mutex_unlock(&mtxfree) != 0) { 742 fprintf(stderr, 743 "pthread_mutex_unlock(pollsend)"); 744 fatal = 1; 745 (void) pthread_kill(master, SIGTERM); 746 return; 747 } 748 continue; 749 } 750 xscount++; 751 if (pthread_mutex_lock(&mtxsent) != 0) { 752 fprintf(stderr, "pthread_mutex_lock(pollsend)"); 753 fatal = 1; 754 (void) pthread_kill(master, SIGTERM); 755 return; 756 } 757 x->state = X_SENT; 758 ISC_INSERT(xsent, xsentl, x); 759 if (pthread_mutex_unlock(&mtxsent) != 0) { 760 fprintf(stderr, "pthread_mutex_unlock(pollsend)"); 761 fatal = 1; 762 (void) pthread_kill(master, SIGTERM); 763 return; 764 } 765 if (epoll_ctl(epoll_ifd, EPOLL_CTL_ADD, x->sock, &ev) < 0) { 766 perror("epoll_ctl(add input)"); 767 fatal = 1; 768 (void) pthread_kill(master, SIGTERM); 769 return; 770 } 771 } 772 } 773 774 /* 775 * receive a TCP DNS RESPONSE 776 */ 777 778 void 779 receiveresp(struct exchange *x) 780 { 781 struct timespec now; 782 ssize_t cc; 783 size_t off; 784 uint16_t v; 785 double delta; 786 787 cc = recv(x->sock, ibuf, sizeof(ibuf), 0); 788 if (cc < 0) { 789 if ((errno == EAGAIN) || 790 (errno == EWOULDBLOCK) || 791 (errno == EINTR) || 792 (errno == ECONNRESET)) { 793 recverr++; 794 return; 795 } 796 perror("recv"); 797 fatal = 1; 798 (void) pthread_kill(master, SIGTERM); 799 return; 800 } 801 if (udp) 802 off = 0; 803 else 804 off = 2; 805 /* enforce a reasonable length */ 806 if ((size_t) cc < length_query + off) { 807 tooshort++; 808 return; 809 } 810 /* must match the ID */ 811 if (memcmp(ibuf + off + NS_OFF_ID, &x->id, 2) != 0) { 812 badid++; 813 return; 814 } 815 /* must be a response */ 816 memcpy(&v, ibuf + off + NS_OFF_FLAGS, 2); 817 v = ntohs(v); 818 if ((v & NS_FLAG_QR) == 0) { 819 notresp++; 820 return; 821 } 822 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 823 perror("clock_gettime(receive)"); 824 fatal = 1; 825 (void) pthread_kill(master, SIGTERM); 826 return; 827 } 828 /* got it: update stats */ 829 xrcount++; 830 x->ts3 = now; 831 delta = x->ts3.tv_sec - x->ts2.tv_sec; 832 delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9; 833 if (delta < dmin) 834 dmin = delta; 835 if (delta > dmax) 836 dmax = delta; 837 dsum += delta; 838 dsumsq += delta * delta; 839 v &= NS_RCODE_MASK; 840 if (v >= NS_RCODE_LAST) 841 v = NS_RCODE_LAST; 842 rcodes[v] += 1; 843 } 844 845 /* 846 * flush/timeout receive 847 */ 848 849 void 850 flushrecv(void) 851 { 852 struct exchange *x; 853 struct timespec now; 854 int idx = xsent; 855 int cnt = 5; 856 double waited; 857 858 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 859 perror("clock_gettime(receive)"); 860 fatal = 1; 861 (void) pthread_kill(master, SIGTERM); 862 return; 863 } 864 865 while (--cnt >= 0) { 866 if (idx < 0) 867 return; 868 x = xlist + idx; 869 idx = x->next; 870 if (x->state != X_SENT) 871 abort(); 872 /* check for a timed-out exchange */ 873 waited = now.tv_sec - x->ts2.tv_sec; 874 waited += (now.tv_nsec - x->ts2.tv_nsec) / 1e9; 875 if (waited < losttime[1]) 876 return; 877 /* garbage collect timed-out exchange */ 878 if (pthread_mutex_lock(&mtxsent) != 0) { 879 fprintf(stderr, "pthread_mutex_lock(flushrecv)"); 880 fatal = 1; 881 (void) pthread_kill(master, SIGTERM); 882 return; 883 } 884 ISC_REMOVE(xsentl, x); 885 if (pthread_mutex_unlock(&mtxsent) != 0) { 886 fprintf(stderr, "pthread_mutex_unlock(flushrecv)"); 887 fatal = 1; 888 (void) pthread_kill(master, SIGTERM); 889 return; 890 } 891 (void) close(x->sock); 892 x->sock = -1; 893 collsent++; 894 if (pthread_mutex_lock(&mtxfree) != 0) { 895 fprintf(stderr, "pthread_mutex_lock(flushrecv)"); 896 fatal = 1; 897 (void) pthread_kill(master, SIGTERM); 898 return; 899 } 900 x->state = X_FREE; 901 ISC_INSERT(xfree, xfreel, x); 902 if (pthread_mutex_unlock(&mtxfree) != 0) { 903 fprintf(stderr, "pthread_mutex_unlock(flushrecv)"); 904 fatal = 1; 905 (void) pthread_kill(master, SIGTERM); 906 return; 907 } 908 } 909 } 910 911 /* 912 * poll receive 913 */ 914 915 void 916 pollrecv(int topoll) 917 { 918 struct exchange *x; 919 int evn, idx; 920 921 for (evn = 0; evn < topoll; evn++) { 922 idx = ievents[evn].data.fd; 923 x = xlist + idx; 924 if (x->state != X_SENT) 925 continue; 926 if (ievents[evn].events == 0) 927 continue; 928 if (pthread_mutex_lock(&mtxsent) != 0) { 929 fprintf(stderr, "pthread_mutex_lock(pollrecv)"); 930 fatal = 1; 931 (void) pthread_kill(master, SIGTERM); 932 return; 933 } 934 ISC_REMOVE(xsentl, x); 935 if (pthread_mutex_unlock(&mtxsent) != 0) { 936 fprintf(stderr, "pthread_mutex_unlock(pollrecv)"); 937 fatal = 1; 938 (void) pthread_kill(master, SIGTERM); 939 return; 940 } 941 receiveresp(x); 942 ievents[evn].events = 0; 943 (void) close(x->sock); 944 x->sock = -1; 945 if (pthread_mutex_lock(&mtxfree) != 0) { 946 fprintf(stderr, "pthread_mutex_lock(pollrecv)"); 947 fatal = 1; 948 (void) pthread_kill(master, SIGTERM); 949 return; 950 } 951 x->state = X_FREE; 952 ISC_INSERT(xfree, xfreel, x); 953 if (pthread_mutex_unlock(&mtxfree) != 0) { 954 fprintf(stderr, "pthread_mutex_unlock(pollrecv)"); 955 fatal = 1; 956 (void) pthread_kill(master, SIGTERM); 957 return; 958 } 959 } 960 } 961 962 /* 963 * get the TCP DNS socket descriptor (IPv4) 964 */ 965 966 int 967 getsock4(void) 968 { 969 int sock; 970 int flags; 971 972 errno = 0; 973 if (udp) 974 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 975 else 976 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 977 if (sock < 0) 978 return -errno; 979 980 /* make the socket descriptor not blocking */ 981 flags = fcntl(sock, F_GETFL, 0); 982 if (flags == -1) { 983 (void) close(sock); 984 return -errno; 985 } 986 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { 987 (void) close(sock); 988 return -errno; 989 } 990 991 /* bind if wanted */ 992 if (localname != NULL) { 993 if (curport) { 994 struct sockaddr_in *l4; 995 996 l4 = (struct sockaddr_in *) &localaddr; 997 l4->sin_port = htons((uint16_t) curport); 998 curport++; 999 if (curport > maxport) 1000 curport = minport; 1001 } 1002 if (bind(sock, 1003 (struct sockaddr *) &localaddr, 1004 sizeof(struct sockaddr_in)) < 0) { 1005 (void) close(sock); 1006 return -errno; 1007 } 1008 } 1009 1010 /* connect */ 1011 if (connect(sock, 1012 (struct sockaddr *) &serveraddr, 1013 sizeof(struct sockaddr_in)) < 0) { 1014 if (errno != EINPROGRESS) { 1015 (void) close(sock); 1016 return -errno; 1017 } 1018 } 1019 return sock; 1020 } 1021 1022 /* 1023 * connect the TCP DNS QUERY (IPv4) 1024 */ 1025 1026 int 1027 connect4(void) 1028 { 1029 struct exchange *x; 1030 int ret; 1031 int idx; 1032 struct epoll_event ev; 1033 1034 ret = clock_gettime(CLOCK_REALTIME, &last); 1035 if (ret < 0) { 1036 perror("clock_gettime(connect)"); 1037 fatal = 1; 1038 (void) pthread_kill(master, SIGTERM); 1039 return -errno; 1040 } 1041 1042 if (xfree >= 0) { 1043 idx = xfree; 1044 x = xlist + idx; 1045 ret = pthread_mutex_lock(&mtxfree); 1046 if (ret != 0) { 1047 fprintf(stderr, "pthread_mutex_lock(connect4)"); 1048 fatal = 1; 1049 (void) pthread_kill(master, SIGTERM); 1050 return -ret; 1051 } 1052 ISC_REMOVE(xfreel, x); 1053 ret = pthread_mutex_unlock(&mtxfree); 1054 if (ret != 0) { 1055 fprintf(stderr, "pthread_mutex_unlock(connect4)"); 1056 fatal = 1; 1057 (void) pthread_kill(master, SIGTERM); 1058 return -ret; 1059 } 1060 } else if (xused < xlast) { 1061 idx = xused; 1062 x = xlist + idx; 1063 xused++; 1064 } else 1065 return -ENOMEM; 1066 1067 if ((x->state != X_FREE) || (x->sock != -1)) 1068 abort(); 1069 1070 memset(x, 0, sizeof(*x)); 1071 memset(&ev, 0, sizeof(ev)); 1072 x->next = -1; 1073 x->prev = NULL; 1074 x->ts0 = last; 1075 x->sock = getsock4(); 1076 if (x->sock < 0) { 1077 int result = x->sock; 1078 1079 x->sock = -1; 1080 ret = pthread_mutex_lock(&mtxfree); 1081 if (ret != 0) { 1082 fprintf(stderr, "pthread_mutex_lock(connect4)"); 1083 fatal = 1; 1084 (void) pthread_kill(master, SIGTERM); 1085 return -ret; 1086 } 1087 ISC_INSERT(xfree, xfreel, x); 1088 ret = pthread_mutex_unlock(&mtxfree); 1089 if (ret != 0) { 1090 fprintf(stderr, "pthread_mutex_unlock(connect4)"); 1091 fatal = 1; 1092 (void) pthread_kill(master, SIGTERM); 1093 return -ret; 1094 } 1095 return result; 1096 } 1097 ret = pthread_mutex_lock(&mtxconn); 1098 if (ret != 0) { 1099 fprintf(stderr, "pthread_mutex_lock(connect4)"); 1100 fatal = 1; 1101 (void) pthread_kill(master, SIGTERM); 1102 return -ret; 1103 } 1104 x->state = X_CONN; 1105 ISC_INSERT(xconn, xconnl, x); 1106 ret = pthread_mutex_unlock(&mtxconn); 1107 if (ret != 0) { 1108 fprintf(stderr, "pthread_mutex_unlock(connect4)"); 1109 fatal = 1; 1110 (void) pthread_kill(master, SIGTERM); 1111 return -ret; 1112 } 1113 ev.events = EPOLLOUT | EPOLLET | EPOLLONESHOT; 1114 ev.data.fd = idx; 1115 if (epoll_ctl(epoll_ofd, EPOLL_CTL_ADD, x->sock, &ev) < 0) { 1116 perror("epoll_ctl(add output)"); 1117 fatal = 1; 1118 (void) pthread_kill(master, SIGTERM); 1119 return -errno; 1120 } 1121 x->order = xccount++; 1122 x->id = (uint16_t) random(); 1123 #if 0 1124 if (random_query > 0) 1125 x->rnd = (uint32_t) random(); 1126 #endif 1127 return idx; 1128 } 1129 1130 /* 1131 * get the TCP DNS socket descriptor (IPv6) 1132 */ 1133 1134 int 1135 getsock6(void) 1136 { 1137 int sock; 1138 int flags; 1139 1140 errno = 0; 1141 if (udp) 1142 sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); 1143 else 1144 sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 1145 if (sock < 0) 1146 return -errno; 1147 1148 /* make the socket descriptor not blocking */ 1149 flags = fcntl(sock, F_GETFL, 0); 1150 if (flags == -1) { 1151 (void) close(sock); 1152 return -errno; 1153 } 1154 if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { 1155 (void) close(sock); 1156 return -errno; 1157 } 1158 1159 /* bind if wanted */ 1160 if (localname != NULL) { 1161 if (curport) { 1162 struct sockaddr_in6 *l6; 1163 1164 l6 = (struct sockaddr_in6 *) &localaddr; 1165 l6->sin6_port = htons((uint16_t) curport); 1166 curport++; 1167 if (curport > maxport) 1168 curport = minport; 1169 } 1170 if (bind(sock, 1171 (struct sockaddr *) &localaddr, 1172 sizeof(struct sockaddr_in6)) < 0) { 1173 (void) close(sock); 1174 return -errno; 1175 } 1176 } 1177 1178 /* connect */ 1179 if (connect(sock, 1180 (struct sockaddr *) &serveraddr, 1181 sizeof(struct sockaddr_in6)) < 0) { 1182 if (errno != EINPROGRESS) { 1183 (void) close(sock); 1184 return -errno; 1185 } 1186 } 1187 return sock; 1188 } 1189 1190 /* 1191 * connect the TCP DNS QUERY (IPv6) 1192 */ 1193 1194 int 1195 connect6(void) 1196 { 1197 struct exchange *x; 1198 int ret; 1199 int idx; 1200 struct epoll_event ev; 1201 1202 ret = clock_gettime(CLOCK_REALTIME, &last); 1203 if (ret < 0) { 1204 perror("clock_gettime(connect)"); 1205 fatal = 1; 1206 (void) pthread_kill(master, SIGTERM); 1207 return -errno; 1208 } 1209 1210 if (xfree >= 0) { 1211 idx = xfree; 1212 x = xlist + idx; 1213 ret = pthread_mutex_lock(&mtxfree); 1214 if (ret != 0) { 1215 fprintf(stderr, "pthread_mutex_lock(connect6)"); 1216 fatal = 1; 1217 (void) pthread_kill(master, SIGTERM); 1218 return -ret; 1219 } 1220 ISC_REMOVE(xfreel, x); 1221 ret = pthread_mutex_unlock(&mtxfree); 1222 if (ret != 0) { 1223 fprintf(stderr, "pthread_mutex_unlock(connect6)"); 1224 fatal = 1; 1225 (void) pthread_kill(master, SIGTERM); 1226 return -ret; 1227 } 1228 } else if (xused < xlast) { 1229 idx = xused; 1230 x = xlist + idx; 1231 xused++; 1232 } else 1233 return -ENOMEM; 1234 1235 memset(x, 0, sizeof(*x)); 1236 memset(&ev, 0, sizeof(ev)); 1237 x->next = -1; 1238 x->prev = NULL; 1239 x->ts0 = last; 1240 x->sock = getsock6(); 1241 if (x->sock < 0) { 1242 int result = x->sock; 1243 1244 x->sock = -1; 1245 ret = pthread_mutex_lock(&mtxfree); 1246 if (ret != 0) { 1247 fprintf(stderr, "pthread_mutex_lock(connect6)"); 1248 fatal = 1; 1249 (void) pthread_kill(master, SIGTERM); 1250 return -ret; 1251 } 1252 ISC_INSERT(xfree, xfreel, x); 1253 ret = pthread_mutex_unlock(&mtxfree); 1254 if (ret != 0) { 1255 fprintf(stderr, "pthread_mutex_unlock(connect6)"); 1256 fatal = 1; 1257 (void) pthread_kill(master, SIGTERM); 1258 return -ret; 1259 } 1260 return result; 1261 } 1262 ret = pthread_mutex_lock(&mtxconn); 1263 if (ret != 0) { 1264 fprintf(stderr, "pthread_mutex_lock(connect6)"); 1265 fatal = 1; 1266 (void) pthread_kill(master, SIGTERM); 1267 return -ret; 1268 } 1269 x->state = X_CONN; 1270 ISC_INSERT(xconn, xconnl, x); 1271 ret = pthread_mutex_unlock(&mtxconn); 1272 if (ret != 0) { 1273 fprintf(stderr, "pthread_mutex_unlock(connect6)"); 1274 fatal = 1; 1275 (void) pthread_kill(master, SIGTERM); 1276 return -ret; 1277 } 1278 ev.events = EPOLLOUT | EPOLLET | EPOLLONESHOT; 1279 ev.data.fd = idx; 1280 if (epoll_ctl(epoll_ofd, EPOLL_CTL_ADD, x->sock, &ev) < 0) { 1281 perror("epoll_ctl(add output)"); 1282 fatal = 1; 1283 (void) pthread_kill(master, SIGTERM); 1284 return -errno; 1285 } 1286 x->order = xccount++; 1287 x->id = (uint16_t) random(); 1288 #if 0 1289 if (random_query > 0) 1290 x->rnd = (uint32_t) random(); 1291 #endif 1292 return idx; 1293 } 1294 1295 /* 1296 * connector working routine 1297 */ 1298 1299 void * 1300 connecting(void *dummy) 1301 { 1302 struct timespec now, ts; 1303 int ret; 1304 int i; 1305 char name[16]; 1306 1307 dummy = dummy; 1308 1309 /* set conn-name */ 1310 memset(name, 0, sizeof(name)); 1311 ret = prctl(PR_GET_NAME, name, 0, 0, 0); 1312 if (ret < 0) 1313 perror("prctl(PR_GET_NAME)"); 1314 else { 1315 memmove(name + 5, name, 11); 1316 memcpy(name, "conn-", 5); 1317 ret = prctl(PR_SET_NAME, name, 0, 0, 0); 1318 if (ret < 0) 1319 perror("prctl(PR_SET_NAME"); 1320 } 1321 1322 for (;;) { 1323 if (fatal) 1324 break; 1325 1326 loops[1]++; 1327 1328 /* compute the delay for the next connection */ 1329 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 1330 perror("clock_gettime(connecting)"); 1331 fatal = 1; 1332 (void) pthread_kill(master, SIGTERM); 1333 break; 1334 } 1335 1336 due = last; 1337 if (rate == 1) 1338 due.tv_sec += 1; 1339 else 1340 due.tv_nsec += 1010000000 / rate; 1341 while (due.tv_nsec >= 1000000000) { 1342 due.tv_sec += 1; 1343 due.tv_nsec -= 1000000000; 1344 } 1345 ts = due; 1346 ts.tv_sec -= now.tv_sec; 1347 ts.tv_nsec -= now.tv_nsec; 1348 while (ts.tv_nsec < 0) { 1349 ts.tv_sec -= 1; 1350 ts.tv_nsec += 1000000000; 1351 } 1352 /* the connection was already due? */ 1353 if (ts.tv_sec < 0) { 1354 ts.tv_sec = ts.tv_nsec = 0; 1355 lateconn++; 1356 } else { 1357 /* wait until */ 1358 ret = clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); 1359 if (ret < 0) { 1360 if (errno == EINTR) 1361 continue; 1362 perror("clock_nanosleep"); 1363 fatal = 1; 1364 (void) pthread_kill(master, SIGTERM); 1365 break; 1366 } 1367 } 1368 1369 /* compute how many connections to open */ 1370 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 1371 perror("clock_gettime(connecting)"); 1372 fatal = 1; 1373 (void) pthread_kill(master, SIGTERM); 1374 break; 1375 } 1376 1377 if ((now.tv_sec > due.tv_sec) || 1378 ((now.tv_sec == due.tv_sec) && 1379 (now.tv_nsec >= due.tv_nsec))) { 1380 double toconnect; 1381 1382 toconnect = (now.tv_nsec - due.tv_nsec) / 1e9; 1383 toconnect += now.tv_sec - due.tv_sec; 1384 toconnect *= rate; 1385 toconnect++; 1386 if (toconnect > (double) aggressiveness) 1387 i = aggressiveness; 1388 else 1389 i = (int) toconnect; 1390 compconn += i; 1391 /* open connections */ 1392 while (i-- > 0) { 1393 if (ipversion == 4) 1394 ret = connect4(); 1395 else 1396 ret = connect6(); 1397 if (ret < 0) { 1398 if ((ret == -EAGAIN) || 1399 (ret == -EWOULDBLOCK) || 1400 (ret == -ENOBUFS) || 1401 (ret == -ENFILE) || 1402 (ret == -EMFILE) || 1403 (ret == -EADDRNOTAVAIL) || 1404 (ret == -ENOMEM)) 1405 locallimit++; 1406 fprintf(stderr, 1407 "connect: %s\n", 1408 strerror(-ret)); 1409 break; 1410 } 1411 } 1412 } else 1413 /* there was no connection to open */ 1414 shortwait[0]++; 1415 } 1416 1417 return NULL; 1418 } 1419 1420 /* 1421 * sender working routine 1422 */ 1423 1424 void * 1425 sending(void *dummy) 1426 { 1427 int ret; 1428 int nfds; 1429 char name[16]; 1430 1431 dummy = dummy; 1432 1433 /* set send-name */ 1434 memset(name, 0, sizeof(name)); 1435 ret = prctl(PR_GET_NAME, name, 0, 0, 0); 1436 if (ret < 0) 1437 perror("prctl(PR_GET_NAME)"); 1438 else { 1439 memmove(name + 5, name, 11); 1440 memcpy(name, "send-", 5); 1441 ret = prctl(PR_SET_NAME, name, 0, 0, 0); 1442 if (ret < 0) 1443 perror("prctl(PR_SET_NAME"); 1444 } 1445 1446 for (;;) { 1447 if (fatal) 1448 break; 1449 1450 loops[2]++; 1451 1452 /* epoll_wait() */ 1453 memset(oevents, 0, sizeof(oevents)); 1454 nfds = epoll_wait(epoll_ofd, oevents, EVENTS_CNT, 1); 1455 if (nfds < 0) { 1456 if (errno == EINTR) 1457 continue; 1458 perror("epoll_wait(output)"); 1459 fatal = 1; 1460 (void) pthread_kill(master, SIGTERM); 1461 break; 1462 } 1463 1464 /* connection(s) to finish */ 1465 if (nfds == 0) 1466 shortwait[1]++; 1467 else 1468 pollconnect(nfds); 1469 if (fatal) 1470 break; 1471 flushconnect(); 1472 if (fatal) 1473 break; 1474 1475 /* packet(s) to send */ 1476 pollsend(); 1477 if (fatal) 1478 break; 1479 } 1480 1481 return NULL; 1482 } 1483 1484 /* 1485 * receiver working routine 1486 */ 1487 1488 void * 1489 receiving(void *dummy) 1490 { 1491 int ret; 1492 int nfds; 1493 char name[16]; 1494 1495 dummy = dummy; 1496 1497 /* set recv-name */ 1498 memset(name, 0, sizeof(name)); 1499 ret = prctl(PR_GET_NAME, name, 0, 0, 0); 1500 if (ret < 0) 1501 perror("prctl(PR_GET_NAME)"); 1502 else { 1503 memmove(name + 5, name, 11); 1504 memcpy(name, "recv-", 5); 1505 ret = prctl(PR_SET_NAME, name, 0, 0, 0); 1506 if (ret < 0) 1507 perror("prctl(PR_SET_NAME"); 1508 } 1509 1510 for (;;) { 1511 if (fatal) 1512 break; 1513 1514 loops[3]++; 1515 1516 /* epoll_wait() */ 1517 memset(ievents, 0, sizeof(ievents)); 1518 nfds = epoll_wait(epoll_ifd, ievents, EVENTS_CNT, 1); 1519 if (nfds < 0) { 1520 if (errno == EINTR) 1521 continue; 1522 perror("epoll_wait(input)"); 1523 fatal = 1; 1524 (void) pthread_kill(master, SIGTERM); 1525 break; 1526 } 1527 1528 /* packet(s) to receive */ 1529 if (nfds == 0) 1530 shortwait[2]++; 1531 else 1532 pollrecv(nfds); 1533 if (fatal) 1534 break; 1535 flushrecv(); 1536 if (fatal) 1537 break; 1538 } 1539 1540 return NULL; 1541 } 1542 1543 /* 1544 * get the server socket address from the command line: 1545 * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals) 1546 */ 1547 1548 void 1549 getserveraddr(const int flags) 1550 { 1551 struct addrinfo hints, *res; 1552 int ret; 1553 1554 memset(&hints, 0, sizeof(hints)); 1555 if (ipversion == 4) 1556 hints.ai_family = AF_INET; 1557 else 1558 hints.ai_family = AF_INET6; 1559 if (udp) { 1560 hints.ai_socktype = SOCK_DGRAM; 1561 hints.ai_protocol = IPPROTO_UDP; 1562 } else { 1563 hints.ai_socktype = SOCK_STREAM; 1564 hints.ai_protocol = IPPROTO_TCP; 1565 } 1566 hints.ai_flags = AI_ADDRCONFIG | flags; 1567 1568 ret = getaddrinfo(servername, NULL, &hints, &res); 1569 if (ret != 0) { 1570 fprintf(stderr, "bad server=%s: %s\n", 1571 servername, gai_strerror(ret)); 1572 exit(2); 1573 } 1574 if (res->ai_next != NULL) { 1575 fprintf(stderr, "ambiguous server=%s\n", servername); 1576 exit(2); 1577 } 1578 memcpy(&serveraddr, res->ai_addr, res->ai_addrlen); 1579 freeaddrinfo(res); 1580 if (ipversion == 4) 1581 ((struct sockaddr_in *)&serveraddr)->sin_port = htons(port); 1582 else 1583 ((struct sockaddr_in6 *)&serveraddr)->sin6_port = htons(port); 1584 } 1585 1586 /* 1587 * get the local socket address from the command line 1588 */ 1589 1590 void 1591 getlocaladdr(void) 1592 { 1593 struct addrinfo hints, *res; 1594 int ret; 1595 1596 memset(&hints, 0, sizeof(hints)); 1597 if (ipversion == 4) 1598 hints.ai_family = AF_INET; 1599 else 1600 hints.ai_family = AF_INET6; 1601 if (udp) { 1602 hints.ai_socktype = SOCK_DGRAM; 1603 hints.ai_protocol = IPPROTO_UDP; 1604 } else { 1605 hints.ai_socktype = SOCK_STREAM; 1606 hints.ai_protocol = IPPROTO_TCP; 1607 } 1608 hints.ai_flags = AI_ADDRCONFIG; 1609 1610 ret = getaddrinfo(localname, NULL, &hints, &res); 1611 if (ret != 0) { 1612 fprintf(stderr, 1613 "bad -l<local-addr=%s>: %s\n", 1614 localname, 1615 gai_strerror(ret)); 1616 exit(2); 1617 } 1618 /* refuse multiple addresses */ 1619 if (res->ai_next != NULL) { 1620 fprintf(stderr, 1621 "ambiguous -l<local-addr=%s>\n", 1622 localname); 1623 exit(2); 1624 } 1625 memcpy(&localaddr, res->ai_addr, res->ai_addrlen); 1626 freeaddrinfo(res); 1627 } 1628 1629 /* 1630 * intermediate reporting 1631 * (note: an in-transit packet can be reported as lost) 1632 */ 1633 1634 void 1635 reporting(void) 1636 { 1637 dreport.tv_sec += report; 1638 1639 if (xccount != 0) { 1640 printf("connect: %llu, sent: %llu, received: %llu " 1641 "(embryonics: %lld, drops: %lld)", 1642 (unsigned long long) xccount, 1643 (unsigned long long) xscount, 1644 (unsigned long long) xrcount, 1645 (long long) (xccount - xscount), 1646 (long long) (xscount - xrcount)); 1647 if (xrcount != 0) { 1648 double avg; 1649 1650 avg = dsum / xrcount; 1651 printf(" average: %.3f ms", avg * 1e3); 1652 } 1653 } 1654 printf("\n"); 1655 } 1656 1657 /* 1658 * SIGCHLD handler 1659 */ 1660 1661 void 1662 reapchild(int sig) 1663 { 1664 int status; 1665 1666 sig = sig; 1667 while (wait3(&status, WNOHANG, NULL) > 0) 1668 /* continue */; 1669 } 1670 1671 /* 1672 * SIGINT handler 1673 */ 1674 1675 void 1676 interrupt(int sig) 1677 { 1678 sig = sig; 1679 interrupted = 1; 1680 } 1681 1682 /* 1683 * SIGTERM handler 1684 */ 1685 1686 void 1687 terminate(int sig) 1688 { 1689 sig = sig; 1690 fatal = 1; 1691 } 1692 1693 /* 1694 * '-v' handler 1695 */ 1696 1697 void 1698 version(void) 1699 { 1700 fprintf(stderr, "version 0.01\n"); 1701 } 1702 1703 /* 1704 * usage (from the wiki) 1705 */ 1706 1707 void 1708 usage(void) 1709 { 1710 fprintf(stderr, "%s", 1711 "perftcpdns [-huvX0] [-4|-6] [-r<rate>] [-t<report>] [-p<test-period>]\n" 1712 " [-n<num-request>]* [-d<lost-time>]* [-D<max-loss>]* [-T<template-file>]\n" 1713 " [-l<local-addr>] [-L<local-port>]* [-a<aggressiveness>] [-s<seed>]\n" 1714 " [-M<memory>] [-x<diagnostic-selector>] [-P<port>] server\n" 1715 "\f\n" 1716 "The server argument is the name/address of the DNS server to contact.\n" 1717 "\n" 1718 "Options:\n" 1719 "-0: Add EDNS0 option with DO flag.\n" 1720 "-4: TCP/IPv4 operation (default). This is incompatible with the -6 option.\n" 1721 "-6: TCP/IPv6 operation. This is incompatible with the -4 option.\n" 1722 "-a<aggressiveness>: When the target sending rate is not yet reached,\n" 1723 " control how many connections are initiated before the next pause.\n" 1724 "-d<lost-time>: Specify the time after which a connection or a query is\n" 1725 " treated as having been lost. The value is given in seconds and\n" 1726 " may contain a fractional component. The default is 1 second.\n" 1727 "-h: Print this help.\n" 1728 "-l<local-addr>: Specify the local hostname/address to use when\n" 1729 " communicating with the server.\n" 1730 "-L<local-port>: Specify the (minimal and maximal) local port number\n" 1731 "-M<memory>: Size of the tables (default 60000)\n" 1732 "-P<port>: Specify an alternate (i.e., not 53) port\n" 1733 "-r<rate>: Initiate <rate> TCP DNS connections per second. A periodic\n" 1734 " report is generated showing the number of exchanges which were not\n" 1735 " completed, as well as the average response latency. The program\n" 1736 " continues until interrupted, at which point a final report is\n" 1737 " generated.\n" 1738 "-s<seed>: Specify the seed for randomization, making it repeatable.\n" 1739 "-t<report>: Delay in seconds between two periodic reports.\n" 1740 "-T<template-file>: The name of a file containing the template to use\n" 1741 " as a stream of hexadecimal digits.\n" 1742 "-u: Use UDP in place of TCP.\n" 1743 "-v: Report the version number of this program.\n" 1744 "-X: change default template to get NXDOMAIN responses.\n" 1745 "-x<diagnostic-selector>: Include extended diagnostics in the output.\n" 1746 " <diagnostic-selector> is a string of single-keywords specifying\n" 1747 " the operations for which verbose output is desired. The selector\n" 1748 " keyletters are:\n" 1749 " * 'a': print the decoded command line arguments\n" 1750 " * 'e': print the exit reason\n" 1751 " * 'i': print rate processing details\n" 1752 " * 'T': when finished, print templates\n" 1753 "\n" 1754 "Stopping conditions:\n" 1755 "-D<max-loss>: Abort the test if more than <max-loss> connections or\n" 1756 " queries have been lost. If <max-loss> includes the suffix '%', it\n" 1757 " specifies a maximum percentage of losses before stopping.\n" 1758 " In this case, testing of the threshold begins after 10\n" 1759 " connections/responses have been expected to be accepted/received.\n" 1760 "-n<num-request>: Initiate <num-request> transactions. No report is\n" 1761 " generated until all transactions have been initiated/waited-for,\n" 1762 " after which a report is generated and the program terminates.\n" 1763 "-p<test-period>: Send requests for the given test period, which is\n" 1764 " specified in the same manner as -d. This can be used as an\n" 1765 " alternative to -n, or both options can be given, in which case the\n" 1766 " testing is completed when either limit is reached.\n" 1767 "\n" 1768 "Errors:\n" 1769 "- locallimit: reached to local system limits when sending a message.\n" 1770 "- badconn: connection failed (from getsockopt(SO_ERROR))\n" 1771 "- collconn: connect() timed out\n" 1772 "- badsent: send() failed\n" 1773 "- callsent: timed out waiting from a response\n" 1774 "- recverr: recv() system call failed\n" 1775 "- tooshort: received a too short message\n" 1776 "- badid: the id mismatches between the query and the response\n" 1777 "- notresp: doesn't receive a response\n" 1778 "Rate stats:\n" 1779 "- loops: number of thread loop iterations\n" 1780 "- shortwait: no direct activity in a thread iteration\n" 1781 "- compconn: computed number of connect() calls\n" 1782 "- lateconn: connect() already dued when computing delay to the next one\n" 1783 "\n" 1784 "Exit status:\n" 1785 "The exit status is:\n" 1786 "0 on complete success.\n" 1787 "1 for a general error.\n" 1788 "2 if an error is found in the command line arguments.\n" 1789 "3 if there are no general failures in operation, but one or more\n" 1790 " exchanges are not successfully completed.\n"); 1791 } 1792 1793 /* 1794 * main function / entry point 1795 */ 1796 1797 int 1798 main(const int argc, char * const argv[]) 1799 { 1800 int opt, flags = 0, ret, i; 1801 long long r; 1802 char *pc; 1803 double d; 1804 extern char *optarg; 1805 extern int optind; 1806 1807 #define OPTIONS "hv46u0XM:r:t:R:b:n:p:d:D:l:L:a:s:T:O:x:P:" 1808 1809 /* decode options */ 1810 while ((opt = getopt(argc, argv, OPTIONS)) != -1) 1811 switch (opt) { 1812 case 'h': 1813 usage(); 1814 exit(0); 1815 1816 case 'u': 1817 udp = 1; 1818 break; 1819 1820 case 'v': 1821 version(); 1822 exit(0); 1823 1824 case '0': 1825 edns0 = 1; 1826 break; 1827 1828 case '4': 1829 if (ipversion == 6) { 1830 fprintf(stderr, "IP version already set to 6\n"); 1831 usage(); 1832 exit(2); 1833 } 1834 ipversion = 4; 1835 break; 1836 1837 case '6': 1838 if (ipversion == 4) { 1839 fprintf(stderr, "IP version already set to 4\n"); 1840 usage(); 1841 exit(2); 1842 } 1843 ipversion = 6; 1844 break; 1845 1846 case 'X': 1847 ixann = 1; 1848 break; 1849 1850 case 'M': 1851 xlast = atoi(optarg); 1852 if (xlast <= 1000) { 1853 fprintf(stderr, "memory must be greater than 1000\n"); 1854 usage(); 1855 exit(2); 1856 } 1857 break; 1858 1859 case 'r': 1860 rate = atoi(optarg); 1861 if (rate <= 0) { 1862 fprintf(stderr, "rate must be a positive integer\n"); 1863 usage(); 1864 exit(2); 1865 } 1866 break; 1867 1868 case 't': 1869 report = atoi(optarg); 1870 if (report <= 0) { 1871 fprintf(stderr, "report must be a positive integer\n"); 1872 usage(); 1873 exit(2); 1874 } 1875 break; 1876 1877 case 'R': 1878 r = atoll(optarg); 1879 if (r < 0) { 1880 fprintf(stderr, 1881 "range must not be a negative integer\n"); 1882 usage(); 1883 exit(2); 1884 } 1885 range = (uint32_t) r; 1886 if ((range != 0) && (range != UINT32_MAX)) { 1887 uint32_t s = range + 1; 1888 uint64_t b = UINT32_MAX + 1, m; 1889 1890 m = (b / s) * s; 1891 if (m == b) 1892 maxrandom = 0; 1893 else 1894 maxrandom = (uint32_t) m; 1895 } 1896 break; 1897 1898 case 'b': 1899 if (basecnt > 1) { 1900 fprintf(stderr, "too many bases\n"); 1901 usage(); 1902 exit(2); 1903 } 1904 base[basecnt] = optarg; 1905 /* decodebase(); */ 1906 basecnt++; 1907 break; 1908 1909 case 'n': 1910 noreport = 1; 1911 gotnumreq++; 1912 if (gotnumreq > 1) { 1913 fprintf(stderr, "too many num-request's\n"); 1914 usage(); 1915 exit(2); 1916 } 1917 numreq[gotnumreq] = atoi(optarg); 1918 if ((numreq[gotnumreq] < 0) || 1919 ((numreq[gotnumreq] == 0) && (gotnumreq == 1))) { 1920 fprintf(stderr, 1921 "num-request must be a positive integer\n"); 1922 usage(); 1923 exit(2); 1924 } 1925 break; 1926 1927 case 'p': 1928 noreport = 1; 1929 period = atoi(optarg); 1930 if (period <= 0) { 1931 fprintf(stderr, 1932 "test-period must be a positive integer\n"); 1933 usage(); 1934 exit(2); 1935 } 1936 break; 1937 1938 case 'd': 1939 gotlosttime++; 1940 if (gotlosttime > 1) { 1941 fprintf(stderr, "too many lost-time's\n"); 1942 usage(); 1943 exit(2); 1944 } 1945 d = atof(optarg); 1946 if ((d < 0.) || ((d == 0.) && (gotlosttime == 1))) { 1947 fprintf(stderr, 1948 "lost-time must be a positive number\n"); 1949 usage(); 1950 exit(2); 1951 } 1952 if (d > 0.) 1953 losttime[gotlosttime] = d; 1954 break; 1955 1956 case 'D': 1957 noreport = 1; 1958 gotmaxloss++; 1959 if (gotmaxloss > 1) { 1960 fprintf(stderr, "too many max-loss's\n"); 1961 usage(); 1962 exit(2); 1963 } 1964 pc = strchr(optarg, '%'); 1965 if (pc != NULL) { 1966 *pc = '\0'; 1967 maxploss[gotmaxloss] = atof(optarg); 1968 if ((maxploss[gotmaxloss] < 0) || 1969 (maxploss[gotmaxloss] >= 100)) { 1970 fprintf(stderr, 1971 "invalid max-loss percentage\n"); 1972 usage(); 1973 exit(2); 1974 } 1975 } else { 1976 maxloss[gotmaxloss] = atoi(optarg); 1977 if ((maxloss[gotmaxloss] < 0) || 1978 ((maxloss[gotmaxloss] == 0) && 1979 (gotmaxloss == 1))) { 1980 fprintf(stderr, 1981 "max-loss must be a " 1982 "positive integer\n"); 1983 usage(); 1984 exit(2); 1985 } 1986 } 1987 break; 1988 1989 case 'l': 1990 localname = optarg; 1991 break; 1992 1993 case 'L': 1994 i = atoi(optarg); 1995 if ((i <= 0) || (i >65535)) { 1996 fprintf(stderr, 1997 "local-port must be a small positive integer\n"); 1998 usage(); 1999 exit(2); 2000 } 2001 if (maxport != 0) { 2002 fprintf(stderr, "too many local-port's\n"); 2003 usage(); 2004 exit(2); 2005 } 2006 if (curport == 0) 2007 minport = curport = i; 2008 else 2009 maxport = i; 2010 break; 2011 2012 case 'a': 2013 aggressiveness = atoi(optarg); 2014 if (aggressiveness <= 0) { 2015 fprintf(stderr, 2016 "aggressiveness must be a positive integer\n"); 2017 usage(); 2018 exit(2); 2019 } 2020 break; 2021 2022 case 's': 2023 seeded = 1; 2024 seed = (unsigned int) atol(optarg); 2025 break; 2026 2027 case 'T': 2028 if (templatefile != NULL) { 2029 fprintf(stderr, "template-file is already set\n"); 2030 usage(); 2031 exit(2); 2032 } 2033 templatefile = optarg; 2034 break; 2035 2036 case 'O': 2037 rndoffset = atoi(optarg); 2038 if (rndoffset < 14) { 2039 fprintf(stderr, 2040 "random-offset must be greater than 14\n"); 2041 usage(); 2042 exit(2); 2043 } 2044 break; 2045 2046 case 'x': 2047 diags = optarg; 2048 break; 2049 2050 case 'P': 2051 i = atoi(optarg); 2052 if ((i <= 0) || (i > 65535)) { 2053 fprintf(stderr, 2054 "port must be a positive short integer\n"); 2055 usage(); 2056 exit(2); 2057 } 2058 port = (in_port_t) i; 2059 break; 2060 2061 default: 2062 usage(); 2063 exit(2); 2064 } 2065 2066 /* adjust some global variables */ 2067 if (ipversion == 0) 2068 ipversion = 4; 2069 if (rate == 0) 2070 rate = 100; 2071 if (xlast == 0) 2072 xlast = 60000; 2073 if (noreport == 0) 2074 report = 1; 2075 if ((curport != 0) && (maxport == 0)) 2076 maxport = 65535; 2077 2078 /* when required, print the internal view of the command line */ 2079 if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { 2080 if (udp) 2081 printf("UDP "); 2082 printf("IPv%d", ipversion); 2083 printf(" rate=%d", rate); 2084 if (edns0 != 0) 2085 printf(" EDNS0"); 2086 if (report != 0) 2087 printf(" report=%d", report); 2088 if (range != 0) { 2089 if (strchr(diags, 'r') != NULL) 2090 printf(" range=0..%d [0x%x]", 2091 range, 2092 (unsigned int) maxrandom); 2093 else 2094 printf(" range=0..%d", range); 2095 } 2096 if (basecnt != 0) 2097 for (i = 0; i < basecnt; i++) 2098 printf(" base[%d]='%s'", i, base[i]); 2099 if (gotnumreq >= 0) { 2100 if ((numreq[0] == 0) && (numreq[1] != 0)) 2101 printf(" num-request=*,%d", numreq[1]); 2102 if ((numreq[0] != 0) && (numreq[1] == 0)) 2103 printf(" num-request=%d,*", numreq[0]); 2104 if ((numreq[0] != 0) && (numreq[1] != 0)) 2105 printf(" num-request=%d,%d", 2106 numreq[0], numreq[1]); 2107 } 2108 if (period != 0) 2109 printf(" test-period=%d", period); 2110 printf(" lost-time=%g,%g", losttime[0], losttime[1]); 2111 if (gotmaxloss == 0) { 2112 if (maxloss[0] != 0) 2113 printf(" max-loss=%d,*", maxloss[0]); 2114 if (maxploss[0] != 0.) 2115 printf(" max-loss=%2.2f%%,*", maxploss[0]); 2116 } else if (gotmaxloss == 1) { 2117 if (maxloss[0] != 0) 2118 printf(" max-loss=%d,", maxloss[0]); 2119 else if (maxploss[0] != 0.) 2120 printf(" max-loss=%2.2f%%,", maxploss[0]); 2121 else 2122 printf(" max-loss=*,"); 2123 if (maxloss[1] != 0) 2124 printf("%d", maxloss[1]); 2125 else if (maxploss[1] != 0.) 2126 printf("%2.2f%%", maxploss[1]); 2127 else 2128 printf("*"); 2129 } 2130 printf(" aggressiveness=%d", aggressiveness); 2131 if (seeded) 2132 printf(" seed=%u", seed); 2133 if (templatefile != NULL) 2134 printf(" template-file='%s'", templatefile); 2135 else if (ixann != 0) 2136 printf(" Xflag"); 2137 if (rndoffset >= 0) 2138 printf(" rnd-offset=%d", rndoffset); 2139 printf(" diagnotic-selectors='%s'", diags); 2140 printf("\n"); 2141 } 2142 2143 /* check local address options */ 2144 if ((localname == NULL) && (curport != 0)) { 2145 fprintf(stderr, 2146 "-l<local-addr> must be set to use -L<local-port>\n"); 2147 usage(); 2148 exit(2); 2149 } 2150 2151 /* check template file options */ 2152 if ((templatefile == NULL) && (rndoffset >= 0)) { 2153 fprintf(stderr, 2154 "-T<template-file> must be set to " 2155 "use -O<random-offset>\n"); 2156 usage(); 2157 exit(2); 2158 } 2159 2160 /* check various template file(s) and other condition(s) options */ 2161 if ((templatefile != NULL) && (range > 0) && (rndoffset < 0)) { 2162 fprintf(stderr, 2163 "-O<random-offset> must be set when " 2164 "-T<template-file> and -R<range> are used\n"); 2165 usage(); 2166 exit(2); 2167 } 2168 2169 /* get the server argument */ 2170 if (optind < argc - 1) { 2171 fprintf(stderr, "extra arguments?\n"); 2172 usage(); 2173 exit(2); 2174 } 2175 if (optind == argc - 1) 2176 servername = argv[optind]; 2177 2178 /* handle the local '-l' address/interface */ 2179 if (localname != NULL) { 2180 /* given */ 2181 getlocaladdr(); 2182 if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { 2183 printf("local-addr='%s'", localname); 2184 if (curport != 0) 2185 printf(" local-port='%d..%d'", 2186 minport, maxport); 2187 printf("\n"); 2188 } 2189 } 2190 2191 /* get the server socket address */ 2192 if (servername == NULL) { 2193 fprintf(stderr, "server is required\n"); 2194 usage(); 2195 exit(2); 2196 } 2197 getserveraddr(flags); 2198 2199 /* finish local/server socket address stuff and print it */ 2200 if ((diags != NULL) && (strchr(diags, 'a') != NULL)) 2201 printf("server='%s'\n", servername); 2202 if ((localname != NULL) && 2203 (diags != NULL) && (strchr(diags, 'a') != NULL)) { 2204 char addr[NI_MAXHOST]; 2205 2206 ret = getnameinfo((struct sockaddr *) &localaddr, 2207 sizeof(localaddr), 2208 addr, 2209 NI_MAXHOST, 2210 NULL, 2211 0, 2212 NI_NUMERICHOST); 2213 if (ret != 0) { 2214 fprintf(stderr, 2215 "can't get the local address: %s\n", 2216 gai_strerror(ret)); 2217 exit(1); 2218 } 2219 printf("local address='%s'\n", addr); 2220 } 2221 2222 /* initialize exchange structures */ 2223 inits(); 2224 2225 /* get the socket descriptor and template(s) */ 2226 if (templatefile == NULL) 2227 build_template_query(); 2228 else 2229 get_template_query(); 2230 2231 /* boot is done! */ 2232 if (clock_gettime(CLOCK_REALTIME, &boot) < 0) { 2233 perror("clock_gettime(boot)"); 2234 exit(1); 2235 } 2236 2237 /* compute the next intermediate reporting date */ 2238 if (report != 0) { 2239 dreport.tv_sec = boot.tv_sec + report; 2240 dreport.tv_nsec = boot.tv_nsec; 2241 } 2242 2243 /* seed the random generator */ 2244 if (seeded == 0) 2245 seed = (unsigned int) (boot.tv_sec + boot.tv_nsec); 2246 srandom(seed); 2247 2248 /* required only before the interrupted flag check */ 2249 (void) signal(SIGINT, interrupt); 2250 (void) signal(SIGTERM, terminate); 2251 2252 /* threads */ 2253 master = pthread_self(); 2254 ret = pthread_create(&connector, NULL, connecting, NULL); 2255 if (ret != 0) { 2256 fprintf(stderr, "pthread_create: %s\n", strerror(ret)); 2257 exit(1); 2258 } 2259 ret = pthread_create(&sender, NULL, sending, NULL); 2260 if (ret != 0) { 2261 fprintf(stderr, "pthread_create: %s\n", strerror(ret)); 2262 exit(1); 2263 } 2264 ret = pthread_create(&receiver, NULL, receiving, NULL); 2265 if (ret != 0) { 2266 fprintf(stderr, "pthread_create: %s\n", strerror(ret)); 2267 exit(1); 2268 } 2269 2270 /* main loop */ 2271 for (;;) { 2272 struct timespec now, ts; 2273 2274 /* immediate loop exit conditions */ 2275 if (interrupted) { 2276 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2277 printf("interrupted\n"); 2278 break; 2279 } 2280 if (fatal) { 2281 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2282 printf("got a fatal error\n"); 2283 break; 2284 } 2285 2286 loops[0]++; 2287 2288 /* get the date and use it */ 2289 if (clock_gettime(CLOCK_REALTIME, &now) < 0) { 2290 perror("clock_gettime(now)"); 2291 fatal = 1; 2292 continue; 2293 } 2294 if ((period != 0) && 2295 ((boot.tv_sec + period < now.tv_sec) || 2296 ((boot.tv_sec + period == now.tv_sec) && 2297 (boot.tv_nsec < now.tv_nsec)))) { 2298 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2299 printf("reached test-period\n"); 2300 break; 2301 } 2302 if ((report != 0) && 2303 ((dreport.tv_sec < now.tv_sec) || 2304 ((dreport.tv_sec == now.tv_sec) && 2305 (dreport.tv_nsec < now.tv_nsec)))) 2306 reporting(); 2307 2308 /* check receive loop exit conditions */ 2309 if ((numreq[0] != 0) && ((int) xccount >= numreq[0])) { 2310 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2311 printf("reached num-connection\n"); 2312 break; 2313 } 2314 if ((numreq[1] != 0) && ((int) xscount >= numreq[1])) { 2315 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2316 printf("reached num-query\n"); 2317 break; 2318 } 2319 if ((maxloss[0] != 0) && 2320 ((int) (xccount - xscount) > maxloss[0])) { 2321 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2322 printf("reached max-loss " 2323 "(connection/absolute)\n"); 2324 break; 2325 } 2326 if ((maxloss[1] != 0) && 2327 ((int) (xscount - xrcount) > maxloss[1])) { 2328 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2329 printf("reached max-loss " 2330 "(query/absolute)\n"); 2331 break; 2332 } 2333 if ((maxploss[0] != 0.) && 2334 (xccount > 10) && 2335 (((100. * (xccount - xscount)) / xccount) > maxploss[1])) { 2336 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2337 printf("reached max-loss " 2338 "(connection/percent)\n"); 2339 break; 2340 } 2341 if ((maxploss[1] != 0.) && 2342 (xscount > 10) && 2343 (((100. * (xscount - xrcount)) / xscount) > maxploss[1])) { 2344 if ((diags != NULL) && (strchr(diags, 'e') != NULL)) 2345 printf("reached max-loss " 2346 "(query/percent)\n"); 2347 break; 2348 } 2349 2350 /* waiting 1ms */ 2351 memset(&ts, 0, sizeof(ts)); 2352 ts.tv_nsec = 1000000; 2353 (void) clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); 2354 } 2355 2356 /* after main loop: finished */ 2357 if (clock_gettime(CLOCK_REALTIME, &finished) < 0) 2358 perror("clock_gettime(finished)"); 2359 2360 /* threads */ 2361 (void) pthread_cancel(connector); 2362 (void) pthread_cancel(sender); 2363 (void) pthread_cancel(receiver); 2364 2365 /* main statictics */ 2366 printf("connect: %llu, sent: %llu, received: %llu\n", 2367 (unsigned long long) xccount, 2368 (unsigned long long) xscount, 2369 (unsigned long long) xrcount); 2370 printf("embryonics: %lld (%.1f%%)\n", 2371 (long long) (xccount - xscount), 2372 (100. * (xccount - xscount)) / xccount); 2373 printf("drops: %lld (%.1f%%)\n", 2374 (long long) (xscount - xrcount), 2375 (100. * (xscount - xrcount)) / xscount); 2376 printf("total losses: %lld (%.1f%%)\n", 2377 (long long) (xccount - xrcount), 2378 (100. * (xccount - xrcount)) / xccount); 2379 printf("local limits: %llu, bad connects: %llu, " 2380 "connect timeouts: %llu\n", 2381 (unsigned long long) locallimit, 2382 (unsigned long long) badconn, 2383 (unsigned long long) collconn); 2384 printf("bad sends: %llu, bad recvs: %llu, recv timeouts: %llu\n", 2385 (unsigned long long) badsent, 2386 (unsigned long long) recverr, 2387 (unsigned long long) collsent); 2388 printf("too shorts: %llu, bad IDs: %llu, not responses: %llu\n", 2389 (unsigned long long) tooshort, 2390 (unsigned long long) badid, 2391 (unsigned long long) notresp); 2392 printf("rcode counters:\n noerror: %llu, formerr: %llu, " 2393 "servfail: %llu\n " 2394 "nxdomain: %llu, noimp: %llu, refused: %llu, others: %llu\n", 2395 (unsigned long long) rcodes[NS_RCODE_NOERROR], 2396 (unsigned long long) rcodes[NS_RCODE_FORMERR], 2397 (unsigned long long) rcodes[NS_RCODE_SERVFAIL], 2398 (unsigned long long) rcodes[NS_RCODE_NXDOMAIN], 2399 (unsigned long long) rcodes[NS_RCODE_NOIMP], 2400 (unsigned long long) rcodes[NS_RCODE_REFUSED], 2401 (unsigned long long) rcodes[NS_RCODE_LAST]); 2402 2403 /* print the rates */ 2404 if (finished.tv_sec != 0) { 2405 double dall, erate[3]; 2406 2407 dall = (finished.tv_nsec - boot.tv_nsec) / 1e9; 2408 dall += finished.tv_sec - boot.tv_sec; 2409 erate[0] = xccount / dall; 2410 erate[1] = xscount / dall; 2411 erate[2] = xrcount / dall; 2412 printf("rates: %.0f,%.0f,%.0f (target %d)\n", 2413 erate[0], erate[1], erate[2], rate); 2414 } 2415 2416 /* rate processing instrumentation */ 2417 if ((diags != NULL) && (strchr(diags, 'i') != NULL)) { 2418 printf("loops: %llu,%llu,%llu,%llu\n", 2419 (unsigned long long) loops[0], 2420 (unsigned long long) loops[1], 2421 (unsigned long long) loops[2], 2422 (unsigned long long) loops[3]); 2423 printf("shortwait: %llu,%llu,%llu\n", 2424 (unsigned long long) shortwait[0], 2425 (unsigned long long) shortwait[1], 2426 (unsigned long long) shortwait[2]); 2427 printf("compconn: %llu, lateconn: %llu\n", 2428 (unsigned long long) compconn, 2429 (unsigned long long) lateconn); 2430 printf("badconn: %llu, collconn: %llu, " 2431 "recverr: %llu, collsent: %llu\n", 2432 (unsigned long long) badconn, 2433 (unsigned long long) collconn, 2434 (unsigned long long) recverr, 2435 (unsigned long long) collsent); 2436 printf("memory: used(%d) / allocated(%d)\n", 2437 xused, xlast); 2438 } 2439 2440 /* round-time trip statistics */ 2441 if (xrcount != 0) { 2442 double avg, stddev; 2443 2444 avg = dsum / xrcount; 2445 stddev = sqrt(dsumsq / xrcount - avg * avg); 2446 printf("RTT: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n", 2447 dmin * 1e3, avg * 1e3, dmax * 1e3, stddev * 1e3); 2448 } 2449 printf("\n"); 2450 2451 /* template(s) */ 2452 if ((diags != NULL) && (strchr(diags, 'T') != NULL)) { 2453 size_t n; 2454 2455 printf("length = 0x%zx\n", length_query); 2456 if (random_query > 0) 2457 printf("random offset = %zu\n", random_query); 2458 printf("content:\n"); 2459 for (n = 0; n < length_query; n++) { 2460 printf("%s%02hhx", 2461 (n & 15) == 0 ? "" : " ", 2462 template_query[n]); 2463 if ((n & 15) == 15) 2464 printf("\n"); 2465 } 2466 if ((n & 15) != 15) 2467 printf("\n"); 2468 printf("\n"); 2469 } 2470 2471 /* compute the exit code (and exit) */ 2472 if (fatal) 2473 exit(1); 2474 else if ((xccount == xscount) && (xscount == xrcount)) 2475 exit(0); 2476 else 2477 exit(3); 2478 } 2479