perftcpdns.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 2013, 2014, 2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* TCP DNS perf tool
*
* main parameters are -r<rate> and <server>
* standard options are 4|6 (IPv4|IPv6), rate computations, terminaisons,
* EDNS0, NOERROR|NXDOMAIN, template (for your own query), diags,
* alternate server port and UDP version.
*
* To help to crush kernels (unfortunately both client and server :-)
* this version of the tool is multi-threaded:
* - the master thread inits, monitors the activity each millisecond,
* and report results when finished
* - the connecting thread computes the date of the next connection,
* creates a socket, makes it non blocking, binds it if wanted,
* connects it and pushes it on the output epoll queue
* - the sending thread gets by epoll connected sockets, timeouts
* embryonic connections, sends queries and pushes sockets on
* the input epoll queue
* - the receiving thread gets by epoll sockets with a pending
* response, receives responses, timeouts unanswered queries,
* and recycles (by closing them) all sockets.
*
* Rate computation details:
* - the target rate is in query+response per second.
* - rating is done by the connecting thread.
* - of course the tool is always late so the target rate is never
* reached. BTW there is no attempt to internally adjust the
* effective rate to the target one: this must be by tuning
* the rate related parameters, first the -r<rate> itself.
* - at the beginning of the connecting thread iteration loop
* (second "loops" counter) the date of the due (aka next) connect()
* call is computed from the last one with 101% of the rate.
* - the due date is compared with the current date (aka now).
* - if the due is before, lateconn counter is incremented, else
* the thread sleeps for the difference,
* - the next step is to reget the current date, if it is still
* before the due date (e.g., because the sleep was interrupted)
* the first shortwait counter is incremented.
* - if it is after (common case) the number of connect calls is
* computed from the difference between now and due divided by rate,
* rounded to the next number,
* - this number of connect() calls is bounded by the -a<aggressiveness>
* parameter to avoid too many back to back new connection attempts.
* - the compconn counter is incremented, errors (other than EINPROGRESS
* from not blocking connect()) are printed. When an error is
* related to a local limit (e.g., EMFILE, EADDRNOTAVAIL or the
* internal ENOMEM) the locallimit counter is incremented.
*/
#ifdef __linux__
#define _GNU_SOURCE
#endif
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* DNS defines */
#define NS_TYPE_A 1
#define NS_TYPE_NS 2
#define NS_TYPE_CNAME 5
#define NS_TYPE_SOA 6
#define NS_TYPE_NULL 10
#define NS_TYPE_PTR 12
#define NS_TYPE_MX 15
#define NS_TYPE_TXT 16
#define NS_TYPE_AAAA 28
#define NS_TYPE_OPT 41
#define NS_TYPE_DS 43
#define NS_TYPE_RRSIG 46
#define NS_TYPE_NSEC 47
#define NS_TYPE_DNSKEY 48
#define NS_TYPE_NSEC3 50
#define NS_TYPE_NSEC3PARAM 51
#define NS_TYPE_TSIG 250
#define NS_TYPE_IXFR 251
#define NS_TYPE_AXFR 252
#define NS_TYPE_ANY 255
#define NS_CLASS_IN 1
#define NS_CLASS_ANY 255
#define NS_OFF_ID 0
#define NS_OFF_FLAGS 2
#define NS_OFF_QDCOUNT 4
#define NS_OFF_ANCOUNT 6
#define NS_OFF_NSCOUNT 8
#define NS_OFF_ARCOUNT 10
#define NS_OFF_QUESTION 12
#define NS_FLAG_QR 0x8000U
#define NS_FLAG_AA 0x0400U
#define NS_FLAG_TC 0x0200U
#define NS_FLAG_RD 0x0100U
#define NS_FLAG_RA 0x0080U
#define NS_FLAG_AD 0x0020U
#define NS_FLAG_CD 0x0010U
#define NS_XFLAG_DO 0x8000U
#define NS_OPCODE_MASK 0x7000U
#define NS_OPCODE_QUERY 0
#define NS_RCODE_MASK 0x000fU
#define NS_RCODE_NOERROR 0
#define NS_RCODE_FORMERR 1
#define NS_RCODE_SERVFAIL 2
#define NS_RCODE_NXDOMAIN 3
#define NS_RCODE_NOIMP 4
#define NS_RCODE_REFUSED 5
#define NS_RCODE_LAST 6
/* chaining macros */
(head) = -1; \
} while (0)
} while (0)
else \
} while (0)
/*
* Data structures
*/
/*
* exchange:
* - per exchange values:
* * order (for debugging)
* * id
* * random (for debugging)
* * time-stamps
*
*/
struct exchange { /* per exchange structure */
int sock; /* socket descriptor */
#define X_FREE 0
#define X_CONN 1
#define X_READY 2
#define X_SENT 3
int state; /* state */
};
int xlast; /* number of exchanges */
int xused; /* next to be used list */
/*
* statictics counters and accumulators
*/
double dmax = 0.; /* maximum delay */
double dsum = 0.; /* delay sum */
double dsumsq = 0.; /* square delay sum */
/*
* command line parameters
*/
int edns0; /* EDNS0 DO flag */
int ipversion = 0; /* IP version */
int rate; /* rate in connections per second */
int noreport; /* disable auto reporting */
int report; /* delay between two reports */
int basecnt; /* base count */
int period; /* test period */
char *localname; /* local address or interface */
int seeded; /* is a seed provided */
unsigned int seed; /* randomization seed */
char *templatefile; /* template file name */
char *diags; /* diagnostic selectors */
char *servername; /* server */
int ixann; /* ixann NXDOMAIN */
int udp; /* use UDP in place of TCP */
/*
* global variables
*/
#ifndef EVENTS_CNT
#define EVENTS_CNT 16
#endif
/*
* template
*/
/*
* threads
*/
/*
* initialize data structures handling exchanges
*/
void
inits(void)
{
int idx;
exit(1);
}
if (epoll_ifd < 0) {
perror("epoll_create(input)");
exit(1);
}
if (epoll_ofd < 0) {
perror("epoll_create(output)");
exit(1);
}
perror("malloc(exchanges)");
exit(1);
}
}
/*
* build a TCP DNS QUERY
*/
void
build_template_query(void)
{
uint8_t *p = template_query;
uint16_t v;
/* flags */
p += NS_OFF_FLAGS;
v = NS_FLAG_RD;
*p++ = v >> 8;
*p++ = v & 0xff;
/* qdcount */
v = 1;
*p++ = v >> 8;
*p++ = v & 0xff;
/* ancount */
v = 0;
*p++ = v >> 8;
*p++ = v & 0xff;
/* nscount */
v = 0;
*p++ = v >> 8;
*p++ = v & 0xff;
/* arcount */
v = edns0;
*p++ = v >> 8;
*p++ = v & 0xff;
/* icann.link (or ixann.link) */
*p++ = 5;
*p++ = 'i';
if (ixann == 0)
*p++ = 'c';
else
*p++ = 'x';
*p++ = 'a';
*p++ = 'n';
*p++ = 'n';
*p++ = 4;
*p++ = 'l';
*p++ = 'i';
*p++ = 'n';
*p++ = 'k';
*p++ = 0;
if (ipversion == 4)
v = NS_TYPE_A;
else
v = NS_TYPE_AAAA;
*p++ = v >> 8;
*p++ = v & 0xff;
/* class IN */
v = NS_CLASS_IN;
*p++ = v >> 8;
*p++ = v & 0xff;
/* EDNS0 OPT with DO */
if (edns0) {
/* root name */
*p++ = 0;
/* type OPT */
v = NS_TYPE_OPT;
*p++ = v >> 8;
*p++ = v & 0xff;
/* class UDP length */
v = 4096;
*p++ = v >> 8;
*p++ = v & 0xff;
/* extended rcode 0 */
*p++ = 0;
/* version 0 */
*p++ = 0;
/* extended flags DO */
v = NS_XFLAG_DO;
*p++ = v >> 8;
*p++ = v & 0xff;
/* rdlength */
v = 0;
*p++ = v >> 8;
*p++ = v & 0xff;
}
/* length */
length_query = p - template_query;
}
/*
* get a TCP DNS client QUERY template
* from the file given in the command line (-T<template-file>)
* and rnd offset (-O<random-offset>)
*/
void
get_template_query(void)
{
uint8_t *p = template_query;
if (fd < 0) {
exit(2);
}
if (cc < 0) {
exit(1);
}
exit(2);
}
if (cc > 4096) {
exit(2);
}
j = 0;
for (i = 0; i < cc; i++) {
continue;
"illegal char[%d]='%c' in file '%s'\n",
i, (int) tbuf[i], templatefile);
exit(2);
}
j++;
}
cc = j;
if ((cc & 1) != 0) {
"odd number of hexadecimal digits in file '%s'\n",
exit(2);
}
for (i = 0; i < cc; i += 2)
if (rndoffset >= 0)
if (random_query > length_query) {
"random (at %zu) outside the template (length %zu)?\n",
exit(2);
}
}
#if 0
/*
* randomize the value of the given field:
* - offset of the field
* - random seed (used as it when suitable)
* - returns the random value which was used
*/
{
uint32_t v;
if (range == 0)
return 0;
if (range == UINT32_MAX)
return r;
if (maxrandom != 0)
while (r >= maxrandom)
r %= range + 1;
v = r;
if (v < 256)
return r;
v >>= 8;
if (v < 256)
return r;
v >>= 8;
if (v < 256)
return r;
v >>= 8;
return r;
}
#endif
/*
*/
void
flushconnect(void)
{
struct exchange *x;
int cnt = 10;
double waited;
perror("clock_gettime(flushconnect)");
fatal = 1;
return;
}
while (--cnt >= 0) {
if (idx < 0)
return;
abort();
/* check for a timed-out connection */
return;
/* garbage collect timed-out connections */
if (pthread_mutex_lock(&mtxconn) != 0) {
fatal = 1;
return;
}
ISC_REMOVE(xconnl, x);
if (pthread_mutex_unlock(&mtxconn) != 0) {
fatal = 1;
return;
}
x->sock = -1;
collconn++;
if (pthread_mutex_lock(&mtxfree) != 0) {
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxfree) != 0) {
fatal = 1;
return;
}
}
}
/*
* poll connected
*/
void
pollconnect(int topoll)
{
struct exchange *x;
continue;
continue;
if (pthread_mutex_lock(&mtxconn) != 0) {
fatal = 1;
return;
}
ISC_REMOVE(xconnl, x);
if (pthread_mutex_unlock(&mtxconn) != 0) {
fatal = 1;
return;
}
(err != 0)) {
x->sock = -1;
badconn++;
if (pthread_mutex_lock(&mtxfree) != 0) {
"pthread_mutex_lock(pollconnect)");
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxfree) != 0) {
"pthread_mutex_unlock(pollconnect)");
fatal = 1;
return;
}
continue;
}
}
}
/*
* send the TCP DNS QUERY
*/
int
{
if (udp)
off = 0;
else {
off = 2;
/* message length */
}
/* message from template */
/* ID */
#if 0
/* random */
if (random_query > 0)
#endif
/* timestamp */
errno = 0;
if (ret < 0) {
perror("clock_gettime(send)");
fatal = 1;
return -errno;
}
return 0;
return -errno;
}
/*
* poll ready and send
*/
void
pollsend(void)
{
struct exchange *x;
struct epoll_event ev;
for (;;) {
if (idx < 0)
return;
ISC_REMOVE(xreadyl, x);
if (sendquery(x) < 0) {
x->sock = -1;
badsent++;
if (pthread_mutex_lock(&mtxfree) != 0) {
"pthread_mutex_lock(pollsend)");
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxfree) != 0) {
"pthread_mutex_unlock(pollsend)");
fatal = 1;
return;
}
continue;
}
xscount++;
if (pthread_mutex_lock(&mtxsent) != 0) {
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxsent) != 0) {
fatal = 1;
return;
}
perror("epoll_ctl(add input)");
fatal = 1;
return;
}
}
}
/*
* receive a TCP DNS RESPONSE
*/
void
receiveresp(struct exchange *x)
{
uint16_t v;
double delta;
if (cc < 0) {
(errno == EWOULDBLOCK) ||
(errno == ECONNRESET)) {
recverr++;
return;
}
perror("recv");
fatal = 1;
return;
}
if (udp)
off = 0;
else
off = 2;
/* enforce a reasonable length */
tooshort++;
return;
}
/* must match the ID */
badid++;
return;
}
/* must be a response */
v = ntohs(v);
if ((v & NS_FLAG_QR) == 0) {
notresp++;
return;
}
perror("clock_gettime(receive)");
fatal = 1;
return;
}
/* got it: update stats */
xrcount++;
v &= NS_RCODE_MASK;
if (v >= NS_RCODE_LAST)
v = NS_RCODE_LAST;
rcodes[v] += 1;
}
/*
*/
void
flushrecv(void)
{
struct exchange *x;
int cnt = 5;
double waited;
perror("clock_gettime(receive)");
fatal = 1;
return;
}
while (--cnt >= 0) {
if (idx < 0)
return;
abort();
/* check for a timed-out exchange */
return;
/* garbage collect timed-out exchange */
if (pthread_mutex_lock(&mtxsent) != 0) {
fatal = 1;
return;
}
ISC_REMOVE(xsentl, x);
if (pthread_mutex_unlock(&mtxsent) != 0) {
fatal = 1;
return;
}
x->sock = -1;
collsent++;
if (pthread_mutex_lock(&mtxfree) != 0) {
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxfree) != 0) {
fatal = 1;
return;
}
}
}
/*
* poll receive
*/
void
{
struct exchange *x;
continue;
continue;
if (pthread_mutex_lock(&mtxsent) != 0) {
fatal = 1;
return;
}
ISC_REMOVE(xsentl, x);
if (pthread_mutex_unlock(&mtxsent) != 0) {
fatal = 1;
return;
}
receiveresp(x);
x->sock = -1;
if (pthread_mutex_lock(&mtxfree) != 0) {
fatal = 1;
return;
}
if (pthread_mutex_unlock(&mtxfree) != 0) {
fatal = 1;
return;
}
}
}
/*
* get the TCP DNS socket descriptor (IPv4)
*/
int
getsock4(void)
{
int sock;
int flags;
errno = 0;
if (udp)
else
if (sock < 0)
return -errno;
/* make the socket descriptor not blocking */
if (flags == -1) {
return -errno;
}
return -errno;
}
/* bind if wanted */
if (curport) {
struct sockaddr_in *l4;
curport++;
}
sizeof(struct sockaddr_in)) < 0) {
return -errno;
}
}
/* connect */
(struct sockaddr *) &serveraddr,
sizeof(struct sockaddr_in)) < 0) {
if (errno != EINPROGRESS) {
return -errno;
}
}
return sock;
}
/*
* connect the TCP DNS QUERY (IPv4)
*/
int
connect4(void)
{
struct exchange *x;
int ret;
int idx;
struct epoll_event ev;
if (ret < 0) {
perror("clock_gettime(connect)");
fatal = 1;
return -errno;
}
if (xfree >= 0) {
if (ret != 0) {
fatal = 1;
return -ret;
}
ISC_REMOVE(xfreel, x);
if (ret != 0) {
fatal = 1;
return -ret;
}
xused++;
} else
return -ENOMEM;
abort();
memset(x, 0, sizeof(*x));
x->next = -1;
if (x->sock < 0) {
x->sock = -1;
if (ret != 0) {
fatal = 1;
return -ret;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
return result;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
perror("epoll_ctl(add output)");
fatal = 1;
return -errno;
}
#if 0
if (random_query > 0)
#endif
return idx;
}
/*
* get the TCP DNS socket descriptor (IPv6)
*/
int
getsock6(void)
{
int sock;
int flags;
errno = 0;
if (udp)
else
if (sock < 0)
return -errno;
/* make the socket descriptor not blocking */
if (flags == -1) {
return -errno;
}
return -errno;
}
/* bind if wanted */
if (curport) {
struct sockaddr_in6 *l6;
curport++;
}
sizeof(struct sockaddr_in6)) < 0) {
return -errno;
}
}
/* connect */
(struct sockaddr *) &serveraddr,
sizeof(struct sockaddr_in6)) < 0) {
if (errno != EINPROGRESS) {
return -errno;
}
}
return sock;
}
/*
* connect the TCP DNS QUERY (IPv6)
*/
int
connect6(void)
{
struct exchange *x;
int ret;
int idx;
struct epoll_event ev;
if (ret < 0) {
perror("clock_gettime(connect)");
fatal = 1;
return -errno;
}
if (xfree >= 0) {
if (ret != 0) {
fatal = 1;
return -ret;
}
ISC_REMOVE(xfreel, x);
if (ret != 0) {
fatal = 1;
return -ret;
}
xused++;
} else
return -ENOMEM;
memset(x, 0, sizeof(*x));
x->next = -1;
if (x->sock < 0) {
x->sock = -1;
if (ret != 0) {
fatal = 1;
return -ret;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
return result;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
if (ret != 0) {
fatal = 1;
return -ret;
}
perror("epoll_ctl(add output)");
fatal = 1;
return -errno;
}
#if 0
if (random_query > 0)
#endif
return idx;
}
/*
* connector working routine
*/
void *
connecting(void *dummy)
{
int ret;
int i;
char name[16];
/* set conn-name */
if (ret < 0)
perror("prctl(PR_GET_NAME)");
else {
if (ret < 0)
perror("prctl(PR_SET_NAME");
}
for (;;) {
if (fatal)
break;
loops[1]++;
/* compute the delay for the next connection */
perror("clock_gettime(connecting)");
fatal = 1;
break;
}
if (rate == 1)
else
}
}
/* the connection was already due? */
lateconn++;
} else {
/* wait until */
if (ret != 0) {
continue;
fatal = 1;
break;
}
}
/* compute how many connections to open */
perror("clock_gettime(connecting)");
fatal = 1;
break;
}
double toconnect;
toconnect++;
if (toconnect > (double) aggressiveness)
i = aggressiveness;
else
i = (int) toconnect;
compconn += i;
/* open connections */
while (i-- > 0) {
if (ipversion == 4)
else
if (ret < 0) {
(ret == -EWOULDBLOCK) ||
(ret == -EADDRNOTAVAIL) ||
locallimit++;
"connect: %s\n",
break;
}
}
} else
/* there was no connection to open */
shortwait[0]++;
}
return NULL;
}
/*
* sender working routine
*/
void *
{
int ret;
int nfds;
char name[16];
/* set send-name */
if (ret < 0)
perror("prctl(PR_GET_NAME)");
else {
if (ret < 0)
perror("prctl(PR_SET_NAME");
}
for (;;) {
if (fatal)
break;
loops[2]++;
/* epoll_wait() */
if (nfds < 0) {
continue;
perror("epoll_wait(output)");
fatal = 1;
break;
}
/* connection(s) to finish */
if (nfds == 0)
shortwait[1]++;
else
if (fatal)
break;
flushconnect();
if (fatal)
break;
/* packet(s) to send */
pollsend();
if (fatal)
break;
}
return NULL;
}
/*
* receiver working routine
*/
void *
{
int ret;
int nfds;
char name[16];
/* set recv-name */
if (ret < 0)
perror("prctl(PR_GET_NAME)");
else {
if (ret < 0)
perror("prctl(PR_SET_NAME");
}
for (;;) {
if (fatal)
break;
loops[3]++;
/* epoll_wait() */
if (nfds < 0) {
continue;
perror("epoll_wait(input)");
fatal = 1;
break;
}
/* packet(s) to receive */
if (nfds == 0)
shortwait[2]++;
else
if (fatal)
break;
flushrecv();
if (fatal)
break;
}
return NULL;
}
/*
* get the server socket address from the command line:
* - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
*/
void
getserveraddr(const int flags)
{
int ret;
if (ipversion == 4)
else
if (udp) {
} else {
}
if (ret != 0) {
exit(2);
}
exit(2);
}
if (ipversion == 4)
else
}
/*
* get the local socket address from the command line
*/
void
getlocaladdr(void)
{
int ret;
if (ipversion == 4)
else
if (udp) {
} else {
}
if (ret != 0) {
"bad -l<local-addr=%s>: %s\n",
gai_strerror(ret));
exit(2);
}
/* refuse multiple addresses */
"ambiguous -l<local-addr=%s>\n",
exit(2);
}
}
/*
* intermediate reporting
* (note: an in-transit packet can be reported as lost)
*/
void
reporting(void)
{
if (xccount != 0) {
printf("connect: %llu, sent: %llu, received: %llu "
"(embryonics: %lld, drops: %lld)",
(unsigned long long) xccount,
(unsigned long long) xscount,
(unsigned long long) xrcount,
if (xrcount != 0) {
double avg;
}
}
printf("\n");
}
/*
* SIGCHLD handler
*/
void
{
int status;
/* continue */;
}
/*
* SIGINT handler
*/
void
{
interrupted = 1;
}
/*
* SIGTERM handler
*/
void
{
fatal = 1;
}
/*
* '-v' handler
*/
void
version(void)
{
}
/*
* usage (from the wiki)
*/
void
usage(void)
{
"perftcpdns [-huvX0] [-4|-6] [-r<rate>] [-t<report>] [-p<test-period>]\n"
" [-n<num-request>]* [-d<lost-time>]* [-D<max-loss>]* [-T<template-file>]\n"
" [-l<local-addr>] [-L<local-port>]* [-a<aggressiveness>] [-s<seed>]\n"
" [-M<memory>] [-x<diagnostic-selector>] [-P<port>] server\n"
"\f\n"
"\n"
"Options:\n"
"-0: Add EDNS0 option with DO flag.\n"
"-a<aggressiveness>: When the target sending rate is not yet reached,\n"
" control how many connections are initiated before the next pause.\n"
"-d<lost-time>: Specify the time after which a connection or a query is\n"
" treated as having been lost. The value is given in seconds and\n"
" may contain a fractional component. The default is 1 second.\n"
"-h: Print this help.\n"
" communicating with the server.\n"
"-L<local-port>: Specify the (minimal and maximal) local port number\n"
"-M<memory>: Size of the tables (default 60000)\n"
"-P<port>: Specify an alternate (i.e., not 53) port\n"
"-r<rate>: Initiate <rate> TCP DNS connections per second. A periodic\n"
" report is generated showing the number of exchanges which were not\n"
" completed, as well as the average response latency. The program\n"
" continues until interrupted, at which point a final report is\n"
" generated.\n"
"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
"-t<report>: Delay in seconds between two periodic reports.\n"
"-T<template-file>: The name of a file containing the template to use\n"
" as a stream of hexadecimal digits.\n"
"-u: Use UDP in place of TCP.\n"
"-v: Report the version number of this program.\n"
"-X: change default template to get NXDOMAIN responses.\n"
"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
" <diagnostic-selector> is a string of single-keywords specifying\n"
" the operations for which verbose output is desired. The selector\n"
" keyletters are:\n"
" * 'a': print the decoded command line arguments\n"
" * 'e': print the exit reason\n"
" * 'i': print rate processing details\n"
" * 'T': when finished, print templates\n"
"\n"
"Stopping conditions:\n"
"-D<max-loss>: Abort the test if more than <max-loss> connections or\n"
" queries have been lost. If <max-loss> includes the suffix '%', it\n"
" specifies a maximum percentage of losses before stopping.\n"
" In this case, testing of the threshold begins after 10\n"
" connections/responses have been expected to be accepted/received.\n"
"-n<num-request>: Initiate <num-request> transactions. No report is\n"
" generated until all transactions have been initiated/waited-for,\n"
" after which a report is generated and the program terminates.\n"
"-p<test-period>: Send requests for the given test period, which is\n"
" specified in the same manner as -d. This can be used as an\n"
" alternative to -n, or both options can be given, in which case the\n"
" testing is completed when either limit is reached.\n"
"\n"
"Errors:\n"
"- locallimit: reached to local system limits when sending a message.\n"
"- badconn: connection failed (from getsockopt(SO_ERROR))\n"
"- collconn: connect() timed out\n"
"- badsent: send() failed\n"
"- callsent: timed out waiting from a response\n"
"- recverr: recv() system call failed\n"
"- tooshort: received a too short message\n"
"- badid: the id mismatches between the query and the response\n"
"- notresp: doesn't receive a response\n"
"Rate stats:\n"
"- loops: number of thread loop iterations\n"
"- shortwait: no direct activity in a thread iteration\n"
"- compconn: computed number of connect() calls\n"
"- lateconn: connect() already dued when computing delay to the next one\n"
"\n"
"Exit status:\n"
"The exit status is:\n"
"0 on complete success.\n"
"1 for a general error.\n"
"2 if an error is found in the command line arguments.\n"
"3 if there are no general failures in operation, but one or more\n"
" exchanges are not successfully completed.\n");
}
/*
* main function / entry point
*/
int
{
long long r;
char *pc;
double d;
extern char *optarg;
extern int optind;
#define OPTIONS "hv46u0XM:r:t:R:b:n:p:d:D:l:L:a:s:T:O:x:P:"
/* decode options */
switch (opt) {
case 'h':
usage();
exit(0);
case 'u':
udp = 1;
break;
case 'v':
version();
exit(0);
case '0':
edns0 = 1;
break;
case '4':
if (ipversion == 6) {
usage();
exit(2);
}
ipversion = 4;
break;
case '6':
if (ipversion == 4) {
usage();
exit(2);
}
ipversion = 6;
break;
case 'X':
ixann = 1;
break;
case 'M':
if (xlast <= 1000) {
usage();
exit(2);
}
break;
case 'r':
if (rate <= 0) {
usage();
exit(2);
}
break;
case 't':
if (report <= 0) {
usage();
exit(2);
}
break;
case 'R':
if (r < 0) {
"range must not be a negative integer\n");
usage();
exit(2);
}
m = (b / s) * s;
if (m == b)
maxrandom = 0;
else
}
break;
case 'b':
if (basecnt > 1) {
usage();
exit(2);
}
/* decodebase(); */
basecnt++;
break;
case 'n':
noreport = 1;
gotnumreq++;
if (gotnumreq > 1) {
usage();
exit(2);
}
"num-request must be a positive integer\n");
usage();
exit(2);
}
break;
case 'p':
noreport = 1;
if (period <= 0) {
"test-period must be a positive integer\n");
usage();
exit(2);
}
break;
case 'd':
gotlosttime++;
if (gotlosttime > 1) {
usage();
exit(2);
}
if ((d < 0.) || ((d == 0.) && (gotlosttime == 1))) {
"lost-time must be a positive number\n");
usage();
exit(2);
}
if (d > 0.)
losttime[gotlosttime] = d;
break;
case 'D':
noreport = 1;
gotmaxloss++;
if (gotmaxloss > 1) {
usage();
exit(2);
}
*pc = '\0';
if ((maxploss[gotmaxloss] < 0) ||
"invalid max-loss percentage\n");
usage();
exit(2);
}
} else {
if ((maxloss[gotmaxloss] < 0) ||
((maxloss[gotmaxloss] == 0) &&
(gotmaxloss == 1))) {
"max-loss must be a "
"positive integer\n");
usage();
exit(2);
}
}
break;
case 'l':
break;
case 'L':
if ((i <= 0) || (i >65535)) {
"local-port must be a small positive integer\n");
usage();
exit(2);
}
if (maxport != 0) {
usage();
exit(2);
}
if (curport == 0)
else
maxport = i;
break;
case 'a':
if (aggressiveness <= 0) {
"aggressiveness must be a positive integer\n");
usage();
exit(2);
}
break;
case 's':
seeded = 1;
break;
case 'T':
if (templatefile != NULL) {
usage();
exit(2);
}
break;
case 'O':
if (rndoffset < 14) {
"random-offset must be greater than 14\n");
usage();
exit(2);
}
break;
case 'x':
break;
case 'P':
if ((i <= 0) || (i > 65535)) {
"port must be a positive short integer\n");
usage();
exit(2);
}
break;
default:
usage();
exit(2);
}
/* adjust some global variables */
if (ipversion == 0)
ipversion = 4;
if (rate == 0)
rate = 100;
if (xlast == 0)
xlast = 60000;
if (noreport == 0)
report = 1;
maxport = 65535;
/* when required, print the internal view of the command line */
if (udp)
printf("UDP ");
if (edns0 != 0)
printf(" EDNS0");
if (report != 0)
if (range != 0) {
printf(" range=0..%d [0x%x]",
(unsigned int) maxrandom);
else
}
if (basecnt != 0)
for (i = 0; i < basecnt; i++)
if (gotnumreq >= 0) {
printf(" num-request=%d,%d",
}
if (period != 0)
if (gotmaxloss == 0) {
if (maxloss[0] != 0)
if (maxploss[0] != 0.)
} else if (gotmaxloss == 1) {
if (maxloss[0] != 0)
else if (maxploss[0] != 0.)
else
printf(" max-loss=*,");
if (maxloss[1] != 0)
else if (maxploss[1] != 0.)
else
printf("*");
}
if (seeded)
if (templatefile != NULL)
else if (ixann != 0)
printf(" Xflag");
if (rndoffset >= 0)
printf("\n");
}
/* check local address options */
"-l<local-addr> must be set to use -L<local-port>\n");
usage();
exit(2);
}
/* check template file options */
"-T<template-file> must be set to "
"use -O<random-offset>\n");
usage();
exit(2);
}
/* check various template file(s) and other condition(s) options */
"-O<random-offset> must be set when "
"-T<template-file> and -R<range> are used\n");
usage();
exit(2);
}
/* get the server argument */
usage();
exit(2);
}
/* given */
getlocaladdr();
if (curport != 0)
printf(" local-port='%d..%d'",
printf("\n");
}
}
/* get the server socket address */
if (servername == NULL) {
usage();
exit(2);
}
char addr[NI_MAXHOST];
sizeof(localaddr),
addr,
NULL,
0,
if (ret != 0) {
"can't get the local address: %s\n",
gai_strerror(ret));
exit(1);
}
}
/* initialize exchange structures */
inits();
/* get the socket descriptor and template(s) */
if (templatefile == NULL)
else
/* boot is done! */
perror("clock_gettime(boot)");
exit(1);
}
/* compute the next intermediate reporting date */
if (report != 0) {
}
/* seed the random generator */
if (seeded == 0)
/* required only before the interrupted flag check */
/* threads */
master = pthread_self();
if (ret != 0) {
exit(1);
}
if (ret != 0) {
exit(1);
}
if (ret != 0) {
exit(1);
}
/* main loop */
for (;;) {
/* immediate loop exit conditions */
if (interrupted) {
printf("interrupted\n");
break;
}
if (fatal) {
printf("got a fatal error\n");
break;
}
loops[0]++;
/* get the date and use it */
perror("clock_gettime(now)");
fatal = 1;
continue;
}
if ((period != 0) &&
printf("reached test-period\n");
break;
}
if ((report != 0) &&
reporting();
/* check receive loop exit conditions */
printf("reached num-connection\n");
break;
}
printf("reached num-query\n");
break;
}
if ((maxloss[0] != 0) &&
printf("reached max-loss "
"(connection/absolute)\n");
break;
}
if ((maxloss[1] != 0) &&
printf("reached max-loss "
break;
}
if ((maxploss[0] != 0.) &&
(xccount > 10) &&
printf("reached max-loss "
"(connection/percent)\n");
break;
}
if ((maxploss[1] != 0.) &&
(xscount > 10) &&
printf("reached max-loss "
break;
}
/* waiting 1ms */
}
/* after main loop: finished */
perror("clock_gettime(finished)");
/* threads */
(void) pthread_cancel(connector);
(void) pthread_cancel(sender);
(void) pthread_cancel(receiver);
/* main statictics */
printf("connect: %llu, sent: %llu, received: %llu\n",
(unsigned long long) xccount,
(unsigned long long) xscount,
(unsigned long long) xrcount);
printf("embryonics: %lld (%.1f%%)\n",
printf("drops: %lld (%.1f%%)\n",
printf("total losses: %lld (%.1f%%)\n",
printf("local limits: %llu, bad connects: %llu, "
"connect timeouts: %llu\n",
(unsigned long long) locallimit,
(unsigned long long) badconn,
(unsigned long long) collconn);
printf("bad sends: %llu, bad recvs: %llu, recv timeouts: %llu\n",
(unsigned long long) badsent,
(unsigned long long) recverr,
(unsigned long long) collsent);
printf("too shorts: %llu, bad IDs: %llu, not responses: %llu\n",
(unsigned long long) tooshort,
(unsigned long long) badid,
(unsigned long long) notresp);
printf("rcode counters:\n noerror: %llu, formerr: %llu, "
"servfail: %llu\n "
"nxdomain: %llu, noimp: %llu, refused: %llu, others: %llu\n",
(unsigned long long) rcodes[NS_RCODE_NOERROR],
(unsigned long long) rcodes[NS_RCODE_FORMERR],
(unsigned long long) rcodes[NS_RCODE_SERVFAIL],
(unsigned long long) rcodes[NS_RCODE_NXDOMAIN],
(unsigned long long) rcodes[NS_RCODE_NOIMP],
(unsigned long long) rcodes[NS_RCODE_REFUSED],
(unsigned long long) rcodes[NS_RCODE_LAST]);
/* print the rates */
printf("rates: %.0f,%.0f,%.0f (target %d)\n",
}
/* rate processing instrumentation */
printf("loops: %llu,%llu,%llu,%llu\n",
(unsigned long long) loops[0],
(unsigned long long) loops[1],
(unsigned long long) loops[2],
(unsigned long long) loops[3]);
printf("shortwait: %llu,%llu,%llu\n",
(unsigned long long) shortwait[0],
(unsigned long long) shortwait[1],
(unsigned long long) shortwait[2]);
printf("compconn: %llu, lateconn: %llu\n",
(unsigned long long) compconn,
(unsigned long long) lateconn);
printf("badconn: %llu, collconn: %llu, "
"recverr: %llu, collsent: %llu\n",
(unsigned long long) badconn,
(unsigned long long) collconn,
(unsigned long long) recverr,
(unsigned long long) collsent);
printf("memory: used(%d) / allocated(%d)\n",
}
/* round-time trip statistics */
if (xrcount != 0) {
}
printf("\n");
/* template(s) */
size_t n;
if (random_query > 0)
printf("content:\n");
for (n = 0; n < length_query; n++) {
printf("%s%02hhx",
(n & 15) == 0 ? "" : " ",
template_query[n]);
if ((n & 15) == 15)
printf("\n");
}
if ((n & 15) != 15)
printf("\n");
printf("\n");
}
/* compute the exit code (and exit) */
if (fatal)
exit(1);
exit(0);
else
exit(3);
}