1 /* $NetBSD: perftcpdns.c,v 1.3 2014/12/10 04:37:56 christos Exp $ */
4 * Copyright (C) 2013, 2014 Internet Systems Consortium, Inc. ("ISC")
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.
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.
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.
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.
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.
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>
79 #include <netinet/in.h>
80 #include <arpa/inet.h>
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
119 #define NS_CLASS_IN 1
120 #define NS_CLASS_ANY 255
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
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
138 #define NS_XFLAG_DO 0x8000U
140 #define NS_OPCODE_MASK 0x7000U
141 #define NS_OPCODE_QUERY 0
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
152 /* chaining macros */
154 #define ISC_INIT(head, headl) do { \
157 } while (/*CONSTCOND*/0)
159 #define ISC_INSERT(head, headl, elm) do { \
161 (elm)->prev = (headl); \
162 *(headl) = (elm) - xlist; \
163 (headl) = &((elm)->next); \
164 } while (/*CONSTCOND*/0)
166 #define ISC_REMOVE(headl, elm) do { \
167 if ((elm)->next != -1) \
168 xlist[(elm)->next].prev = (elm)->prev; \
170 (headl) = (elm)->prev; \
171 *(elm)->prev = (elm)->next; \
172 } while (/*CONSTCOND*/0)
180 * - per exchange values:
181 * * order (for debugging)
183 * * random (for debugging)
186 * sent/rcvd chain, "next to be received" on entry cache.
189 struct exchange
{ /* per exchange structure */
190 int sock
; /* socket descriptor */
191 int next
, *prev
; /* chaining */
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 */
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 */
214 * statictics counters and accumulators
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 */
229 * command line parameters
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 */
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 */
269 int epoll_ifd
, epoll_ofd
; /* epoll file descriptors */
271 #define EVENTS_CNT 16
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 */
277 uint8_t obuf
[4098], ibuf
[4098]; /* I/O buffers */
278 char tbuf
[4098]; /* template buffer */
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 */
291 uint8_t template_query
[4096];
298 pthread_t master
, connector
, sender
, receiver
;
301 * initialize data structures handling exchanges
309 ISC_INIT(xconn
, xconnl
);
310 ISC_INIT(xready
, xreadyl
);
311 ISC_INIT(xsent
, xsentl
);
312 ISC_INIT(xfree
, xfreel
);
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");
321 epoll_ifd
= epoll_create(EVENTS_CNT
);
323 perror("epoll_create(input)");
326 epoll_ofd
= epoll_create(EVENTS_CNT
);
328 perror("epoll_create(output)");
332 xlist
= (struct exchange
*) malloc(xlast
* sizeof(struct exchange
));
334 perror("malloc(exchanges)");
337 memset(xlist
, 0, xlast
* sizeof(struct exchange
));
339 for (idx
= 0; idx
< xlast
; idx
++)
340 xlist
[idx
].sock
= xlist
[idx
].next
= -1;
344 * build a TCP DNS QUERY
348 build_template_query(void)
350 uint8_t *p
= template_query
;
374 /* icann.link (or ixann.link) */
401 /* EDNS0 OPT with DO */
409 /* class UDP length */
413 /* extended rcode 0 */
417 /* extended flags DO */
427 length_query
= p
- template_query
;
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>)
437 get_template_query(void)
439 uint8_t *p
= template_query
;
442 fd
= open(templatefile
, O_RDONLY
);
444 fprintf(stderr
, "open(%s): %s\n",
445 templatefile
, strerror(errno
));
448 cc
= read(fd
, tbuf
, sizeof(tbuf
));
451 fprintf(stderr
, "read(%s): %s\n",
452 templatefile
, strerror(errno
));
455 if (cc
< NS_OFF_QUESTION
+ 6) {
456 fprintf(stderr
,"file '%s' too small\n", templatefile
);
460 fprintf(stderr
,"file '%s' too large\n", templatefile
);
464 for (i
= 0; i
< cc
; i
++) {
465 if (isspace((int) tbuf
[i
]))
467 if (!isxdigit((int) tbuf
[i
])) {
469 "illegal char[%d]='%c' in file '%s'\n",
470 i
, (int) tbuf
[i
], templatefile
);
479 "odd number of hexadecimal digits in file '%s'\n",
483 length_query
= cc
>> 1;
484 for (i
= 0; i
< cc
; i
+= 2)
485 (void) sscanf(tbuf
+ i
, "%02hhx", &p
[i
>> 1]);
487 random_query
= (size_t) rndoffset
;
488 if (random_query
> length_query
) {
490 "random (at %zu) outside the template (length %zu)?\n",
491 random_query
, length_query
);
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
505 randomize(size_t offset
, uint32_t r
)
511 if (range
== UINT32_MAX
)
514 while (r
>= maxrandom
)
515 r
= (uint32_t) random();
523 v
+= obuf
[offset
- 1];
524 obuf
[offset
- 1] = v
;
528 v
+= obuf
[offset
- 2];
529 obuf
[offset
- 2] = v
;
533 v
+= obuf
[offset
- 3];
534 obuf
[offset
- 3] = v
;
540 * flush/timeout connect
552 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
553 perror("clock_gettime(flushconnect)");
555 (void) pthread_kill(master
, SIGTERM
);
564 if (x
->state
!= X_CONN
)
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])
571 /* garbage collect timed-out connections */
572 if (pthread_mutex_lock(&mtxconn
) != 0) {
573 fprintf(stderr
, "pthread_mutex_lock(flushconnect)");
575 (void) pthread_kill(master
, SIGTERM
);
578 ISC_REMOVE(xconnl
, x
);
579 if (pthread_mutex_unlock(&mtxconn
) != 0) {
580 fprintf(stderr
, "pthread_mutex_unlock(flushconnect)");
582 (void) pthread_kill(master
, SIGTERM
);
585 (void) close(x
->sock
);
588 if (pthread_mutex_lock(&mtxfree
) != 0) {
589 fprintf(stderr
, "pthread_mutex_lock(flushconnect)");
591 (void) pthread_kill(master
, SIGTERM
);
595 ISC_INSERT(xfree
, xfreel
, x
);
596 if (pthread_mutex_unlock(&mtxfree
) != 0) {
597 fprintf(stderr
, "pthread_mutex_unlock(flushconnect)");
599 (void) pthread_kill(master
, SIGTERM
);
610 pollconnect(int topoll
)
614 socklen_t len
= sizeof(int);
616 for (evn
= 0; evn
< topoll
; evn
++) {
617 idx
= oevents
[evn
].data
.fd
;
619 if (x
->state
!= X_CONN
)
621 if (oevents
[evn
].events
== 0)
623 if (pthread_mutex_lock(&mtxconn
) != 0) {
624 fprintf(stderr
, "pthread_mutex_lock(pollconnect)");
626 (void) pthread_kill(master
, SIGTERM
);
629 ISC_REMOVE(xconnl
, x
);
630 if (pthread_mutex_unlock(&mtxconn
) != 0) {
631 fprintf(stderr
, "pthread_mutex_unlock(pollconnect)");
633 (void) pthread_kill(master
, SIGTERM
);
636 oevents
[evn
].events
= 0;
637 if ((getsockopt(x
->sock
, SOL_SOCKET
, SO_ERROR
,
640 (void) close(x
->sock
);
643 if (pthread_mutex_lock(&mtxfree
) != 0) {
645 "pthread_mutex_lock(pollconnect)");
647 (void) pthread_kill(master
, SIGTERM
);
651 ISC_INSERT(xfree
, xfreel
, x
);
652 if (pthread_mutex_unlock(&mtxfree
) != 0) {
654 "pthread_mutex_unlock(pollconnect)");
656 (void) pthread_kill(master
, SIGTERM
);
662 ISC_INSERT(xready
, xreadyl
, x
);
667 * send the TCP DNS QUERY
671 sendquery(struct exchange
*x
)
681 obuf
[0] = length_query
>> 8;
682 obuf
[1]= length_query
& 0xff;
684 /* message from template */
685 memcpy(obuf
+ off
, template_query
, length_query
);
687 memcpy(obuf
+ off
+ NS_OFF_ID
, &x
->id
, 2);
690 if (random_query
> 0)
691 x
->rnd
= randomize(random_query
+ off
, x
->rnd
);
695 ret
= clock_gettime(CLOCK_REALTIME
, &x
->ts2
);
697 perror("clock_gettime(send)");
699 (void) pthread_kill(master
, SIGTERM
);
702 ret
= send(x
->sock
, obuf
, length_query
+ off
, 0);
703 if ((size_t) ret
== length_query
+ off
)
709 * poll ready and send
717 struct epoll_event ev
;
719 memset(&ev
, 0, sizeof(ev
));
720 ev
.events
= EPOLLIN
| EPOLLET
| EPOLLONESHOT
;
727 ISC_REMOVE(xreadyl
, x
);
728 if (sendquery(x
) < 0) {
729 (void) close(x
->sock
);
732 if (pthread_mutex_lock(&mtxfree
) != 0) {
734 "pthread_mutex_lock(pollsend)");
736 (void) pthread_kill(master
, SIGTERM
);
740 ISC_INSERT(xfree
, xfreel
, x
);
741 if (pthread_mutex_unlock(&mtxfree
) != 0) {
743 "pthread_mutex_unlock(pollsend)");
745 (void) pthread_kill(master
, SIGTERM
);
751 if (pthread_mutex_lock(&mtxsent
) != 0) {
752 fprintf(stderr
, "pthread_mutex_lock(pollsend)");
754 (void) pthread_kill(master
, SIGTERM
);
758 ISC_INSERT(xsent
, xsentl
, x
);
759 if (pthread_mutex_unlock(&mtxsent
) != 0) {
760 fprintf(stderr
, "pthread_mutex_unlock(pollsend)");
762 (void) pthread_kill(master
, SIGTERM
);
765 if (epoll_ctl(epoll_ifd
, EPOLL_CTL_ADD
, x
->sock
, &ev
) < 0) {
766 perror("epoll_ctl(add input)");
768 (void) pthread_kill(master
, SIGTERM
);
775 * receive a TCP DNS RESPONSE
779 receiveresp(struct exchange
*x
)
787 cc
= recv(x
->sock
, ibuf
, sizeof(ibuf
), 0);
789 if ((errno
== EAGAIN
) ||
790 (errno
== EWOULDBLOCK
) ||
792 (errno
== ECONNRESET
)) {
798 (void) pthread_kill(master
, SIGTERM
);
805 /* enforce a reasonable length */
806 if ((size_t) cc
< length_query
+ off
) {
810 /* must match the ID */
811 if (memcmp(ibuf
+ off
+ NS_OFF_ID
, &x
->id
, 2) != 0) {
815 /* must be a response */
816 memcpy(&v
, ibuf
+ off
+ NS_OFF_FLAGS
, 2);
818 if ((v
& NS_FLAG_QR
) == 0) {
822 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
823 perror("clock_gettime(receive)");
825 (void) pthread_kill(master
, SIGTERM
);
828 /* got it: update stats */
831 delta
= x
->ts3
.tv_sec
- x
->ts2
.tv_sec
;
832 delta
+= (x
->ts3
.tv_nsec
- x
->ts2
.tv_nsec
) / 1e9
;
838 dsumsq
+= delta
* delta
;
840 if (v
>= NS_RCODE_LAST
)
846 * flush/timeout receive
858 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
859 perror("clock_gettime(receive)");
861 (void) pthread_kill(master
, SIGTERM
);
870 if (x
->state
!= X_SENT
)
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])
877 /* garbage collect timed-out exchange */
878 if (pthread_mutex_lock(&mtxsent
) != 0) {
879 fprintf(stderr
, "pthread_mutex_lock(flushrecv)");
881 (void) pthread_kill(master
, SIGTERM
);
884 ISC_REMOVE(xsentl
, x
);
885 if (pthread_mutex_unlock(&mtxsent
) != 0) {
886 fprintf(stderr
, "pthread_mutex_unlock(flushrecv)");
888 (void) pthread_kill(master
, SIGTERM
);
891 (void) close(x
->sock
);
894 if (pthread_mutex_lock(&mtxfree
) != 0) {
895 fprintf(stderr
, "pthread_mutex_lock(flushrecv)");
897 (void) pthread_kill(master
, SIGTERM
);
901 ISC_INSERT(xfree
, xfreel
, x
);
902 if (pthread_mutex_unlock(&mtxfree
) != 0) {
903 fprintf(stderr
, "pthread_mutex_unlock(flushrecv)");
905 (void) pthread_kill(master
, SIGTERM
);
921 for (evn
= 0; evn
< topoll
; evn
++) {
922 idx
= ievents
[evn
].data
.fd
;
924 if (x
->state
!= X_SENT
)
926 if (ievents
[evn
].events
== 0)
928 if (pthread_mutex_lock(&mtxsent
) != 0) {
929 fprintf(stderr
, "pthread_mutex_lock(pollrecv)");
931 (void) pthread_kill(master
, SIGTERM
);
934 ISC_REMOVE(xsentl
, x
);
935 if (pthread_mutex_unlock(&mtxsent
) != 0) {
936 fprintf(stderr
, "pthread_mutex_unlock(pollrecv)");
938 (void) pthread_kill(master
, SIGTERM
);
942 ievents
[evn
].events
= 0;
943 (void) close(x
->sock
);
945 if (pthread_mutex_lock(&mtxfree
) != 0) {
946 fprintf(stderr
, "pthread_mutex_lock(pollrecv)");
948 (void) pthread_kill(master
, SIGTERM
);
952 ISC_INSERT(xfree
, xfreel
, x
);
953 if (pthread_mutex_unlock(&mtxfree
) != 0) {
954 fprintf(stderr
, "pthread_mutex_unlock(pollrecv)");
956 (void) pthread_kill(master
, SIGTERM
);
963 * get the TCP DNS socket descriptor (IPv4)
974 sock
= socket(PF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
976 sock
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
980 /* make the socket descriptor not blocking */
981 flags
= fcntl(sock
, F_GETFL
, 0);
986 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1) {
992 if (localname
!= NULL
) {
994 struct sockaddr_in
*l4
;
996 l4
= (struct sockaddr_in
*) &localaddr
;
997 l4
->sin_port
= htons((uint16_t) curport
);
999 if (curport
> maxport
)
1003 (struct sockaddr
*) &localaddr
,
1004 sizeof(struct sockaddr_in
)) < 0) {
1012 (struct sockaddr
*) &serveraddr
,
1013 sizeof(struct sockaddr_in
)) < 0) {
1014 if (errno
!= EINPROGRESS
) {
1023 * connect the TCP DNS QUERY (IPv4)
1032 struct epoll_event ev
;
1034 ret
= clock_gettime(CLOCK_REALTIME
, &last
);
1036 perror("clock_gettime(connect)");
1038 (void) pthread_kill(master
, SIGTERM
);
1045 ret
= pthread_mutex_lock(&mtxfree
);
1047 fprintf(stderr
, "pthread_mutex_lock(connect4)");
1049 (void) pthread_kill(master
, SIGTERM
);
1052 ISC_REMOVE(xfreel
, x
);
1053 ret
= pthread_mutex_unlock(&mtxfree
);
1055 fprintf(stderr
, "pthread_mutex_unlock(connect4)");
1057 (void) pthread_kill(master
, SIGTERM
);
1060 } else if (xused
< xlast
) {
1067 if ((x
->state
!= X_FREE
) || (x
->sock
!= -1))
1070 memset(x
, 0, sizeof(*x
));
1071 memset(&ev
, 0, sizeof(ev
));
1075 x
->sock
= getsock4();
1077 int result
= x
->sock
;
1080 ret
= pthread_mutex_lock(&mtxfree
);
1082 fprintf(stderr
, "pthread_mutex_lock(connect4)");
1084 (void) pthread_kill(master
, SIGTERM
);
1087 ISC_INSERT(xfree
, xfreel
, x
);
1088 ret
= pthread_mutex_unlock(&mtxfree
);
1090 fprintf(stderr
, "pthread_mutex_unlock(connect4)");
1092 (void) pthread_kill(master
, SIGTERM
);
1097 ret
= pthread_mutex_lock(&mtxconn
);
1099 fprintf(stderr
, "pthread_mutex_lock(connect4)");
1101 (void) pthread_kill(master
, SIGTERM
);
1105 ISC_INSERT(xconn
, xconnl
, x
);
1106 ret
= pthread_mutex_unlock(&mtxconn
);
1108 fprintf(stderr
, "pthread_mutex_unlock(connect4)");
1110 (void) pthread_kill(master
, SIGTERM
);
1113 ev
.events
= EPOLLOUT
| EPOLLET
| EPOLLONESHOT
;
1115 if (epoll_ctl(epoll_ofd
, EPOLL_CTL_ADD
, x
->sock
, &ev
) < 0) {
1116 perror("epoll_ctl(add output)");
1118 (void) pthread_kill(master
, SIGTERM
);
1121 x
->order
= xccount
++;
1122 x
->id
= (uint16_t) random();
1124 if (random_query
> 0)
1125 x
->rnd
= (uint32_t) random();
1131 * get the TCP DNS socket descriptor (IPv6)
1142 sock
= socket(PF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
1144 sock
= socket(PF_INET6
, SOCK_STREAM
, IPPROTO_TCP
);
1148 /* make the socket descriptor not blocking */
1149 flags
= fcntl(sock
, F_GETFL
, 0);
1154 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1) {
1159 /* bind if wanted */
1160 if (localname
!= NULL
) {
1162 struct sockaddr_in6
*l6
;
1164 l6
= (struct sockaddr_in6
*) &localaddr
;
1165 l6
->sin6_port
= htons((uint16_t) curport
);
1167 if (curport
> maxport
)
1171 (struct sockaddr
*) &localaddr
,
1172 sizeof(struct sockaddr_in6
)) < 0) {
1180 (struct sockaddr
*) &serveraddr
,
1181 sizeof(struct sockaddr_in6
)) < 0) {
1182 if (errno
!= EINPROGRESS
) {
1191 * connect the TCP DNS QUERY (IPv6)
1200 struct epoll_event ev
;
1202 ret
= clock_gettime(CLOCK_REALTIME
, &last
);
1204 perror("clock_gettime(connect)");
1206 (void) pthread_kill(master
, SIGTERM
);
1213 ret
= pthread_mutex_lock(&mtxfree
);
1215 fprintf(stderr
, "pthread_mutex_lock(connect6)");
1217 (void) pthread_kill(master
, SIGTERM
);
1220 ISC_REMOVE(xfreel
, x
);
1221 ret
= pthread_mutex_unlock(&mtxfree
);
1223 fprintf(stderr
, "pthread_mutex_unlock(connect6)");
1225 (void) pthread_kill(master
, SIGTERM
);
1228 } else if (xused
< xlast
) {
1235 memset(x
, 0, sizeof(*x
));
1236 memset(&ev
, 0, sizeof(ev
));
1240 x
->sock
= getsock6();
1242 int result
= x
->sock
;
1245 ret
= pthread_mutex_lock(&mtxfree
);
1247 fprintf(stderr
, "pthread_mutex_lock(connect6)");
1249 (void) pthread_kill(master
, SIGTERM
);
1252 ISC_INSERT(xfree
, xfreel
, x
);
1253 ret
= pthread_mutex_unlock(&mtxfree
);
1255 fprintf(stderr
, "pthread_mutex_unlock(connect6)");
1257 (void) pthread_kill(master
, SIGTERM
);
1262 ret
= pthread_mutex_lock(&mtxconn
);
1264 fprintf(stderr
, "pthread_mutex_lock(connect6)");
1266 (void) pthread_kill(master
, SIGTERM
);
1270 ISC_INSERT(xconn
, xconnl
, x
);
1271 ret
= pthread_mutex_unlock(&mtxconn
);
1273 fprintf(stderr
, "pthread_mutex_unlock(connect6)");
1275 (void) pthread_kill(master
, SIGTERM
);
1278 ev
.events
= EPOLLOUT
| EPOLLET
| EPOLLONESHOT
;
1280 if (epoll_ctl(epoll_ofd
, EPOLL_CTL_ADD
, x
->sock
, &ev
) < 0) {
1281 perror("epoll_ctl(add output)");
1283 (void) pthread_kill(master
, SIGTERM
);
1286 x
->order
= xccount
++;
1287 x
->id
= (uint16_t) random();
1289 if (random_query
> 0)
1290 x
->rnd
= (uint32_t) random();
1296 * connector working routine
1300 connecting(void *dummy
)
1302 struct timespec now
, ts
;
1310 memset(name
, 0, sizeof(name
));
1311 ret
= prctl(PR_GET_NAME
, name
, 0, 0, 0);
1313 perror("prctl(PR_GET_NAME)");
1315 memmove(name
+ 5, name
, 11);
1316 memcpy(name
, "conn-", 5);
1317 ret
= prctl(PR_SET_NAME
, name
, 0, 0, 0);
1319 perror("prctl(PR_SET_NAME");
1328 /* compute the delay for the next connection */
1329 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
1330 perror("clock_gettime(connecting)");
1332 (void) pthread_kill(master
, SIGTERM
);
1340 due
.tv_nsec
+= 1010000000 / rate
;
1341 while (due
.tv_nsec
>= 1000000000) {
1343 due
.tv_nsec
-= 1000000000;
1346 ts
.tv_sec
-= now
.tv_sec
;
1347 ts
.tv_nsec
-= now
.tv_nsec
;
1348 while (ts
.tv_nsec
< 0) {
1350 ts
.tv_nsec
+= 1000000000;
1352 /* the connection was already due? */
1353 if (ts
.tv_sec
< 0) {
1354 ts
.tv_sec
= ts
.tv_nsec
= 0;
1358 ret
= clock_nanosleep(CLOCK_REALTIME
, 0, &ts
, NULL
);
1362 perror("clock_nanosleep");
1364 (void) pthread_kill(master
, SIGTERM
);
1369 /* compute how many connections to open */
1370 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
1371 perror("clock_gettime(connecting)");
1373 (void) pthread_kill(master
, SIGTERM
);
1377 if ((now
.tv_sec
> due
.tv_sec
) ||
1378 ((now
.tv_sec
== due
.tv_sec
) &&
1379 (now
.tv_nsec
>= due
.tv_nsec
))) {
1382 toconnect
= (now
.tv_nsec
- due
.tv_nsec
) / 1e9
;
1383 toconnect
+= now
.tv_sec
- due
.tv_sec
;
1386 if (toconnect
> (double) aggressiveness
)
1389 i
= (int) toconnect
;
1391 /* open connections */
1398 if ((ret
== -EAGAIN
) ||
1399 (ret
== -EWOULDBLOCK
) ||
1400 (ret
== -ENOBUFS
) ||
1403 (ret
== -EADDRNOTAVAIL
) ||
1413 /* there was no connection to open */
1421 * sender working routine
1425 sending(void *dummy
)
1434 memset(name
, 0, sizeof(name
));
1435 ret
= prctl(PR_GET_NAME
, name
, 0, 0, 0);
1437 perror("prctl(PR_GET_NAME)");
1439 memmove(name
+ 5, name
, 11);
1440 memcpy(name
, "send-", 5);
1441 ret
= prctl(PR_SET_NAME
, name
, 0, 0, 0);
1443 perror("prctl(PR_SET_NAME");
1453 memset(oevents
, 0, sizeof(oevents
));
1454 nfds
= epoll_wait(epoll_ofd
, oevents
, EVENTS_CNT
, 1);
1458 perror("epoll_wait(output)");
1460 (void) pthread_kill(master
, SIGTERM
);
1464 /* connection(s) to finish */
1475 /* packet(s) to send */
1485 * receiver working routine
1489 receiving(void *dummy
)
1498 memset(name
, 0, sizeof(name
));
1499 ret
= prctl(PR_GET_NAME
, name
, 0, 0, 0);
1501 perror("prctl(PR_GET_NAME)");
1503 memmove(name
+ 5, name
, 11);
1504 memcpy(name
, "recv-", 5);
1505 ret
= prctl(PR_SET_NAME
, name
, 0, 0, 0);
1507 perror("prctl(PR_SET_NAME");
1517 memset(ievents
, 0, sizeof(ievents
));
1518 nfds
= epoll_wait(epoll_ifd
, ievents
, EVENTS_CNT
, 1);
1522 perror("epoll_wait(input)");
1524 (void) pthread_kill(master
, SIGTERM
);
1528 /* packet(s) to receive */
1544 * get the server socket address from the command line:
1545 * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
1549 getserveraddr(const int flags
)
1551 struct addrinfo hints
, *res
;
1554 memset(&hints
, 0, sizeof(hints
));
1556 hints
.ai_family
= AF_INET
;
1558 hints
.ai_family
= AF_INET6
;
1560 hints
.ai_socktype
= SOCK_DGRAM
;
1561 hints
.ai_protocol
= IPPROTO_UDP
;
1563 hints
.ai_socktype
= SOCK_STREAM
;
1564 hints
.ai_protocol
= IPPROTO_TCP
;
1566 hints
.ai_flags
= AI_ADDRCONFIG
| flags
;
1568 ret
= getaddrinfo(servername
, NULL
, &hints
, &res
);
1570 fprintf(stderr
, "bad server=%s: %s\n",
1571 servername
, gai_strerror(ret
));
1574 if (res
->ai_next
!= NULL
) {
1575 fprintf(stderr
, "ambiguous server=%s\n", servername
);
1578 memcpy(&serveraddr
, res
->ai_addr
, res
->ai_addrlen
);
1581 ((struct sockaddr_in
*)&serveraddr
)->sin_port
= htons(port
);
1583 ((struct sockaddr_in6
*)&serveraddr
)->sin6_port
= htons(port
);
1587 * get the local socket address from the command line
1593 struct addrinfo hints
, *res
;
1596 memset(&hints
, 0, sizeof(hints
));
1598 hints
.ai_family
= AF_INET
;
1600 hints
.ai_family
= AF_INET6
;
1602 hints
.ai_socktype
= SOCK_DGRAM
;
1603 hints
.ai_protocol
= IPPROTO_UDP
;
1605 hints
.ai_socktype
= SOCK_STREAM
;
1606 hints
.ai_protocol
= IPPROTO_TCP
;
1608 hints
.ai_flags
= AI_ADDRCONFIG
;
1610 ret
= getaddrinfo(localname
, NULL
, &hints
, &res
);
1613 "bad -l<local-addr=%s>: %s\n",
1618 /* refuse multiple addresses */
1619 if (res
->ai_next
!= NULL
) {
1621 "ambiguous -l<local-addr=%s>\n",
1625 memcpy(&localaddr
, res
->ai_addr
, res
->ai_addrlen
);
1630 * intermediate reporting
1631 * (note: an in-transit packet can be reported as lost)
1637 dreport
.tv_sec
+= report
;
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
));
1650 avg
= dsum
/ xrcount
;
1651 printf(" average: %.3f ms", avg
* 1e3
);
1667 while (wait3(&status
, WNOHANG
, NULL
) > 0)
1700 fprintf(stderr
, "version 0.01\n");
1704 * usage (from the wiki)
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"
1716 "The server argument is the name/address of the DNS server to contact.\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"
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"
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"
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"
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"
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");
1794 * main function / entry point
1798 main(const int argc
, char * const argv
[])
1800 int opt
, flags
= 0, ret
, i
;
1804 extern char *optarg
;
1807 #define OPTIONS "hv46u0XM:r:t:R:b:n:p:d:D:l:L:a:s:T:O:x:P:"
1809 /* decode options */
1810 while ((opt
= getopt(argc
, argv
, OPTIONS
)) != -1)
1829 if (ipversion
== 6) {
1830 fprintf(stderr
, "IP version already set to 6\n");
1838 if (ipversion
== 4) {
1839 fprintf(stderr
, "IP version already set to 4\n");
1851 xlast
= atoi(optarg
);
1852 if (xlast
<= 1000) {
1853 fprintf(stderr
, "memory must be greater than 1000\n");
1860 rate
= atoi(optarg
);
1862 fprintf(stderr
, "rate must be a positive integer\n");
1869 report
= atoi(optarg
);
1871 fprintf(stderr
, "report must be a positive integer\n");
1881 "range must not be a negative integer\n");
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
;
1894 maxrandom
= (uint32_t) m
;
1900 fprintf(stderr
, "too many bases\n");
1904 base
[basecnt
] = optarg
;
1912 if (gotnumreq
> 1) {
1913 fprintf(stderr
, "too many num-request's\n");
1917 numreq
[gotnumreq
] = atoi(optarg
);
1918 if ((numreq
[gotnumreq
] < 0) ||
1919 ((numreq
[gotnumreq
] == 0) && (gotnumreq
== 1))) {
1921 "num-request must be a positive integer\n");
1929 period
= atoi(optarg
);
1932 "test-period must be a positive integer\n");
1940 if (gotlosttime
> 1) {
1941 fprintf(stderr
, "too many lost-time's\n");
1946 if ((d
< 0.) || ((d
== 0.) && (gotlosttime
== 1))) {
1948 "lost-time must be a positive number\n");
1953 losttime
[gotlosttime
] = d
;
1959 if (gotmaxloss
> 1) {
1960 fprintf(stderr
, "too many max-loss's\n");
1964 pc
= strchr(optarg
, '%');
1967 maxploss
[gotmaxloss
] = atof(optarg
);
1968 if ((maxploss
[gotmaxloss
] < 0) ||
1969 (maxploss
[gotmaxloss
] >= 100)) {
1971 "invalid max-loss percentage\n");
1976 maxloss
[gotmaxloss
] = atoi(optarg
);
1977 if ((maxloss
[gotmaxloss
] < 0) ||
1978 ((maxloss
[gotmaxloss
] == 0) &&
1979 (gotmaxloss
== 1))) {
1981 "max-loss must be a "
1982 "positive integer\n");
1995 if ((i
<= 0) || (i
>65535)) {
1997 "local-port must be a small positive integer\n");
2002 fprintf(stderr
, "too many local-port's\n");
2007 minport
= curport
= i
;
2013 aggressiveness
= atoi(optarg
);
2014 if (aggressiveness
<= 0) {
2016 "aggressiveness must be a positive integer\n");
2024 seed
= (unsigned int) atol(optarg
);
2028 if (templatefile
!= NULL
) {
2029 fprintf(stderr
, "template-file is already set\n");
2033 templatefile
= optarg
;
2037 rndoffset
= atoi(optarg
);
2038 if (rndoffset
< 14) {
2040 "random-offset must be greater than 14\n");
2052 if ((i
<= 0) || (i
> 65535)) {
2054 "port must be a positive short integer\n");
2058 port
= (in_port_t
) i
;
2066 /* adjust some global variables */
2075 if ((curport
!= 0) && (maxport
== 0))
2078 /* when required, print the internal view of the command line */
2079 if ((diags
!= NULL
) && (strchr(diags
, 'a') != NULL
)) {
2082 printf("IPv%d", ipversion
);
2083 printf(" rate=%d", rate
);
2087 printf(" report=%d", report
);
2089 if (strchr(diags
, 'r') != NULL
)
2090 printf(" range=0..%d [0x%x]",
2092 (unsigned int) maxrandom
);
2094 printf(" range=0..%d", range
);
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]);
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]);
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]);
2130 printf(" aggressiveness=%d", aggressiveness
);
2132 printf(" seed=%u", seed
);
2133 if (templatefile
!= NULL
)
2134 printf(" template-file='%s'", templatefile
);
2135 else if (ixann
!= 0)
2138 printf(" rnd-offset=%d", rndoffset
);
2139 printf(" diagnotic-selectors='%s'", diags
);
2143 /* check local address options */
2144 if ((localname
== NULL
) && (curport
!= 0)) {
2146 "-l<local-addr> must be set to use -L<local-port>\n");
2151 /* check template file options */
2152 if ((templatefile
== NULL
) && (rndoffset
>= 0)) {
2154 "-T<template-file> must be set to "
2155 "use -O<random-offset>\n");
2160 /* check various template file(s) and other condition(s) options */
2161 if ((templatefile
!= NULL
) && (range
> 0) && (rndoffset
< 0)) {
2163 "-O<random-offset> must be set when "
2164 "-T<template-file> and -R<range> are used\n");
2169 /* get the server argument */
2170 if (optind
< argc
- 1) {
2171 fprintf(stderr
, "extra arguments?\n");
2175 if (optind
== argc
- 1)
2176 servername
= argv
[optind
];
2178 /* handle the local '-l' address/interface */
2179 if (localname
!= NULL
) {
2182 if ((diags
!= NULL
) && (strchr(diags
, 'a') != NULL
)) {
2183 printf("local-addr='%s'", localname
);
2185 printf(" local-port='%d..%d'",
2191 /* get the server socket address */
2192 if (servername
== NULL
) {
2193 fprintf(stderr
, "server is required\n");
2197 getserveraddr(flags
);
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
];
2206 ret
= getnameinfo((struct sockaddr
*) &localaddr
,
2215 "can't get the local address: %s\n",
2219 printf("local address='%s'\n", addr
);
2222 /* initialize exchange structures */
2225 /* get the socket descriptor and template(s) */
2226 if (templatefile
== NULL
)
2227 build_template_query();
2229 get_template_query();
2232 if (clock_gettime(CLOCK_REALTIME
, &boot
) < 0) {
2233 perror("clock_gettime(boot)");
2237 /* compute the next intermediate reporting date */
2239 dreport
.tv_sec
= boot
.tv_sec
+ report
;
2240 dreport
.tv_nsec
= boot
.tv_nsec
;
2243 /* seed the random generator */
2245 seed
= (unsigned int) (boot
.tv_sec
+ boot
.tv_nsec
);
2248 /* required only before the interrupted flag check */
2249 (void) signal(SIGINT
, interrupt
);
2250 (void) signal(SIGTERM
, terminate
);
2253 master
= pthread_self();
2254 ret
= pthread_create(&connector
, NULL
, connecting
, NULL
);
2256 fprintf(stderr
, "pthread_create: %s\n", strerror(ret
));
2259 ret
= pthread_create(&sender
, NULL
, sending
, NULL
);
2261 fprintf(stderr
, "pthread_create: %s\n", strerror(ret
));
2264 ret
= pthread_create(&receiver
, NULL
, receiving
, NULL
);
2266 fprintf(stderr
, "pthread_create: %s\n", strerror(ret
));
2272 struct timespec now
, ts
;
2274 /* immediate loop exit conditions */
2276 if ((diags
!= NULL
) && (strchr(diags
, 'e') != NULL
))
2277 printf("interrupted\n");
2281 if ((diags
!= NULL
) && (strchr(diags
, 'e') != NULL
))
2282 printf("got a fatal error\n");
2288 /* get the date and use it */
2289 if (clock_gettime(CLOCK_REALTIME
, &now
) < 0) {
2290 perror("clock_gettime(now)");
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");
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
))))
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");
2314 if ((numreq
[1] != 0) && ((int) xscount
>= numreq
[1])) {
2315 if ((diags
!= NULL
) && (strchr(diags
, 'e') != NULL
))
2316 printf("reached num-query\n");
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");
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");
2333 if ((maxploss
[0] != 0.) &&
2335 (((100. * (xccount
- xscount
)) / xccount
) > maxploss
[1])) {
2336 if ((diags
!= NULL
) && (strchr(diags
, 'e') != NULL
))
2337 printf("reached max-loss "
2338 "(connection/percent)\n");
2341 if ((maxploss
[1] != 0.) &&
2343 (((100. * (xscount
- xrcount
)) / xscount
) > maxploss
[1])) {
2344 if ((diags
!= NULL
) && (strchr(diags
, 'e') != NULL
))
2345 printf("reached max-loss "
2346 "(query/percent)\n");
2351 memset(&ts
, 0, sizeof(ts
));
2352 ts
.tv_nsec
= 1000000;
2353 (void) clock_nanosleep(CLOCK_REALTIME
, 0, &ts
, NULL
);
2356 /* after main loop: finished */
2357 if (clock_gettime(CLOCK_REALTIME
, &finished
) < 0)
2358 perror("clock_gettime(finished)");
2361 (void) pthread_cancel(connector
);
2362 (void) pthread_cancel(sender
);
2363 (void) pthread_cancel(receiver
);
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, "
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
]);
2403 /* print the rates */
2404 if (finished
.tv_sec
!= 0) {
2405 double dall
, erate
[3];
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
);
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",
2440 /* round-time trip statistics */
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
);
2452 if ((diags
!= NULL
) && (strchr(diags
, 'T') != NULL
)) {
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
++) {
2461 (n
& 15) == 0 ? "" : " ",
2471 /* compute the exit code (and exit) */
2474 else if ((xccount
== xscount
) && (xscount
== xrcount
))