1/* 2 * Copyright (C) 2000, 2001 Nominum, Inc. 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM 9 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 10 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 11 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, 12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING 13 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 14 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 15 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18/*** 19 *** DNS Query Performance Testing Tool (queryperf.c) 20 *** 21 *** Version $Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp $ 22 *** 23 *** Stephen Jacob <sj@nominum.com> 24 ***/ 25 26#define BIND_8_COMPAT /* Pull in <arpa/nameser_compat.h> */ 27 28#include <sys/time.h> 29#include <sys/types.h> 30#include <sys/socket.h> 31#include <stdio.h> 32#include <stdlib.h> 33#include <string.h> 34#include <limits.h> 35#include <time.h> 36#include <unistd.h> 37#include <netdb.h> 38#include <netinet/in.h> 39#include <arpa/nameser.h> 40#include <resolv.h> 41#include <math.h> 42#include <errno.h> 43 44#ifdef HAVE_CONFIG_H 45#include "config.h" 46#ifndef HAVE_GETADDRINFO 47#include "missing/addrinfo.h" 48#endif 49#endif 50 51/* 52 * Configuration defaults 53 */ 54 55#define DEF_MAX_QUERIES_OUTSTANDING 20 56#define DEF_QUERY_TIMEOUT 5 /* in seconds */ 57#define DEF_SERVER_TO_QUERY "127.0.0.1" 58#define DEF_SERVER_PORT "53" 59#define DEF_BUFFER_SIZE 32 /* in k */ 60 61#define DEF_RTTARRAY_SIZE 50000 62#define DEF_RTTARRAY_UNIT 100 /* in usec */ 63 64/* 65 * Other constants / definitions 66 */ 67 68#define COMMENT_CHAR ';' 69#define CONFIG_CHAR '#' 70#define MAX_PORT 65535 71#define MAX_INPUT_LEN 512 72#define MAX_DOMAIN_LEN 255 73#define MAX_BUFFER_LEN 8192 /* in bytes */ 74#define HARD_TIMEOUT_EXTRA 5 /* in seconds */ 75#define RESPONSE_BLOCKING_WAIT_TIME 0.1 /* in seconds */ 76#define EDNSLEN 11 77 78#define FALSE 0 79#define TRUE 1 80 81#define WHITESPACE " \t\n" 82 83enum directives_enum { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT }; 84#define DIRECTIVES { "server", "port", "maxqueries", "maxwait" } 85#define DIR_VALUES { V_SERVER, V_PORT, V_MAXQUERIES, V_MAXWAIT } 86 87#define QTYPE_STRINGS { \ 88 "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", \ 89 "MR", "NULL", "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", \ 90 "AAAA", "SRV", "NAPTR", "A6", "AXFR", "MAILB", "MAILA", "*", "ANY" \ 91} 92 93#define QTYPE_CODES { \ 94 1, 2, 3, 4, 5, 6, 7, 8, \ 95 9, 10, 11, 12, 13, 14, 15, 16, \ 96 28, 33, 35, 38, 252, 253, 254, 255, 255 \ 97} 98 99#define RCODE_STRINGS { \ 100 "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", \ 101 "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", \ 102 "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", \ 103 "rcode12", "rcode13", "rcode14", "rcode15" \ 104} 105 106/* 107 * Data type definitions 108 */ 109 110#define QUERY_STATUS_MAGIC 0x51535441U /* QSTA */ 111#define VALID_QUERY_STATUS(q) ((q) != NULL && \ 112 (q)->magic == QUERY_STATUS_MAGIC) 113 114struct query_status { 115 unsigned int magic; 116 int in_use; 117 unsigned short int id; 118 struct timeval sent_timestamp; 119 char *desc; 120}; 121 122/* 123 * Forward declarations. 124 */ 125int is_uint(char *test_int, unsigned int *result); 126 127/* 128 * Configuration options (global) 129 */ 130 131unsigned int max_queries_outstanding; /* init 0 */ 132unsigned int query_timeout = DEF_QUERY_TIMEOUT; 133int ignore_config_changes = FALSE; 134unsigned int socket_bufsize = DEF_BUFFER_SIZE; 135 136int family = AF_UNSPEC; 137int use_stdin = TRUE; 138char *datafile_name; /* init NULL */ 139 140char *server_to_query; /* init NULL */ 141char *server_port; /* init NULL */ 142struct addrinfo *server_ai; /* init NULL */ 143 144int run_only_once = FALSE; 145int use_timelimit = FALSE; 146unsigned int run_timelimit; /* init 0 */ 147unsigned int print_interval; /* init 0 */ 148 149unsigned int target_qps; /* init 0 */ 150 151int serverset = FALSE, portset = FALSE; 152int queriesset = FALSE, timeoutset = FALSE; 153int edns = FALSE, dnssec = FALSE; 154int countrcodes = FALSE; 155int rcodecounts[16] = {0}; 156 157int verbose = FALSE; 158int recurse = 1; 159 160/* 161 * Other global stuff 162 */ 163 164int setup_phase = TRUE; 165 166FILE *datafile_ptr; /* init NULL */ 167unsigned int runs_through_file; /* init 0 */ 168 169unsigned int num_queries_sent; /* init 0 */ 170unsigned int num_queries_sent_interval; 171unsigned int num_queries_outstanding; /* init 0 */ 172unsigned int num_queries_timed_out; /* init 0 */ 173unsigned int num_queries_possiblydelayed; /* init 0 */ 174unsigned int num_queries_timed_out_interval; 175unsigned int num_queries_possiblydelayed_interval; 176 177struct timeval time_of_program_start; 178struct timeval time_of_first_query; 179double time_of_first_query_sec; 180struct timeval time_of_first_query_interval; 181struct timeval time_of_end_of_run; 182struct timeval time_of_stop_sending; 183 184struct timeval time_of_queryset_start; 185double query_interval; 186struct timeval time_of_next_queryset; 187 188double rtt_max = -1; 189double rtt_max_interval = -1; 190double rtt_min = -1; 191double rtt_min_interval = -1; 192double rtt_total; 193double rtt_total_interval; 194int rttarray_size = DEF_RTTARRAY_SIZE; 195int rttarray_unit = DEF_RTTARRAY_UNIT; 196unsigned int *rttarray = NULL; 197unsigned int *rttarray_interval = NULL; 198unsigned int rtt_overflows; 199unsigned int rtt_overflows_interval; 200char *rtt_histogram_file = NULL; 201 202struct query_status *status; /* init NULL */ 203unsigned int query_status_allocated; /* init 0 */ 204 205int query_socket = -1; 206int socket4 = -1, socket6 = -1; 207 208static char *rcode_strings[] = RCODE_STRINGS; 209 210/* 211 * get_uint16: 212 * Get an unsigned short integer from a buffer (in network order) 213 */ 214static unsigned short 215get_uint16(unsigned char *buf) { 216 unsigned short ret; 217 218 ret = buf[0] * 256 + buf[1]; 219 220 return (ret); 221} 222 223/* 224 * show_startup_info: 225 * Show name/version 226 */ 227void 228show_startup_info(void) { 229 printf("\n" 230"DNS Query Performance Testing Tool\n" 231"Version: $Id: queryperf.c,v 1.12 2007/09/05 07:36:04 marka Exp $\n" 232"\n"); 233} 234 235/* 236 * show_usage: 237 * Print out usage/syntax information 238 */ 239void 240show_usage(void) { 241 fprintf(stderr, 242"\n" 243"Usage: queryperf [-d datafile] [-s server_addr] [-p port] [-q num_queries]\n" 244" [-b bufsize] [-t timeout] [-n] [-l limit] [-f family] [-1]\n" 245" [-i interval] [-r arraysize] [-u unit] [-H histfile]\n" 246" [-T qps] [-e] [-D] [-R] [-c] [-v] [-h]\n" 247" -d specifies the input data file (default: stdin)\n" 248" -s sets the server to query (default: %s)\n" 249" -p sets the port on which to query the server (default: %s)\n" 250" -q specifies the maximum number of queries outstanding (default: %d)\n" 251" -t specifies the timeout for query completion in seconds (default: %d)\n" 252" -n causes configuration changes to be ignored\n" 253" -l specifies how a limit for how long to run tests in seconds (no default)\n" 254" -1 run through input only once (default: multiple iff limit given)\n" 255" -b set input/output buffer size in kilobytes (default: %d k)\n" 256" -i specifies interval of intermediate outputs in seconds (default: 0=none)\n" 257" -f specify address family of DNS transport, inet or inet6 (default: any)\n" 258" -r set RTT statistics array size (default: %d)\n" 259" -u set RTT statistics time unit in usec (default: %d)\n" 260" -H specifies RTT histogram data file (default: none)\n" 261" -T specify the target qps (default: 0=unspecified)\n" 262" -e enable EDNS 0\n" 263" -D set the DNSSEC OK bit (implies EDNS)\n" 264" -R disable recursion\n" 265" -c print the number of packets with each rcode\n" 266" -v verbose: report the RCODE of each response on stdout\n" 267" -h print this usage\n" 268"\n", 269 DEF_SERVER_TO_QUERY, DEF_SERVER_PORT, 270 DEF_MAX_QUERIES_OUTSTANDING, DEF_QUERY_TIMEOUT, 271 DEF_BUFFER_SIZE, DEF_RTTARRAY_SIZE, DEF_RTTARRAY_UNIT); 272} 273 274/* 275 * set_datafile: 276 * Set the datafile to read 277 * 278 * Return -1 on failure 279 * Return a non-negative integer otherwise 280 */ 281int 282set_datafile(char *new_file) { 283 char *dfname_tmp; 284 285 if ((new_file == NULL) || (new_file[0] == '\0')) { 286 fprintf(stderr, "Error: null datafile name\n"); 287 return (-1); 288 } 289 290 if ((dfname_tmp = malloc(strlen(new_file) + 1)) == NULL) { 291 fprintf(stderr, "Error allocating memory for datafile name: " 292 "%s\n", new_file); 293 return (-1); 294 } 295 296 free(datafile_name); 297 datafile_name = dfname_tmp; 298 299 strcpy(datafile_name, new_file); 300 use_stdin = FALSE; 301 302 return (0); 303} 304 305/* 306 * set_input_stdin: 307 * Set the input to be stdin (instead of a datafile) 308 */ 309void 310set_input_stdin(void) { 311 use_stdin = TRUE; 312 free(datafile_name); 313 datafile_name = NULL; 314} 315 316/* 317 * set_server: 318 * Set the server to be queried 319 * 320 * Return -1 on failure 321 * Return a non-negative integer otherwise 322 */ 323int 324set_server(char *new_name) { 325 static struct hostent *server_he; 326 327 /* If no change in server name, don't do anything... */ 328 if ((server_to_query != NULL) && (new_name != NULL)) 329 if (strcmp(new_name, server_to_query) == 0) 330 return (0); 331 332 if ((new_name == NULL) || (new_name[0] == '\0')) { 333 fprintf(stderr, "Error: null server name\n"); 334 return (-1); 335 } 336 337 free(server_to_query); 338 server_to_query = NULL; 339 340 if ((server_to_query = malloc(strlen(new_name) + 1)) == NULL) { 341 fprintf(stderr, "Error allocating memory for server name: " 342 "%s\n", new_name); 343 return (-1); 344 } 345 346 strcpy(server_to_query, new_name); 347 348 return (0); 349} 350 351/* 352 * set_server_port: 353 * Set the port on which to contact the server 354 * 355 * Return -1 if port is invalid 356 * Return a non-negative integer otherwise 357 */ 358int 359set_server_port(char *new_port) { 360 unsigned int uint_val; 361 362 if ((is_uint(new_port, &uint_val)) != TRUE) 363 return (-1); 364 365 if (uint_val && uint_val > MAX_PORT) 366 return (-1); 367 else { 368 if (server_port != NULL && new_port != NULL && 369 strcmp(server_port, new_port) == 0) 370 return (0); 371 372 free(server_port); 373 server_port = NULL; 374 375 if ((server_port = malloc(strlen(new_port) + 1)) == NULL) { 376 fprintf(stderr, 377 "Error allocating memory for server port: " 378 "%s\n", new_port); 379 return (-1); 380 } 381 382 strcpy(server_port, new_port); 383 384 return (0); 385 } 386} 387 388int 389set_server_sa(void) { 390 struct addrinfo hints, *res; 391 static struct protoent *proto; 392 int error; 393 394 if (proto == NULL && (proto = getprotobyname("udp")) == NULL) { 395 fprintf(stderr, "Error: getprotobyname call failed"); 396 return (-1); 397 } 398 399 memset(&hints, 0, sizeof(hints)); 400 hints.ai_family = family; 401 hints.ai_socktype = SOCK_DGRAM; 402 hints.ai_protocol = proto->p_proto; 403 if ((error = getaddrinfo(server_to_query, server_port, 404 &hints, &res)) != 0) { 405 fprintf(stderr, "Error: getaddrinfo(%s, %s) failed\n", 406 server_to_query, server_port); 407 return (-1); 408 } 409 410 /* replace the server's addrinfo */ 411 if (server_ai != NULL) 412 freeaddrinfo(server_ai); 413 server_ai = res; 414 return (0); 415} 416 417/* 418 * is_digit: 419 * Tests if a character is a digit 420 * 421 * Return TRUE if it is 422 * Return FALSE if it is not 423 */ 424int 425is_digit(char d) { 426 if (d < '0' || d > '9') 427 return (FALSE); 428 else 429 return (TRUE); 430} 431 432/* 433 * is_uint: 434 * Tests if a string, test_int, is a valid unsigned integer 435 * 436 * Sets *result to be the unsigned integer if it is valid 437 * 438 * Return TRUE if it is 439 * Return FALSE if it is not 440 */ 441int 442is_uint(char *test_int, unsigned int *result) { 443 unsigned long int value; 444 char *end; 445 446 if (test_int == NULL) 447 return (FALSE); 448 449 if (is_digit(test_int[0]) == FALSE) 450 return (FALSE); 451 452 value = strtoul(test_int, &end, 10); 453 454 if ((errno == ERANGE) || (*end != '\0') || (value > UINT_MAX)) 455 return (FALSE); 456 457 *result = (unsigned int)value; 458 return (TRUE); 459} 460 461/* 462 * set_max_queries: 463 * Set the maximum number of outstanding queries 464 * 465 * Returns -1 on failure 466 * Returns a non-negative integer otherwise 467 */ 468int 469set_max_queries(unsigned int new_max) { 470 static unsigned int size_qs = sizeof(struct query_status); 471 struct query_status *temp_stat; 472 unsigned int count; 473 474 if (new_max > query_status_allocated) { 475 temp_stat = realloc(status, new_max * size_qs); 476 477 if (temp_stat == NULL) { 478 fprintf(stderr, "Error resizing query_status\n"); 479 return (-1); 480 } else { 481 status = temp_stat; 482 483 /* 484 * Be careful to only initialise between above 485 * the previously allocated space. Note that the 486 * allocation may be larger than the current 487 * max_queries_outstanding. We don't want to 488 * "forget" any outstanding queries! We might 489 * still have some above the bounds of the max. 490 */ 491 count = query_status_allocated; 492 for (; count < new_max; count++) { 493 status[count].in_use = FALSE; 494 status[count].magic = QUERY_STATUS_MAGIC; 495 status[count].desc = NULL; 496 } 497 498 query_status_allocated = new_max; 499 } 500 } 501 502 max_queries_outstanding = new_max; 503 504 return (0); 505} 506 507/* 508 * parse_args: 509 * Parse program arguments and set configuration options 510 * 511 * Return -1 on failure 512 * Return a non-negative integer otherwise 513 */ 514int 515parse_args(int argc, char **argv) { 516 int c; 517 unsigned int uint_arg_val; 518 519 while ((c = getopt(argc, argv, 520 "f:q:t:i:nd:s:p:1l:b:eDcvr:RT::u:H:h")) != -1) { 521 switch (c) { 522 case 'f': 523 if (strcmp(optarg, "inet") == 0) 524 family = AF_INET; 525#ifdef AF_INET6 526 else if (strcmp(optarg, "inet6") == 0) 527 family = AF_INET6; 528#endif 529 else if (strcmp(optarg, "any") == 0) 530 family = AF_UNSPEC; 531 else { 532 fprintf(stderr, "Invalid address family: %s\n", 533 optarg); 534 return (-1); 535 } 536 break; 537 case 'q': 538 if (is_uint(optarg, &uint_arg_val) == TRUE) { 539 set_max_queries(uint_arg_val); 540 queriesset = TRUE; 541 } else { 542 fprintf(stderr, "Option requires a positive " 543 "integer value: -%c %s\n", 544 c, optarg); 545 return (-1); 546 } 547 break; 548 549 case 't': 550 if (is_uint(optarg, &uint_arg_val) == TRUE) { 551 query_timeout = uint_arg_val; 552 timeoutset = TRUE; 553 } else { 554 fprintf(stderr, "Option requires a positive " 555 "integer value: -%c %s\n", 556 c, optarg); 557 return (-1); 558 } 559 break; 560 561 case 'n': 562 ignore_config_changes = TRUE; 563 break; 564 565 case 'd': 566 if (set_datafile(optarg) == -1) { 567 fprintf(stderr, "Error setting datafile " 568 "name: %s\n", optarg); 569 return (-1); 570 } 571 break; 572 573 case 's': 574 if (set_server(optarg) == -1) { 575 fprintf(stderr, "Error setting server " 576 "name: %s\n", optarg); 577 return (-1); 578 } 579 serverset = TRUE; 580 break; 581 582 case 'p': 583 if (is_uint(optarg, &uint_arg_val) == TRUE && 584 uint_arg_val < MAX_PORT) 585 { 586 set_server_port(optarg); 587 portset = TRUE; 588 } else { 589 fprintf(stderr, "Option requires a positive " 590 "integer between 0 and %d: -%c %s\n", 591 MAX_PORT - 1, c, optarg); 592 return (-1); 593 } 594 break; 595 596 case '1': 597 run_only_once = TRUE; 598 break; 599 600 case 'l': 601 if (is_uint(optarg, &uint_arg_val) == TRUE) { 602 use_timelimit = TRUE; 603 run_timelimit = uint_arg_val; 604 } else { 605 fprintf(stderr, "Option requires a positive " 606 "integer: -%c %s\n", 607 c, optarg); 608 return (-1); 609 } 610 break; 611 612 case 'b': 613 if (is_uint(optarg, &uint_arg_val) == TRUE) { 614 socket_bufsize = uint_arg_val; 615 } else { 616 fprintf(stderr, "Option requires a positive " 617 "integer: -%c %s\n", 618 c, optarg); 619 return (-1); 620 } 621 break; 622 case 'e': 623 edns = TRUE; 624 break; 625 case 'D': 626 dnssec = TRUE; 627 edns = TRUE; 628 break; 629 case 'c': 630 countrcodes = TRUE; 631 break; 632 case 'v': 633 verbose = 1; 634 break; 635 case 'i': 636 if (is_uint(optarg, &uint_arg_val) == TRUE) 637 print_interval = uint_arg_val; 638 else { 639 fprintf(stderr, "Invalid interval: %s\n", 640 optarg); 641 return (-1); 642 } 643 break; 644 case 'R': 645 recurse = 0; 646 break; 647 case 'r': 648 if (is_uint(optarg, &uint_arg_val) == TRUE) 649 rttarray_size = uint_arg_val; 650 else { 651 fprintf(stderr, "Invalid RTT array size: %s\n", 652 optarg); 653 return (-1); 654 } 655 break; 656 case 'u': 657 if (is_uint(optarg, &uint_arg_val) == TRUE) 658 rttarray_unit = uint_arg_val; 659 else { 660 fprintf(stderr, "Invalid RTT unit: %s\n", 661 optarg); 662 return (-1); 663 } 664 break; 665 case 'H': 666 rtt_histogram_file = optarg; 667 break; 668 case 'T': 669 if (is_uint(optarg, &uint_arg_val) == TRUE) 670 target_qps = uint_arg_val; 671 else { 672 fprintf(stderr, "Invalid target qps: %s\n", 673 optarg); 674 return (-1); 675 } 676 break; 677 case 'h': 678 return (-1); 679 default: 680 fprintf(stderr, "Invalid option: -%c\n", optopt); 681 return (-1); 682 } 683 } 684 685 if (run_only_once == FALSE && use_timelimit == FALSE) 686 run_only_once = TRUE; 687 688 return (0); 689} 690 691/* 692 * open_datafile: 693 * Open the data file ready for reading 694 * 695 * Return -1 on failure 696 * Return non-negative integer on success 697 */ 698int 699open_datafile(void) { 700 if (use_stdin == TRUE) { 701 datafile_ptr = stdin; 702 return (0); 703 } else { 704 if ((datafile_ptr = fopen(datafile_name, "r")) == NULL) { 705 fprintf(stderr, "Error: unable to open datafile: %s\n", 706 datafile_name); 707 return (-1); 708 } else 709 return (0); 710 } 711} 712 713/* 714 * close_datafile: 715 * Close the data file if any is open 716 * 717 * Return -1 on failure 718 * Return non-negative integer on success, including if not needed 719 */ 720int 721close_datafile(void) { 722 if ((use_stdin == FALSE) && (datafile_ptr != NULL)) { 723 if (fclose(datafile_ptr) != 0) { 724 fprintf(stderr, "Error: unable to close datafile\n"); 725 return (-1); 726 } 727 } 728 729 return (0); 730} 731 732/* 733 * open_socket: 734 * Open a socket for the queries. When we have an active socket already, 735 * close it and open a new one. 736 * 737 * Return -1 on failure 738 * Return the socket identifier 739 */ 740int 741open_socket(void) { 742 int sock; 743 int ret; 744 int bufsize; 745 struct addrinfo hints, *res; 746 747 memset(&hints, 0, sizeof(hints)); 748 hints.ai_family = server_ai->ai_family; 749 hints.ai_socktype = server_ai->ai_socktype; 750 hints.ai_protocol = server_ai->ai_protocol; 751 hints.ai_flags = AI_PASSIVE; 752 753 if ((ret = getaddrinfo(NULL, "0", &hints, &res)) != 0) { 754 fprintf(stderr, 755 "Error: getaddrinfo for bind socket failed: %s\n", 756 gai_strerror(ret)); 757 return (-1); 758 } 759 760 if ((sock = socket(res->ai_family, SOCK_DGRAM, 761 res->ai_protocol)) == -1) { 762 fprintf(stderr, "Error: socket call failed"); 763 goto fail; 764 } 765 766#if defined(AF_INET6) && defined(IPV6_V6ONLY) 767 if (res->ai_family == AF_INET6) { 768 int on = 1; 769 770 if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, 771 &on, sizeof(on)) == -1) { 772 fprintf(stderr, 773 "Warning: setsockopt(IPV6_V6ONLY) failed\n"); 774 } 775 } 776#endif 777 778 if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) 779 fprintf(stderr, "Error: bind call failed"); 780 781 freeaddrinfo(res); 782 783 bufsize = 1024 * socket_bufsize; 784 785 ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, 786 (char *) &bufsize, sizeof(bufsize)); 787 if (ret < 0) 788 fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n"); 789 790 ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, 791 (char *) &bufsize, sizeof(bufsize)); 792 if (ret < 0) 793 fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n"); 794 795 return (sock); 796 797 fail: 798 if (res) 799 freeaddrinfo(res); 800 return (-1); 801} 802 803/* 804 * close_socket: 805 * Close the query socket(s) 806 * 807 * Return -1 on failure 808 * Return a non-negative integer otherwise 809 */ 810int 811close_socket(void) { 812 if (socket4 != -1) { 813 if (close(socket4) != 0) { 814 fprintf(stderr, 815 "Error: unable to close IPv4 socket\n"); 816 return (-1); 817 } 818 } 819 820 if (socket6 != -1) { 821 if (close(socket6) != 0) { 822 fprintf(stderr, 823 "Error: unable to close IPv6 socket\n"); 824 return (-1); 825 } 826 } 827 828 query_socket = -1; 829 830 return (0); 831} 832 833/* 834 * change_socket: 835 * Choose an appropriate socket according to the address family of the 836 * current server. Open a new socket if necessary. 837 * 838 * Return -1 on failure 839 * Return the socket identifier 840 */ 841int 842change_socket(void) { 843 int s, *sockp; 844 845 switch (server_ai->ai_family) { 846 case AF_INET: 847 sockp = &socket4; 848 break; 849#ifdef AF_INET6 850 case AF_INET6: 851 sockp = &socket6; 852 break; 853#endif 854 default: 855 fprintf(stderr, "unexpected address family: %d\n", 856 server_ai->ai_family); 857 exit(1); 858 } 859 860 if (*sockp == -1) { 861 if ((s = open_socket()) == -1) 862 return (-1); 863 *sockp = s; 864 } 865 866 return (*sockp); 867} 868 869/* 870 * reset_rttarray: 871 * (re)allocate RTT array and zero-clear the whole buffer. 872 * if array is being used, it is freed. 873 * Returns -1 on failure 874 * Returns a non-negative integer otherwise 875 */ 876int 877reset_rttarray(int size) { 878 if (rttarray != NULL) 879 free(rttarray); 880 if (rttarray_interval != NULL) 881 free(rttarray_interval); 882 883 rttarray = NULL; 884 rttarray_interval = NULL; 885 rtt_max = -1; 886 rtt_min = -1; 887 888 if (size > 0) { 889 rttarray = malloc(size * sizeof(rttarray[0])); 890 if (rttarray == NULL) { 891 fprintf(stderr, 892 "Error: allocating memory for RTT array\n"); 893 return (-1); 894 } 895 memset(rttarray, 0, size * sizeof(rttarray[0])); 896 897 rttarray_interval = malloc(size * 898 sizeof(rttarray_interval[0])); 899 if (rttarray_interval == NULL) { 900 fprintf(stderr, 901 "Error: allocating memory for RTT array\n"); 902 return (-1); 903 } 904 905 memset(rttarray_interval, 0, 906 size * sizeof(rttarray_interval[0])); 907 } 908 909 return (0); 910} 911 912/* 913 * set_query_interval: 914 * set the interval of consecutive queries if the target qps are specified. 915 * Returns -1 on failure 916 * Returns a non-negative integer otherwise 917 */ 918int 919set_query_interval(unsigned int qps) { 920 if (qps == 0) 921 return (0); 922 923 query_interval = (1.0 / (double)qps); 924 925 return (0); 926} 927 928/* 929 * setup: 930 * Set configuration options from command line arguments 931 * Open datafile ready for reading 932 * 933 * Return -1 on failure 934 * Return non-negative integer on success 935 */ 936int 937setup(int argc, char **argv) { 938 set_input_stdin(); 939 940 if (set_max_queries(DEF_MAX_QUERIES_OUTSTANDING) == -1) { 941 fprintf(stderr, "%s: Unable to set default max outstanding " 942 "queries\n", argv[0]); 943 return (-1); 944 } 945 946 if (set_server(DEF_SERVER_TO_QUERY) == -1) { 947 fprintf(stderr, "%s: Error setting default server name\n", 948 argv[0]); 949 return (-1); 950 } 951 952 if (set_server_port(DEF_SERVER_PORT) == -1) { 953 fprintf(stderr, "%s: Error setting default server port\n", 954 argv[0]); 955 return (-1); 956 } 957 958 if (parse_args(argc, argv) == -1) { 959 show_usage(); 960 return (-1); 961 } 962 963 if (open_datafile() == -1) 964 return (-1); 965 966 if (set_server_sa() == -1) 967 return (-1); 968 969 if ((query_socket = change_socket()) == -1) 970 return (-1); 971 972 if (reset_rttarray(rttarray_size) == -1) 973 return (-1); 974 975 if (set_query_interval(target_qps) == -1) 976 return (-1); 977 978 return (0); 979} 980 981/* 982 * set_timenow: 983 * Set a timeval struct to indicate the current time 984 */ 985void 986set_timenow(struct timeval *tv) { 987 if (gettimeofday(tv, NULL) == -1) { 988 fprintf(stderr, "Error in gettimeofday(). Using inaccurate " 989 "time() instead\n"); 990 tv->tv_sec = time(NULL); 991 tv->tv_usec = 0; 992 } 993} 994 995/* 996 * addtv: 997 * add tv1 and tv2, store the result in tv_result. 998 */ 999void 1000addtv(struct timeval *tv1, struct timeval *tv2, struct timeval *tv_result) { 1001 tv_result->tv_sec = tv1->tv_sec + tv2->tv_sec; 1002 tv_result->tv_usec = tv1->tv_usec + tv2->tv_usec; 1003 if (tv_result->tv_usec > 1000000) { 1004 tv_result->tv_sec++; 1005 tv_result->tv_usec -= 1000000; 1006 } 1007} 1008 1009/* 1010 * difftv: 1011 * Find the difference in seconds between two timeval structs. 1012 * 1013 * Return the difference between tv1 and tv2 in seconds in a double. 1014 */ 1015double 1016difftv(struct timeval tv1, struct timeval tv2) { 1017 long diff_sec, diff_usec; 1018 double diff; 1019 1020 diff_sec = tv1.tv_sec - tv2.tv_sec; 1021 diff_usec = tv1.tv_usec - tv2.tv_usec; 1022 1023 diff = (double)diff_sec + ((double)diff_usec / 1000000.0); 1024 1025 return (diff); 1026} 1027 1028/* 1029 * timelimit_reached: 1030 * Have we reached the time limit (if any)? 1031 * 1032 * Returns FALSE if there is no time limit or if we have not reached it 1033 * Returns TRUE otherwise 1034 */ 1035int 1036timelimit_reached(void) { 1037 struct timeval time_now; 1038 1039 set_timenow(&time_now); 1040 1041 if (use_timelimit == FALSE) 1042 return (FALSE); 1043 1044 if (setup_phase == TRUE) { 1045 if (difftv(time_now, time_of_program_start) 1046 < (double)(run_timelimit + HARD_TIMEOUT_EXTRA)) 1047 return (FALSE); 1048 else 1049 return (TRUE); 1050 } else { 1051 if (difftv(time_now, time_of_first_query) 1052 < (double)run_timelimit) 1053 return (FALSE); 1054 else 1055 return (TRUE); 1056 } 1057} 1058 1059/* 1060 * keep_sending: 1061 * Should we keep sending queries or stop here? 1062 * 1063 * Return TRUE if we should keep on sending queries 1064 * Return FALSE if we should stop 1065 * 1066 * Side effects: 1067 * Rewinds the input and clears reached_end_input if we have reached the 1068 * end of the input, but we are meant to run through it multiple times 1069 * and have not hit the time limit yet (if any is set). 1070 */ 1071int 1072keep_sending(int *reached_end_input) { 1073 static int stop = FALSE; 1074 1075 if (stop == TRUE) 1076 return (FALSE); 1077 1078 if ((*reached_end_input == FALSE) && (timelimit_reached() == FALSE)) 1079 return (TRUE); 1080 else if ((*reached_end_input == TRUE) && (run_only_once == FALSE) 1081 && (timelimit_reached() == FALSE)) { 1082 rewind(datafile_ptr); 1083 *reached_end_input = FALSE; 1084 runs_through_file++; 1085 return (TRUE); 1086 } else { 1087 if (*reached_end_input == TRUE) 1088 runs_through_file++; 1089 set_timenow(&time_of_stop_sending); 1090 stop = TRUE; 1091 return (FALSE); 1092 } 1093} 1094 1095/* 1096 * queries_outstanding: 1097 * How many queries are outstanding? 1098 * 1099 * Returns the number of outstanding queries 1100 */ 1101unsigned int 1102queries_outstanding(void) { 1103 return (num_queries_outstanding); 1104} 1105 1106/* 1107 * next_input_line: 1108 * Get the next non-comment line from the input file 1109 * 1110 * Put text in line, up to max of n chars. Skip comment lines. 1111 * Skip empty lines. 1112 * 1113 * Return line length on success 1114 * Return 0 if cannot read a non-comment line (EOF or error) 1115 */ 1116int 1117next_input_line(char *line, int n) { 1118 char *result; 1119 1120 do { 1121 result = fgets(line, n, datafile_ptr); 1122 } while ((result != NULL) && 1123 ((line[0] == COMMENT_CHAR) || (line[0] == '\n'))); 1124 1125 if (result == NULL) 1126 return (0); 1127 else 1128 return (strlen(line)); 1129} 1130 1131/* 1132 * identify_directive: 1133 * Gives us a numerical value equivelant for a directive string 1134 * 1135 * Returns the value for the directive 1136 * Returns -1 if not a valid directive 1137 */ 1138int 1139identify_directive(char *dir) { 1140 static char *directives[] = DIRECTIVES; 1141 static int dir_values[] = DIR_VALUES; 1142 unsigned int index, num_directives; 1143 1144 num_directives = sizeof(directives) / sizeof(directives[0]); 1145 1146 if (num_directives > (sizeof(dir_values) / sizeof(int))) 1147 num_directives = sizeof(dir_values) / sizeof(int); 1148 1149 for (index = 0; index < num_directives; index++) { 1150 if (strcmp(dir, directives[index]) == 0) 1151 return (dir_values[index]); 1152 } 1153 1154 return (-1); 1155} 1156 1157/* 1158 * update_config: 1159 * Update configuration options from a line from the input file 1160 */ 1161void 1162update_config(char *config_change_desc) { 1163 char *directive, *config_value, *trailing_garbage; 1164 char conf_copy[MAX_INPUT_LEN + 1]; 1165 unsigned int uint_val; 1166 int directive_number; 1167 int check; 1168 int old_af; 1169 1170 if (ignore_config_changes == TRUE) { 1171 fprintf(stderr, "Ignoring configuration change: %s", 1172 config_change_desc); 1173 return; 1174 } 1175 1176 strcpy(conf_copy, config_change_desc); 1177 1178 ++config_change_desc; 1179 1180 if (*config_change_desc == '\0') { 1181 fprintf(stderr, "Invalid config: No directive present: %s\n", 1182 conf_copy); 1183 return; 1184 } 1185 1186 if (index(WHITESPACE, *config_change_desc) != NULL) { 1187 fprintf(stderr, "Invalid config: Space before directive or " 1188 "no directive present: %s\n", conf_copy); 1189 return; 1190 } 1191 1192 directive = strtok(config_change_desc, WHITESPACE); 1193 config_value = strtok(NULL, WHITESPACE); 1194 trailing_garbage = strtok(NULL, WHITESPACE); 1195 1196 if ((directive_number = identify_directive(directive)) == -1) { 1197 fprintf(stderr, "Invalid config: Bad directive: %s\n", 1198 conf_copy); 1199 return; 1200 } 1201 1202 if (config_value == NULL) { 1203 fprintf(stderr, "Invalid config: No value present: %s\n", 1204 conf_copy); 1205 return; 1206 } 1207 1208 if (trailing_garbage != NULL) { 1209 fprintf(stderr, "Config warning: " 1210 "trailing garbage: %s\n", conf_copy); 1211 } 1212 1213 switch(directive_number) { 1214 1215 case V_SERVER: 1216 if (serverset && (setup_phase == TRUE)) { 1217 fprintf(stderr, "Config change overridden by command " 1218 "line: %s\n", directive); 1219 return; 1220 } 1221 1222 if (set_server(config_value) == -1) { 1223 fprintf(stderr, "Set server error: unable to change " 1224 "the server name to '%s'\n", config_value); 1225 return; 1226 } 1227 1228 old_af = server_ai->ai_family; 1229 if (set_server_sa() == -1) { 1230 fprintf(stderr, "Set server error: unable to resolve " 1231 "a new server '%s'\n", 1232 config_value); 1233 return; 1234 } 1235 if (old_af != server_ai->ai_family) { 1236 if ((query_socket = change_socket()) == -1) { 1237 /* XXX: this is fatal */ 1238 fprintf(stderr, "Set server error: " 1239 "unable to open a new socket " 1240 "for '%s'\n", config_value); 1241 exit(1); 1242 } 1243 } 1244 1245 break; 1246 1247 case V_PORT: 1248 if (portset && (setup_phase == TRUE)) { 1249 fprintf(stderr, "Config change overridden by command " 1250 "line: %s\n", directive); 1251 return; 1252 } 1253 1254 check = is_uint(config_value, &uint_val); 1255 1256 if ((check == TRUE) && (uint_val > 0)) { 1257 if (set_server_port(config_value) == -1) { 1258 fprintf(stderr, "Invalid config: Bad value for" 1259 " %s: %s\n", directive, config_value); 1260 } else { 1261 if (set_server_sa() == -1) { 1262 fprintf(stderr, 1263 "Failed to set a new port\n"); 1264 return; 1265 } 1266 } 1267 } else 1268 fprintf(stderr, "Invalid config: Bad value for " 1269 "%s: %s\n", directive, config_value); 1270 break; 1271 1272 case V_MAXQUERIES: 1273 if (queriesset && (setup_phase == TRUE)) { 1274 fprintf(stderr, "Config change overridden by command " 1275 "line: %s\n", directive); 1276 return; 1277 } 1278 1279 check = is_uint(config_value, &uint_val); 1280 1281 if ((check == TRUE) && (uint_val > 0)) { 1282 set_max_queries(uint_val); 1283 } else 1284 fprintf(stderr, "Invalid config: Bad value for " 1285 "%s: %s\n", directive, config_value); 1286 break; 1287 1288 case V_MAXWAIT: 1289 if (timeoutset && (setup_phase == TRUE)) { 1290 fprintf(stderr, "Config change overridden by command " 1291 "line: %s\n", directive); 1292 return; 1293 } 1294 1295 check = is_uint(config_value, &uint_val); 1296 1297 if ((check == TRUE) && (uint_val > 0)) { 1298 query_timeout = uint_val; 1299 } else 1300 fprintf(stderr, "Invalid config: Bad value for " 1301 "%s: %s\n", directive, config_value); 1302 break; 1303 1304 default: 1305 fprintf(stderr, "Invalid config: Bad directive: %s\n", 1306 directive); 1307 break; 1308 } 1309} 1310 1311/* 1312 * parse_query: 1313 * Parse a query line from the input file 1314 * 1315 * Set qname to be the domain to query (up to a max of qnlen chars) 1316 * Set qtype to be the type of the query 1317 * 1318 * Return -1 on failure 1319 * Return a non-negative integer otherwise 1320 */ 1321int 1322parse_query(char *input, char *qname, int qnlen, int *qtype) { 1323 static char *qtype_strings[] = QTYPE_STRINGS; 1324 static int qtype_codes[] = QTYPE_CODES; 1325 int num_types, index; 1326 int found = FALSE; 1327 char incopy[MAX_INPUT_LEN + 1]; 1328 char *domain_str, *type_str; 1329 1330 num_types = sizeof(qtype_strings) / sizeof(qtype_strings[0]); 1331 if (num_types > (sizeof(qtype_codes) / sizeof(int))) 1332 num_types = sizeof(qtype_codes) / sizeof(int); 1333 1334 strcpy(incopy, input); 1335 1336 domain_str = strtok(incopy, WHITESPACE); 1337 type_str = strtok(NULL, WHITESPACE); 1338 1339 if ((domain_str == NULL) || (type_str == NULL)) { 1340 fprintf(stderr, "Invalid query input format: %s\n", input); 1341 return (-1); 1342 } 1343 1344 if (strlen(domain_str) > qnlen) { 1345 fprintf(stderr, "Query domain too long: %s\n", domain_str); 1346 return (-1); 1347 } 1348 1349 for (index = 0; (index < num_types) && (found == FALSE); index++) { 1350 if (strcasecmp(type_str, qtype_strings[index]) == 0) { 1351 *qtype = qtype_codes[index]; 1352 found = TRUE; 1353 } 1354 } 1355 1356 if (found == FALSE) { 1357 fprintf(stderr, "Query type not understood: %s\n", type_str); 1358 return (-1); 1359 } 1360 1361 strcpy(qname, domain_str); 1362 1363 return (0); 1364} 1365 1366/* 1367 * dispatch_query: 1368 * Send the query packet for the entry 1369 * 1370 * Return -1 on failure 1371 * Return a non-negative integer otherwise 1372 */ 1373int 1374dispatch_query(unsigned short int id, char *dom, int qt) { 1375 static u_char packet_buffer[PACKETSZ + 1]; 1376 int buffer_len = PACKETSZ; 1377 int bytes_sent; 1378 unsigned short int net_id = htons(id); 1379 char *id_ptr = (char *)&net_id; 1380 HEADER *hp = (HEADER *)packet_buffer; 1381 1382 buffer_len = res_mkquery(QUERY, dom, C_IN, qt, NULL, 0, 1383 NULL, packet_buffer, PACKETSZ); 1384 if (buffer_len == -1) { 1385 fprintf(stderr, "Failed to create query packet: %s %d\n", 1386 dom, qt); 1387 return (-1); 1388 } 1389 hp->rd = recurse; 1390 if (edns) { 1391 unsigned char *p; 1392 if (buffer_len + EDNSLEN >= PACKETSZ) { 1393 fprintf(stderr, "Failed to add OPT to query packet\n"); 1394 return (-1); 1395 } 1396 packet_buffer[11] = 1; 1397 p = &packet_buffer[buffer_len]; 1398 *p++ = 0; /* root name */ 1399 *p++ = 0; 1400 *p++ = 41; /* OPT */ 1401 *p++ = 16; 1402 *p++ = 0; /* UDP payload size (4K) */ 1403 *p++ = 0; /* extended rcode */ 1404 *p++ = 0; /* version */ 1405 if (dnssec) 1406 *p++ = 0x80; /* upper flag bits - DO set */ 1407 else 1408 *p++ = 0; /* upper flag bits */ 1409 *p++ = 0; /* lower flag bit */ 1410 *p++ = 0; 1411 *p++ = 0; /* rdlen == 0 */ 1412 buffer_len += EDNSLEN; 1413 } 1414 1415 packet_buffer[0] = id_ptr[0]; 1416 packet_buffer[1] = id_ptr[1]; 1417 1418 bytes_sent = sendto(query_socket, packet_buffer, buffer_len, 0, 1419 server_ai->ai_addr, server_ai->ai_addrlen); 1420 if (bytes_sent == -1) { 1421 fprintf(stderr, "Failed to send query packet: %s %d\n", 1422 dom, qt); 1423 return (-1); 1424 } 1425 1426 if (bytes_sent != buffer_len) 1427 fprintf(stderr, "Warning: incomplete packet sent: %s %d\n", 1428 dom, qt); 1429 1430 return (0); 1431} 1432 1433/* 1434 * send_query: 1435 * Send a query based on a line of input 1436 */ 1437void 1438send_query(char *query_desc) { 1439 static unsigned short int use_query_id = 0; 1440 static int qname_len = MAX_DOMAIN_LEN; 1441 static char domain[MAX_DOMAIN_LEN + 1]; 1442 char serveraddr[NI_MAXHOST]; 1443 int query_type; 1444 unsigned int count; 1445 1446 use_query_id++; 1447 1448 if (parse_query(query_desc, domain, qname_len, &query_type) == -1) { 1449 fprintf(stderr, "Error parsing query: %s\n", query_desc); 1450 return; 1451 } 1452 1453 if (dispatch_query(use_query_id, domain, query_type) == -1) { 1454 char *addrstr; 1455 1456 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen, 1457 serveraddr, sizeof(serveraddr), NULL, 0, 1458 NI_NUMERICHOST) == 0) { 1459 addrstr = serveraddr; 1460 } else 1461 addrstr = "???"; /* XXX: this should not happen */ 1462 fprintf(stderr, "Error sending query to %s: %s\n", 1463 addrstr, query_desc); 1464 return; 1465 } 1466 1467 if (setup_phase == TRUE) { 1468 set_timenow(&time_of_first_query); 1469 time_of_first_query_sec = (double)time_of_first_query.tv_sec + 1470 ((double)time_of_first_query.tv_usec / 1000000.0); 1471 setup_phase = FALSE; 1472 if (getnameinfo(server_ai->ai_addr, server_ai->ai_addrlen, 1473 serveraddr, sizeof(serveraddr), NULL, 0, 1474 NI_NUMERICHOST) != 0) { 1475 fprintf(stderr, "Error printing server address\n"); 1476 return; 1477 } 1478 printf("[Status] Sending queries (beginning with %s)\n", 1479 serveraddr); 1480 } 1481 1482 /* Find the first slot in status[] that is not in use */ 1483 for (count = 0; (status[count].in_use == TRUE) 1484 && (count < max_queries_outstanding); count++); 1485 1486 if (status[count].in_use == TRUE) { 1487 fprintf(stderr, "Unexpected error: We have run out of " 1488 "status[] space!\n"); 1489 return; 1490 } 1491 1492 /* Register the query in status[] */ 1493 status[count].in_use = TRUE; 1494 status[count].id = use_query_id; 1495 if (verbose) 1496 status[count].desc = strdup(query_desc); 1497 set_timenow(&status[count].sent_timestamp); 1498 1499 if (num_queries_sent_interval == 0) 1500 set_timenow(&time_of_first_query_interval); 1501 1502 num_queries_sent++; 1503 num_queries_sent_interval++; 1504 num_queries_outstanding++; 1505} 1506 1507void 1508register_rtt(struct timeval *timestamp) { 1509 int i; 1510 int oldquery = FALSE; 1511 struct timeval now; 1512 double rtt; 1513 1514 set_timenow(&now); 1515 rtt = difftv(now, *timestamp); 1516 1517 if (difftv(*timestamp, time_of_first_query_interval) < 0) 1518 oldquery = TRUE; 1519 1520 if (rtt_max < 0 || rtt_max < rtt) 1521 rtt_max = rtt; 1522 1523 if (rtt_min < 0 || rtt_min > rtt) 1524 rtt_min = rtt; 1525 1526 rtt_total += rtt; 1527 1528 if (!oldquery) { 1529 if (rtt_max_interval < 0 || rtt_max_interval < rtt) 1530 rtt_max_interval = rtt; 1531 1532 if (rtt_min_interval < 0 || rtt_min_interval > rtt) 1533 rtt_min_interval = rtt; 1534 1535 rtt_total_interval += rtt; 1536 } 1537 1538 if (rttarray == NULL) 1539 return; 1540 1541 i = (int)(rtt * (1000000.0 / rttarray_unit)); 1542 if (i < rttarray_size) { 1543 rttarray[i]++; 1544 if (!oldquery) 1545 rttarray_interval[i]++; 1546 } else { 1547 fprintf(stderr, "Warning: RTT is out of range: %.6lf\n", 1548 rtt); 1549 rtt_overflows++; 1550 if (!oldquery) 1551 rtt_overflows_interval++; 1552 } 1553} 1554 1555/* 1556 * register_response: 1557 * Register receipt of a query 1558 * 1559 * Removes (sets in_use = FALSE) the record for the given query id in 1560 * status[] if any exists. 1561 */ 1562void 1563register_response(unsigned short int id, unsigned int rcode) { 1564 unsigned int ct = 0; 1565 int found = FALSE; 1566 struct timeval now; 1567 double rtt; 1568 1569 for (; (ct < query_status_allocated) && (found == FALSE); ct++) { 1570 if ((status[ct].in_use == TRUE) && (status[ct].id == id)) { 1571 status[ct].in_use = FALSE; 1572 num_queries_outstanding--; 1573 found = TRUE; 1574 1575 register_rtt(&status[ct].sent_timestamp); 1576 1577 if (status[ct].desc) { 1578 printf("> %s %s\n", rcode_strings[rcode], 1579 status[ct].desc); 1580 free(status[ct].desc); 1581 } 1582 if (countrcodes) 1583 rcodecounts[rcode]++; 1584 } 1585 } 1586 1587 if (found == FALSE) { 1588 if (target_qps > 0) { 1589 num_queries_possiblydelayed++; 1590 num_queries_possiblydelayed_interval++; 1591 } else { 1592 fprintf(stderr, "Warning: Received a response with an " 1593 "unexpected (maybe timed out) id: %u\n", id); 1594 } 1595 } 1596} 1597 1598/* 1599 * process_single_response: 1600 * Receive from the given socket & process an invididual response packet. 1601 * Remove it from the list of open queries (status[]) and decrement the 1602 * number of outstanding queries if it matches an open query. 1603 */ 1604void 1605process_single_response(int sockfd) { 1606 struct sockaddr_storage from_addr_ss; 1607 struct sockaddr *from_addr; 1608 static unsigned char in_buf[MAX_BUFFER_LEN]; 1609 int numbytes, resp_id; 1610 socklen_t addr_len; 1611 int flags; 1612 1613 memset(&from_addr_ss, 0, sizeof(from_addr_ss)); 1614 from_addr = (struct sockaddr *)&from_addr_ss; 1615 addr_len = sizeof(from_addr_ss); 1616 1617 if ((numbytes = recvfrom(sockfd, in_buf, MAX_BUFFER_LEN, 1618 0, from_addr, &addr_len)) == -1) { 1619 fprintf(stderr, "Error receiving datagram\n"); 1620 return; 1621 } 1622 1623 resp_id = get_uint16(in_buf); 1624 flags = get_uint16(in_buf + 2); 1625 1626 register_response(resp_id, flags & 0xF); 1627} 1628 1629/* 1630 * data_available: 1631 * Is there data available on the given file descriptor? 1632 * 1633 * Return TRUE if there is 1634 * Return FALSE otherwise 1635 */ 1636int 1637data_available(double wait) { 1638 fd_set read_fds; 1639 struct timeval tv; 1640 int retval; 1641 int available = FALSE; 1642 int maxfd = -1; 1643 1644 /* Set list of file descriptors */ 1645 FD_ZERO(&read_fds); 1646 if (socket4 != -1) { 1647 FD_SET(socket4, &read_fds); 1648 maxfd = socket4; 1649 } 1650 if (socket6 != -1) { 1651 FD_SET(socket6, &read_fds); 1652 if (maxfd == -1 || maxfd < socket6) 1653 maxfd = socket6; 1654 } 1655 1656 if ((wait > 0.0) && (wait < (double)LONG_MAX)) { 1657 tv.tv_sec = (long)floor(wait); 1658 tv.tv_usec = (long)(1000000.0 * (wait - floor(wait))); 1659 } else { 1660 tv.tv_sec = 0; 1661 tv.tv_usec = 0; 1662 } 1663 1664 retval = select(maxfd + 1, &read_fds, NULL, NULL, &tv); 1665 1666 if (socket4 != -1 && FD_ISSET(socket4, &read_fds)) { 1667 available = TRUE; 1668 process_single_response(socket4); 1669 } 1670 if (socket6 != -1 && FD_ISSET(socket6, &read_fds)) { 1671 available = TRUE; 1672 process_single_response(socket6); 1673 } 1674 1675 return (available); 1676} 1677 1678/* 1679 * process_responses: 1680 * Go through any/all received responses and remove them from the list of 1681 * open queries (set in_use = FALSE for their entry in status[]), also 1682 * decrementing the number of outstanding queries. 1683 */ 1684void 1685process_responses(int adjust_rate) { 1686 double wait; 1687 struct timeval now, waituntil; 1688 double first_packet_wait = RESPONSE_BLOCKING_WAIT_TIME; 1689 unsigned int outstanding = queries_outstanding(); 1690 1691 if (adjust_rate == TRUE) { 1692 double u; 1693 1694 u = time_of_first_query_sec + 1695 query_interval * num_queries_sent; 1696 waituntil.tv_sec = (long)floor(u); 1697 waituntil.tv_usec = (long)(1000000.0 * (u - waituntil.tv_sec)); 1698 1699 /* 1700 * Wait until a response arrives or the specified limit is 1701 * reached. 1702 */ 1703 while (1) { 1704 set_timenow(&now); 1705 wait = difftv(waituntil, now); 1706 if (wait <= 0) 1707 wait = 0.0; 1708 if (data_available(wait) != TRUE) 1709 break; 1710 1711 /* 1712 * We have reached the limit. Read as many responses 1713 * as possible without waiting, and exit. 1714 */ 1715 if (wait == 0) { 1716 while (data_available(0.0) == TRUE) 1717 ; 1718 break; 1719 } 1720 } 1721 } else { 1722 /* 1723 * Don't block waiting for packets at all if we aren't 1724 * looking for any responses or if we are now able to send new 1725 * queries. 1726 */ 1727 if ((outstanding == 0) || 1728 (outstanding < max_queries_outstanding)) { 1729 first_packet_wait = 0.0; 1730 } 1731 1732 if (data_available(first_packet_wait) == TRUE) { 1733 while (data_available(0.0) == TRUE) 1734 ; 1735 } 1736 } 1737} 1738 1739/* 1740 * retire_old_queries: 1741 * Go through the list of open queries (status[]) and remove any queries 1742 * (i.e. set in_use = FALSE) which are older than the timeout, decrementing 1743 * the number of queries outstanding for each one removed. 1744 */ 1745void 1746retire_old_queries(int sending) { 1747 unsigned int count = 0; 1748 struct timeval curr_time; 1749 double timeout = query_timeout; 1750 int timeout_reduced = FALSE; 1751 1752 /* 1753 * If we have target qps and would not be able to send any packets 1754 * due to buffer full, check whether we are behind the schedule. 1755 * If we are, purge some queries more aggressively. 1756 */ 1757 if (target_qps > 0 && sending == TRUE && count == 0 && 1758 queries_outstanding() == max_queries_outstanding) { 1759 struct timeval next, now; 1760 double n; 1761 1762 n = time_of_first_query_sec + 1763 query_interval * num_queries_sent; 1764 next.tv_sec = (long)floor(n); 1765 next.tv_usec = (long)(1000000.0 * (n - next.tv_sec)); 1766 1767 set_timenow(&now); 1768 if (difftv(next, now) <= 0) { 1769 timeout_reduced = TRUE; 1770 timeout = 0.001; /* XXX: ad-hoc value */ 1771 } 1772 } 1773 1774 set_timenow(&curr_time); 1775 1776 for (; count < query_status_allocated; count++) { 1777 1778 if ((status[count].in_use == TRUE) 1779 && (difftv(curr_time, status[count].sent_timestamp) 1780 >= (double)timeout)) { 1781 1782 status[count].in_use = FALSE; 1783 num_queries_outstanding--; 1784 num_queries_timed_out++; 1785 num_queries_timed_out_interval++; 1786 1787 if (timeout_reduced == FALSE) { 1788 if (status[count].desc) { 1789 printf("> T %s\n", status[count].desc); 1790 free(status[count].desc); 1791 } else { 1792 printf("[Timeout] Query timed out: " 1793 "msg id %u\n", 1794 status[count].id); 1795 } 1796 } 1797 } 1798 } 1799} 1800 1801/* 1802 * print_histogram 1803 * Print RTT histogram to the specified file in the gnuplot format 1804 */ 1805void 1806print_histogram(unsigned int total) { 1807 int i; 1808 double ratio; 1809 FILE *fp; 1810 1811 if (rtt_histogram_file == NULL || rttarray == NULL) 1812 return; 1813 1814 fp = fopen((const char *)rtt_histogram_file, "w+"); 1815 if (fp == NULL) { 1816 fprintf(stderr, "Error opening RTT histogram file: %s\n", 1817 rtt_histogram_file); 1818 return; 1819 } 1820 1821 for (i = 0; i < rttarray_size; i++) { 1822 ratio = ((double)rttarray[i] / (double)total) * 100; 1823 fprintf(fp, "%.6lf %.3lf\n", 1824 (double)(i * rttarray_unit) + 1825 (double)rttarray_unit / 2, 1826 ratio); 1827 } 1828 1829 (void)fclose(fp); 1830} 1831 1832/* 1833 * print_statistics: 1834 * Print out statistics based on the results of the test 1835 */ 1836void 1837print_statistics(int intermediate, unsigned int sent, unsigned int timed_out, 1838 unsigned int possibly_delayed, 1839 struct timeval *first_query, 1840 struct timeval *program_start, 1841 struct timeval *end_perf, struct timeval *end_query, 1842 double rmax, double rmin, double rtotal, 1843 unsigned int roverflows, unsigned int *rarray) 1844{ 1845 unsigned int num_queries_completed; 1846 double per_lost, per_completed, per_lost2, per_completed2; 1847 double run_time, queries_per_sec, queries_per_sec2; 1848 double queries_per_sec_total; 1849 double rtt_average, rtt_stddev; 1850 struct timeval start_time; 1851 1852 num_queries_completed = sent - timed_out; 1853 1854 if (num_queries_completed == 0) { 1855 per_lost = 0.0; 1856 per_completed = 0.0; 1857 1858 per_lost2 = 0.0; 1859 per_completed2 = 0.0; 1860 } else { 1861 per_lost = (100.0 * (double)timed_out) / (double)sent; 1862 per_completed = 100.0 - per_lost; 1863 1864 per_lost2 = (100.0 * (double)(timed_out - possibly_delayed)) 1865 / (double)sent; 1866 per_completed2 = 100 - per_lost2; 1867 } 1868 1869 if (sent == 0) { 1870 start_time.tv_sec = program_start->tv_sec; 1871 start_time.tv_usec = program_start->tv_usec; 1872 run_time = 0.0; 1873 queries_per_sec = 0.0; 1874 queries_per_sec2 = 0.0; 1875 queries_per_sec_total = 0.0; 1876 } else { 1877 start_time.tv_sec = first_query->tv_sec; 1878 start_time.tv_usec = first_query->tv_usec; 1879 run_time = difftv(*end_perf, *first_query); 1880 queries_per_sec = (double)num_queries_completed / run_time; 1881 queries_per_sec2 = (double)(num_queries_completed + 1882 possibly_delayed) / run_time; 1883 1884 queries_per_sec_total = (double)sent / 1885 difftv(*end_query, *first_query); 1886 } 1887 1888 if (num_queries_completed > 0) { 1889 int i; 1890 double sum = 0; 1891 1892 rtt_average = rtt_total / (double)num_queries_completed; 1893 for (i = 0; i < rttarray_size; i++) { 1894 if (rarray[i] != 0) { 1895 double mean, diff; 1896 1897 mean = (double)(i * rttarray_unit) + 1898 (double)rttarray_unit / 2; 1899 diff = rtt_average - (mean / 1000000.0); 1900 sum += (diff * diff) * rarray[i]; 1901 } 1902 } 1903 rtt_stddev = sqrt(sum / (double)num_queries_completed); 1904 } else { 1905 rtt_average = 0.0; 1906 rtt_stddev = 0.0; 1907 } 1908 1909 printf("\n"); 1910 1911 printf("%sStatistics:\n", intermediate ? "Intermediate " : ""); 1912 1913 printf("\n"); 1914 1915 if (!intermediate) { 1916 printf(" Parse input file: %s\n", 1917 ((run_only_once == TRUE) ? "once" : "multiple times")); 1918 if (use_timelimit) 1919 printf(" Run time limit: %u seconds\n", 1920 run_timelimit); 1921 if (run_only_once == FALSE) 1922 printf(" Ran through file: %u times\n", 1923 runs_through_file); 1924 else 1925 printf(" Ended due to: reaching %s\n", 1926 ((runs_through_file == 0) ? "time limit" 1927 : "end of file")); 1928 1929 printf("\n"); 1930 } 1931 1932 printf(" Queries sent: %u queries\n", sent); 1933 printf(" Queries completed: %u queries\n", num_queries_completed); 1934 printf(" Queries lost: %u queries\n", timed_out); 1935 printf(" Queries delayed(?): %u queries\n", possibly_delayed); 1936 1937 printf("\n"); 1938 1939 printf(" RTT max: %3.6lf sec\n", rmax); 1940 printf(" RTT min: %3.6lf sec\n", rmin); 1941 printf(" RTT average: %3.6lf sec\n", rtt_average); 1942 printf(" RTT std deviation: %3.6lf sec\n", rtt_stddev); 1943 printf(" RTT out of range: %u queries\n", roverflows); 1944 1945 if (!intermediate) /* XXX should we print this case also? */ 1946 print_histogram(num_queries_completed); 1947 1948 printf("\n"); 1949 1950 if (countrcodes) { 1951 unsigned int i; 1952 1953 for (i = 0; i < 16; i++) { 1954 if (rcodecounts[i] == 0) 1955 continue; 1956 printf(" Returned %8s: %u queries\n", 1957 rcode_strings[i], rcodecounts[i]); 1958 } 1959 printf("\n"); 1960 } 1961 1962 printf(" Percentage completed: %6.2lf%%\n", per_completed); 1963 if (possibly_delayed > 0) 1964 printf(" (w/ delayed qrys): %6.2lf%%\n", per_completed2); 1965 printf(" Percentage lost: %6.2lf%%\n", per_lost); 1966 if (possibly_delayed > 0) 1967 printf(" (w/o delayed qrys): %6.2lf%%\n", per_lost2); 1968 1969 printf("\n"); 1970 1971 printf(" Started at: %s", 1972 ctime((const time_t *)&start_time.tv_sec)); 1973 printf(" Finished at: %s", 1974 ctime((const time_t *)&end_perf->tv_sec)); 1975 printf(" Ran for: %.6lf seconds\n", run_time); 1976 1977 printf("\n"); 1978 1979 printf(" Queries per second: %.6lf qps\n", queries_per_sec); 1980 if (possibly_delayed > 0) { 1981 printf(" (w/ delayed qrys): %.6lf qps\n", 1982 queries_per_sec2); 1983 } 1984 if (target_qps > 0) { 1985 printf(" Total QPS/target: %.6lf/%d qps\n", 1986 queries_per_sec_total, target_qps); 1987 } 1988 1989 printf("\n"); 1990} 1991 1992void 1993print_interval_statistics() { 1994 struct timeval time_now; 1995 1996 if (use_timelimit == FALSE) 1997 return; 1998 1999 if (setup_phase == TRUE) 2000 return; 2001 2002 if (print_interval == 0) 2003 return; 2004 2005 if (timelimit_reached() == TRUE) 2006 return; 2007 2008 set_timenow(&time_now); 2009 if (difftv(time_now, time_of_first_query_interval) 2010 <= (double)print_interval) 2011 return; 2012 2013 /* Don't count currently outstanding queries */ 2014 num_queries_sent_interval -= queries_outstanding(); 2015 print_statistics(TRUE, num_queries_sent_interval, 2016 num_queries_timed_out_interval, 2017 num_queries_possiblydelayed_interval, 2018 &time_of_first_query_interval, 2019 &time_of_first_query_interval, &time_now, &time_now, 2020 rtt_max_interval, rtt_min_interval, 2021 rtt_total_interval, rtt_overflows_interval, 2022 rttarray_interval); 2023 2024 /* Reset intermediate counters */ 2025 num_queries_sent_interval = 0; 2026 num_queries_timed_out_interval = 0; 2027 num_queries_possiblydelayed_interval = 0; 2028 rtt_max_interval = -1; 2029 rtt_min_interval = -1; 2030 rtt_total_interval = 0.0; 2031 rtt_overflows_interval = 0; 2032 if (rttarray_interval != NULL) { 2033 memset(rttarray_interval, 0, 2034 sizeof(rttarray_interval[0]) * rttarray_size); 2035 } 2036} 2037 2038/* 2039 * queryperf Program Mainline 2040 */ 2041int 2042main(int argc, char **argv) { 2043 int adjust_rate; 2044 int sending = FALSE; 2045 int got_eof = FALSE; 2046 int input_length = MAX_INPUT_LEN; 2047 char input_line[MAX_INPUT_LEN + 1]; 2048 2049 set_timenow(&time_of_program_start); 2050 time_of_first_query.tv_sec = 0; 2051 time_of_first_query.tv_usec = 0; 2052 time_of_first_query_interval.tv_sec = 0; 2053 time_of_first_query_interval.tv_usec = 0; 2054 time_of_end_of_run.tv_sec = 0; 2055 time_of_end_of_run.tv_usec = 0; 2056 2057 input_line[0] = '\0'; 2058 2059 show_startup_info(); 2060 2061 if (setup(argc, argv) == -1) 2062 return (-1); 2063 2064 printf("[Status] Processing input data\n"); 2065 2066 while ((sending = keep_sending(&got_eof)) == TRUE || 2067 queries_outstanding() > 0) { 2068 print_interval_statistics(); 2069 adjust_rate = FALSE; 2070 2071 while ((sending = keep_sending(&got_eof)) == TRUE && 2072 queries_outstanding() < max_queries_outstanding) { 2073 int len = next_input_line(input_line, input_length); 2074 if (len == 0) { 2075 got_eof = TRUE; 2076 } else { 2077 /* Zap the trailing newline */ 2078 if (input_line[len - 1] == '\n') 2079 input_line[len - 1] = '\0'; 2080 2081 /* 2082 * TODO: Should test if we got a whole line 2083 * and flush to the next \n in input if not 2084 * here... Add this later. Only do the next 2085 * few lines if we got a whole line, else 2086 * print a warning. Alternative: Make the 2087 * max line size really big. BAD! :) 2088 */ 2089 2090 if (input_line[0] == CONFIG_CHAR) 2091 update_config(input_line); 2092 else { 2093 send_query(input_line); 2094 if (target_qps > 0 && 2095 (num_queries_sent % 2096 max_queries_outstanding) == 0) { 2097 adjust_rate = TRUE; 2098 } 2099 } 2100 } 2101 } 2102 2103 process_responses(adjust_rate); 2104 retire_old_queries(sending); 2105 } 2106 2107 set_timenow(&time_of_end_of_run); 2108 2109 printf("[Status] Testing complete\n"); 2110 2111 close_socket(); 2112 close_datafile(); 2113 2114 print_statistics(FALSE, num_queries_sent, num_queries_timed_out, 2115 num_queries_possiblydelayed, 2116 &time_of_first_query, &time_of_program_start, 2117 &time_of_end_of_run, &time_of_stop_sending, 2118 rtt_max, rtt_min, rtt_total, rtt_overflows, rttarray); 2119 2120 return (0); 2121} 2122