From: Marcin Siodelski Date: Fri, 14 Sep 2012 11:02:25 +0000 (+0200) Subject: [1960] Removed old perfdhcp. X-Git-Tag: trac2351_base~40^2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5da8f0ce275404a225759f9673daffa4d9da386a;p=thirdparty%2Fkea.git [1960] Removed old perfdhcp. --- diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 0532f27dcc..cefd29851b 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -18,27 +18,24 @@ if USE_STATIC_LINK AM_LDFLAGS += -static endif -pkglibexec_PROGRAMS = perfdhcp2 -perfdhcp2_SOURCES = main.cc -perfdhcp2_SOURCES += command_options.cc command_options.h -perfdhcp2_SOURCES += localized_option.h -perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h -perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h -perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h -perfdhcp2_SOURCES += stats_mgr.h -perfdhcp2_SOURCES += test_control.cc test_control.h +pkglibexec_PROGRAMS = perfdhcp +perfdhcp_SOURCES = main.cc +perfdhcp_SOURCES += command_options.cc command_options.h +perfdhcp_SOURCES += localized_option.h +perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h +perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h +perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h +perfdhcp_SOURCES += stats_mgr.h +perfdhcp_SOURCES += test_control.cc test_control.h libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS) -perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS) +perfdhcp_CXXFLAGS = $(AM_CXXFLAGS) if USE_CLANGPP # Disable unused parameter warning caused by some of the # Boost headers when compiling with clang. -perfdhcp2_CXXFLAGS += -Wno-unused-parameter +perfdhcp_CXXFLAGS += -Wno-unused-parameter endif -perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la -perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la -perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la - -#pkglibexec_PROGRAMS = perfdhcp -#perfdhcp_SOURCES = perfdhcp.c +perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la +perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la +perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c deleted file mode 100644 index 3ab9a2e5df..0000000000 --- a/tests/tools/perfdhcp/perfdhcp.c +++ /dev/null @@ -1,3559 +0,0 @@ -/* - * Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC") - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH - * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, - * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM - * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE - * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -#ifndef HAVE_GETIFADDRS - -/* - * Solaris 10 does not have the getifaddrs() function available (although it - * is present on Solaris 11 and later). For the present we will not implement - * a replacement (as we do with clock_gettime) as the implementation is - * relatively complex. Just output a message saying that the utility is not - * supported on this operating system. - */ - -#include - -int -main(const int argc, char* const argv[]) -{ - fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n"); - return (1); -} - -#else - -/* getifaddrs() present, so the code should compile */ - -#ifdef __linux__ -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef HAVE_PSELECT - -#include - -/* Platforms such as OpenBSD don't provide a pselect(), so we use our - own implementation for this testcase, which wraps around select() and - hence doesn't implement the high precision timer. This implementation - is fine for our purpose. */ - -static int -pselect (int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, const struct timespec *timeout, - const sigset_t *sigmask) -{ - struct timeval my_timeout; - - /* Our particular usage of pselect() doesn't use these fields. */ - assert(writefds == NULL); - assert(exceptfds == NULL); - assert(sigmask == NULL); - - my_timeout.tv_sec = timeout->tv_sec; - my_timeout.tv_usec = timeout->tv_nsec / 1000; - - return (select(nfds, readfds, writefds, exceptfds, &my_timeout)); -} - -#endif /* !HAVE_PSELECT */ - -/* DHCPv4 defines (to be moved/shared) */ - -#define DHCP_OFF_OPCODE 0 -#define DHCP_OFF_HTYPE 1 -#define DHCP_OFF_HLEN 2 -#define DHCP_OFF_HOPS 3 -#define DHCP_OFF_XID 4 -#define DHCP_OFF_SECS 8 -#define DHCP_OFF_FLAGS 10 -#define DHCP_OFF_CIADDR 12 -#define DHCP_OFF_YIADDR 16 -#define DHCP_OFF_SADDR 20 -#define DHCP_OFF_GIADDR 24 -#define DHCP_OFF_CHADDR 28 -#define DHCP_OFF_SNAME 44 -#define DHCP_OFF_FILE 108 -#define DHCP_OFF_COOKIE 236 -#define DHCP_OFF_OPTIONS 240 - -#define BOOTP_OP_REQUEST 1 -#define BOOTP_OP_REPLY 2 -#define BOOTP_MIN_LEN 300 - -#define DHCP_OP_DISCOVER 1 -#define DHCP_OP_OFFER 2 -#define DHCP_OP_REQUEST 3 -#define DHCP_OP_DECLINE 4 -#define DHCP_OP_ACK 5 -#define DHCP_OP_NAK 6 -#define DHCP_OP_RELEASE 7 -#define DHCP_OP_INFORM 8 - -#define DHCP_HTYPE_ETHER 1 - -#define DHCP_OPT_PAD 0 -#define DHCP_OPT_SUBNET_MASK 1 -#define DHCP_OPT_TIME_OFFSET 2 -#define DHCP_OPT_ROUTERS 3 -#define DHCP_OPT_DNS_SERVERS 6 -#define DHCP_OPT_HOST_NAME 12 -#define DHCP_OPT_DOMAIN_NAME 15 -#define DHCP_OPT_BROADCAST 28 -#define DHCP_OPT_DHCP_ADDRESS 50 -#define DHCP_OPT_DHCP_LEASE 51 -#define DHCP_OPT_DHCP_MSGTYPE 53 -#define DHCP_OPT_DHCP_SRVID 54 -#define DHCP_OPT_DHCP_PRL 55 -#define DHCP_OPT_END 255 - -#define DHCP_OPTLEN_SRVID 6 - -/* DHCPv6 defines (to be moved/shared) */ - -#define DHCP6_OFF_MSGTYP 0 -#define DHCP6_OFF_XID 1 -#define DHCP6_OFF_OPTIONS 4 - -#define DHCP6_OP_SOLICIT 1 -#define DHCP6_OP_ADVERTISE 2 -#define DHCP6_OP_REQUEST 3 -#define DHCP6_OP_REPLY 7 - -#define DHCP6_OPT_CLIENTID 1 -#define DHCP6_OPT_SERVERID 2 -#define DHCP6_OPT_IA_NA 3 -#define DHCP6_OPT_ORO 6 -#define DHCP6_OPT_ELAPSED_TIME 8 -#define DHCP6_OPT_STATUS_CODE 13 -#define DHCP6_OPT_RAPID_COMMIT 14 -#define DHCP6_OPT_NAME_SERVERS 23 -#define DHCP6_OPT_DOMAIN_SEARCH 24 - -#define DHCP6_ST_SUCCESS 0 -#define DHCP6_ST_NOADDRSAVAIL 2 - -#define DHCP6_DUID_LLT 1 -#define DHCP6_DUID_EPOCH 946684800 - -/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */ - -#define ISC_TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; \ - struct type **tqh_last; \ -} - -#define ISC_TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; \ - struct type **tqe_prev; \ -} - -#define ISC_TAILQ_EMPTY(head) ((head)->tqh_first == NULL) - -#define ISC_TAILQ_FIRST(head) ((head)->tqh_first) - -#define ISC_TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) - -#define ISC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) - -#define ISC_TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) - -#define ISC_TAILQ_INIT(head) do { \ - ISC_TAILQ_FIRST((head)) = NULL; \ - (head)->tqh_last = &ISC_TAILQ_FIRST((head)); \ -} while (0) - -#define ISC_TAILQ_INSERT_HEAD(head, elm, field) do { \ - ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head)); \ - if (!ISC_TAILQ_EMPTY((head))) \ - ISC_TAILQ_FIRST((head))->field.tqe_prev = \ - &ISC_TAILQ_NEXT((elm), field); \ - else \ - (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \ - ISC_TAILQ_FIRST((head)) = (elm); \ - (elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head)); \ -} while (0) - -#define ISC_TAILQ_INSERT_TAIL(head, elm, field) do { \ - ISC_TAILQ_NEXT((elm), field) = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &ISC_TAILQ_NEXT((elm), field); \ -} while (0) - -#define ISC_TAILQ_REMOVE(head, elm, field) do { \ - if ((ISC_TAILQ_NEXT((elm), field)) != NULL) \ - ISC_TAILQ_NEXT((elm), field)->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field); \ -} while (0) - -#define ISC_TAILQ_FOREACH(var, head, field) \ - for ((var) = ISC_TAILQ_FIRST((head)); \ - (var); \ - (var) = ISC_TAILQ_NEXT((var), field)) - -#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = ISC_TAILQ_FIRST((head)); \ - (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -/* - * Data structures - */ - -/* - * exchange: - * - per exchange values: - * * order (for debugging) - * * xid (even/odd for 4 packet exchanges) - * * random (for debugging) - * * time-stamps - * * server ID (for 3rd packet) - * * IA_NA (for IPv6 3rd packet) - * - * sent/rcvd global chains, "next to be received" on entry cache, - * and hash table for xid -> data structure fast matching - * (using the assumption collisions are unlikely, cf birthday problem) - */ - -struct exchange { /* per exchange structure */ - ISC_TAILQ_ENTRY(exchange) gchain; /* global chaining */ - ISC_TAILQ_ENTRY(exchange) hchain; /* hash table chaining */ - uint64_t order0, order2; /* number of this exchange */ - uint32_t xid; /* transaction ID */ - uint32_t rnd; /* random part */ - struct timespec ts0, ts1, ts2, ts3; /* timespecs */ - uint8_t *sid; /* server ID */ - size_t sidlen; /* server ID length */ - uint8_t *iana; /* (IPv6) IA_NA */ - size_t ianalen; /* (IPv6) IA_NA length */ -}; -struct exchange *xnext0, *xnext2; /* next to be received */ -ISC_TAILQ_HEAD(xlist, exchange); /* exchange list */ -struct xlist xsent0, xsent2, xrcvd0, xrcvd2; /* sent and received lists */ -uint64_t xscount0, xscount2; /* sent counters */ -uint64_t xrcount0, xrcount2; /* received counters */ -caddr_t exchanges0, exchanges2; /* hash tables */ -uint32_t hashsize0, hashsize2; /* hash table sizes */ - -/* - * statictics counters and accumulators - */ - -uint64_t tooshort, orphans, locallimit; /* error counters */ -uint64_t latesent, compsend, latercvd; /* rate stats */ -uint64_t multrcvd, shortwait, collected[2]; /* rate stats (cont) */ -double dmin0 = 999999999., dmin2 = 999999999.; /* minimum delays */ -double dmax0 = 0., dmax2 = 0.; /* maximum delays */ -double dsum0 = 0., dsum2 = 0.; /* delay sums */ -double dsumsq0 = 0., dsumsq2 = 0.; /* square delay sums */ - -/* - * command line parameters - */ - -int ipversion = 0; /* IP version */ -int simple; /* DO/SA in place of DORR/SARR */ -int rate; /* rate in exchange per second */ -int report; /* delay between two reports */ -uint32_t range; /* randomization range */ -uint32_t maxrandom; /* maximum random value */ -int basecnt; /* base count */ -char *base[4]; /* bases */ -int gotnumreq; /* numreq[0] was set */ -int numreq[2]; /* number of exchange */ -int period; /* test period */ -int gotlosttime; /* losttime[0] was set */ -double losttime[2] = {1., 1.}; /* time after a request is lost */ -int gotmaxdrop; /* max{p}drop[0] was set */ -int maxdrop[2]; /* maximum number of lost requests */ -double maxpdrop[2] = { 0., 0.}; /* maximum percentage */ -char *localname; /* local address or interface */ -int isinterface; /* interface vs local address */ -int preload; /* preload exchanges */ -int aggressivity = 1; /* back to back exchanges */ -int localport; /* local port number (host endian) */ -int seeded; /* is a seed provided */ -unsigned int seed; /* randomization seed */ -int isbroadcast; /* use broadcast */ -int rapidcommit; /* add rapid commit option */ -int usefirst; /* where to take the server-ID */ -char *templatefile[2]; /* template file name */ -int xidoffset[2] = {-1, -1}; /* template offsets (xid)*/ -int rndoffset[2] = {-1, -1}; /* template offsets (random) */ -int elpoffset = -1; /* template offset (elapsed time) */ -int sidoffset = -1; /* template offset (server ID) */ -int ripoffset = -1; /* template offset (requested IP) */ -char *diags; /* diagnostic selectors */ -char *wrapped; /* wrapped command */ -char *servername; /* server */ - -/* - * global variables - */ - -struct sockaddr_storage localaddr; /* local socket address */ -struct sockaddr_storage serveraddr; /* server socket address */ - -int sock; /* socket descriptor */ -int interrupted, fatal; /* to finish flags */ - -uint8_t obuf[4096], ibuf[4096]; /* I/O buffers */ -char tbuf[8200]; /* template buffer */ - -struct timespec boot; /* the date of boot */ -struct timespec last; /* the date of last send */ -struct timespec due; /* the date of next send */ -struct timespec dreport; /* the date of next reporting */ -struct timespec finished; /* the date of finish */ - -uint8_t *gsrvid; /* global server id */ -size_t gsrvidlen; /* and its length */ -uint8_t gsrvidbuf[64]; /* and its storage */ - -/* MAC address */ -uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 }; - -/* DUID prefix */ -uint8_t *duid_prefix; -int duid_length; - -/* magic cookie for BOOTP/DHCPv4 */ -uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 }; - -/* - * templates - * - * note: the only hard point is what are the offsets: - * - xid_discover4 and xid_request4: first of the 4 octet long - * transaction ID (default DHCP_OFF_XID = 4) - * - random_discover4 and random_request4: last of the 6 octet long - * MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6) - * - elapsed_request4: first of the 2 octet long secs field - * (default DHCP_OFF_SECS = 8, 0 means disabled) - * - serverid_request4: first of the 6 octet long server ID option - * (no default, required) - * - reqaddr_request4: first of the 4 octet long requested IP address - * option content (i.e., the address itself, btw OFFER yiaddr) - * (no default, required) - * - xid_solicit6 and xid_request6: first of the 3 octet long - * transaction ID (default DHCP6_OFF_XID = 1) - * - random_solicit6 and random_request6: last of the DUID in the - * client ID option (no default, required when rate is set) - * - elapsed_request6: first of the 2 octet long content of - * the option elapsed time option (no default, 0 means disabled) - * - serverid_request6: position where the variable length server ID - * option is inserted (no default, required, set to length means append) - * - reqaddr_request6: position where of the variable length requested - * IP address option is inserted (no default, required, set to - * length means append) - */ - -size_t length_discover4; -uint8_t template_discover4[4096]; -size_t xid_discover4; -size_t random_discover4; -size_t length_request4; -uint8_t template_request4[4096]; -size_t xid_request4; -size_t elapsed_request4; -size_t random_request4; -size_t serverid_request4; -size_t reqaddr_request4; -size_t length_solicit6; -uint8_t template_solicit6[4096]; -size_t xid_solicit6; -size_t random_solicit6; -size_t length_request6; -uint8_t template_request6[4096]; -size_t xid_request6; -size_t elapsed_request6; -size_t random_request6; -size_t serverid_request6; -size_t reqaddr_request6; - - -// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator -// if the code is being compiled or Linux (or somewhere else) -// Perhaps this should be based on OS_LINUX define? - -#if !defined (CLOCK_REALTIME) -#define CLOCK_REALTIME 0 - -/// @brief clock_gettime implementation for non-Linux systems -/// -/// This implementation lacks nanosecond resolution. It is intended -/// to be used on non-Linux systems that does not provide clock_gettime -/// implementation. -/// -/// @param clockid ignored (kept for Linux prototype compatibility) -/// @param tp timespec structure -/// -/// @return always zero (kept for compatibility reasons) -int clock_gettime(int clockid, struct timespec *tp) { - - struct timeval tv; - gettimeofday(&tv, NULL); - tp->tv_sec = tv.tv_sec; - tp->tv_nsec = tv.tv_usec*1000; - - return (0); -} - -#endif - -/* - * initialize data structures handling exchanges - */ - -void -inits(void) -{ - struct xlist *bucket; - caddr_t p; - size_t len, i; - - ISC_TAILQ_INIT(&xsent0); - ISC_TAILQ_INIT(&xsent2); - ISC_TAILQ_INIT(&xrcvd0); - ISC_TAILQ_INIT(&xrcvd2); - - /// compute hashsizes - hashsize0 = 1024; - len = sizeof(*bucket) * hashsize0; - exchanges0 = malloc(len); - if (exchanges0 == NULL) { - perror("malloc(exchanges0)"); - exit(1); - } - for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) { - bucket = (struct xlist *) p; - ISC_TAILQ_INIT(bucket); - } - if (simple != 0) - return; - hashsize2 = 1024; - len = sizeof(*bucket) * hashsize2; - exchanges2 = malloc(len); - if (exchanges2 == NULL) { - perror("malloc(exchanges2)"); - exit(1); - } - for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) { - bucket = (struct xlist *) p; - ISC_TAILQ_INIT(bucket); - } -} - -/* - * 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 -randomize(size_t offset, uint32_t r) -{ - uint32_t v; - - if (range == 0) - return 0; - if (range == UINT32_MAX) - return r; - if (maxrandom != 0) - while (r >= maxrandom) - r = (uint32_t) random(); - r %= range + 1; - v = r; - v += obuf[offset]; - obuf[offset] = v; - if (v < 256) - return r; - v >>= 8; - v += obuf[offset - 1]; - obuf[offset - 1] = v; - if (v < 256) - return r; - v >>= 8; - v += obuf[offset - 2]; - obuf[offset - 2] = v; - if (v < 256) - return r; - v >>= 8; - v += obuf[offset - 3]; - obuf[offset - 3] = v; - return r; -} - -/* - * receive a reply (4th packet), shared between IPv4 and IPv6: - * - transaction ID xid - * - receiving time-stamp now - * called from receive[46]() when the xid is odd - */ - -void -receive_reply(uint32_t xid, struct timespec *now) -{ - struct exchange *x, *t; - struct xlist *bucket; - uint32_t hash; - int checklost; - double delta; - - /* bucket is needed even when the next cache matches */ - hash = (xid >> 1) & (hashsize2 - 1); - bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket)); - /* try the 'next to be received' cache */ - if ((xnext2 != NULL) && (xnext2->xid == xid)) { - x = xnext2; - goto found; - } - /* usually the lost probability is low for request/reply */ - checklost = 1; - /* look for the exchange */ - ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) { - double waited; - - if (x->xid == xid) - goto found; - if (checklost <= 0) - continue; - checklost = 0; - /* check for a timed-out exchange */ - waited = now->tv_sec - x->ts2.tv_sec; - waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9; - if (waited < losttime[1]) - continue; - /* garbage collect timed-out exchange */ - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent2, x, gchain); - free(x); - collected[1] += 1; - } - /* no match? very late or not for us */ - orphans++; - return; - - /* got it: update stats and move to the received queue */ - found: - xrcount2++; - x->ts3 = *now; - delta = x->ts3.tv_sec - x->ts2.tv_sec; - delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9; - if (delta < dmin2) - dmin2 = delta; - if (delta > dmax2) - dmax2 = delta; - dsum2 += delta; - dsumsq2 += delta * delta; - xnext2 = ISC_TAILQ_NEXT(x, gchain); - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent2, x, gchain); - ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain); -} - -/* - * get the DHCPv4 socket descriptor - * (the only complexity is broadcast enabling: there is no easy way to - * recognize broadcast addresses, so the command line -B flag) - */ - -void -getsock4(void) -{ - int ret; - - /* update local port */ - if (localport != 0) { - uint16_t lp = htons((uint16_t) localport); - - ((struct sockaddr_in *) &localaddr)->sin_port = lp; - } - sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) { - perror("socket"); - exit(1); - } - ret = bind(sock, - (struct sockaddr *) &localaddr, - sizeof(struct sockaddr_in)); - if (ret < 0) { - perror("bind"); - exit(1); - } - /* enable broadcast if needed or required */ - if (isbroadcast != 0) { - int on = 1; - - ret = setsockopt(sock, - SOL_SOCKET, SO_BROADCAST, - &on, sizeof(on)); - if (ret < 0) { - perror("setsockopt(SO_BROADCAST)"); - exit(1); - } - } -} - -/* - * build a DHCPv4 DISCOVER from a relay template - * (implicit parameters are the local (giaddr) and MAC addresses (chaddr)) - * (assume the link is Ethernet) - */ - -void -build_template_discover4(void) -{ - uint8_t *p = template_discover4; - - length_discover4 = BOOTP_MIN_LEN; - xid_discover4 = DHCP_OFF_XID; - random_discover4 = DHCP_OFF_CHADDR + 6; - /* opcode */ - p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST; - /* hardware address type */ - p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER; - /* hardware address length */ - p[DHCP_OFF_HLEN] = 6; - /* hops */ - p[DHCP_OFF_HOPS] = 1; - /* gateway address */ - memcpy(p + DHCP_OFF_GIADDR, - &((struct sockaddr_in *) &localaddr)->sin_addr, - 4); - /* hardware address */ - memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6); - /* cookie */ - memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4); - /* options */ - p += DHCP_OFF_OPTIONS; - /* inline DHCP message type */ - *p++ = DHCP_OPT_DHCP_MSGTYPE; - *p++ = 1; - *p++ = DHCP_OP_DISCOVER; - /* inline DHCP parameter request list (default) */ - *p++ = DHCP_OPT_DHCP_PRL; - *p++ = 7; - *p++ = DHCP_OPT_SUBNET_MASK; - *p++ = DHCP_OPT_BROADCAST; - *p++ = DHCP_OPT_TIME_OFFSET; - *p++ = DHCP_OPT_ROUTERS; - *p++ = DHCP_OPT_DOMAIN_NAME; - *p++ = DHCP_OPT_DNS_SERVERS; - *p++ = DHCP_OPT_HOST_NAME; - /* end */ - *p = DHCP_OPT_END; -} - -/* - * get a DHCPv4 client/relay first packet (usually a DISCOVER) template - * from the file given in the command line (-T) - * and xid/rnd offsets (-X and -O) - */ - -void -get_template_discover4(void) -{ - uint8_t *p = template_discover4; - int fd, cc, i, j; - - fd = open(templatefile[0], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "open(%s): %s\n", - templatefile[0], strerror(errno)); - exit(2); - } - cc = read(fd, tbuf, sizeof(tbuf)); - (void) close(fd); - if (cc < 0) { - fprintf(stderr, "read(%s): %s\n", - templatefile[0], strerror(errno)); - exit(1); - } - if (cc < 100) { - fprintf(stderr, "file '%s' too short\n", templatefile[0]); - exit(2); - } - if (cc > 8193) { - fprintf(stderr,"file '%s' too large\n", templatefile[0]); - exit(2); - } - j = 0; - for (i = 0; i < cc; i++) { - if (isspace((int) tbuf[i])) - continue; - if (!isxdigit((int) tbuf[i])) { - fprintf(stderr, - "illegal char[%d]='%c' in file '%s'\n", - i, (int) tbuf[i], templatefile[0]); - exit(2); - } - tbuf[j] = tbuf[i]; - j++; - } - cc = j; - if ((cc & 1) != 0) { - fprintf(stderr, - "odd number of hexadecimal digits in file '%s'\n", - templatefile[0]); - exit(2); - } - length_discover4 = cc >> 1; - for (i = 0; i < cc; i += 2) - (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]); - if (xidoffset[0] >= 0) - xid_discover4 = (size_t) xidoffset[0]; - else - xid_discover4 = DHCP_OFF_XID; - if (xid_discover4 + 4 > length_discover4) { - fprintf(stderr, - "xid (at %zu) outside the template (length %zu)?\n", - xid_discover4, length_discover4); - exit(2); - } - if (rndoffset[0] >= 0) - random_discover4 = (size_t) rndoffset[0]; - else - random_discover4 = DHCP_OFF_CHADDR + 6; - if (random_discover4 > length_discover4) { - fprintf(stderr, - "random (at %zu) outside the template (length %zu)?\n", - random_discover4, length_discover4); - exit(2); - } -} - -/* - * build a DHCPv4 REQUEST from a relay template - * (implicit parameters are the local (giaddr) and MAC addresses (chaddr)) - * (assume the link is Ethernet) - */ - -void -build_template_request4(void) -{ - uint8_t *p = template_request4; - - length_request4 = BOOTP_MIN_LEN; - xid_request4 = DHCP_OFF_XID; - elapsed_request4 = DHCP_OFF_SECS; - random_request4 = DHCP_OFF_CHADDR + 6; - /* opcode */ - p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST; - /* hardware address type */ - p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER; - /* hardware address length */ - p[DHCP_OFF_HLEN] = 6; - /* hops */ - p[DHCP_OFF_HOPS] = 1; - /* gateway address */ - memcpy(p + DHCP_OFF_GIADDR, - &((struct sockaddr_in *) &localaddr)->sin_addr, - 4); - /* hardware address */ - memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6); - /* cookie */ - memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4); - /* options */ - p += DHCP_OFF_OPTIONS; - /* inline DHCP message type */ - *p++ = DHCP_OPT_DHCP_MSGTYPE; - *p++ = 1; - *p++ = DHCP_OP_REQUEST; - /* place for DHCP server id (option) */ - serverid_request4 = p - template_request4; - p += DHCP_OPTLEN_SRVID; - /* place for DHCP requested IP address (address) */ - *p++ = DHCP_OPT_DHCP_ADDRESS; - *p++ = 4; - reqaddr_request4 = p - template_request4; - p += 4; - /* inline DHCP parameter request list (default) */ - *p++ = DHCP_OPT_DHCP_PRL; - *p++ = 7; - *p++ = DHCP_OPT_SUBNET_MASK; - *p++ = DHCP_OPT_BROADCAST; - *p++ = DHCP_OPT_TIME_OFFSET; - *p++ = DHCP_OPT_ROUTERS; - *p++ = DHCP_OPT_DOMAIN_NAME; - *p++ = DHCP_OPT_DNS_SERVERS; - *p++ = DHCP_OPT_HOST_NAME; - /* end */ - *p = DHCP_OPT_END; -} - -/* - * get a DHCPv4 client/relay third packet (usually a REQUEST) template - * from the file given in the command line (-T) - * and offsets (-X,-O,-E,-S,-I). - */ - -void -get_template_request4(void) -{ - uint8_t *p = template_request4; - int fd, cc, i, j; - - fd = open(templatefile[1], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "open(%s): %s\n", - templatefile[1], strerror(errno)); - exit(2); - } - cc = read(fd, tbuf, sizeof(tbuf)); - (void) close(fd); - if (cc < 0) { - fprintf(stderr, "read(%s): %s\n", - templatefile[1], strerror(errno)); - exit(1); - } - if (cc < 100) { - fprintf(stderr, "file '%s' too short\n", templatefile[1]); - exit(2); - } - if (cc > 8193) { - fprintf(stderr,"file '%s' too large\n", templatefile[1]); - exit(2); - } - j = 0; - for (i = 0; i < cc; i++) { - if (isspace((int) tbuf[i])) - continue; - if (!isxdigit((int) tbuf[i])) { - fprintf(stderr, - "illegal char[%d]='%c' in file '%s'\n", - i, (int) tbuf[i], templatefile[1]); - exit(2); - } - tbuf[j] = tbuf[i]; - j++; - } - cc = j; - if ((cc & 1) != 0) { - fprintf(stderr, - "odd number of hexadecimal digits in file '%s'\n", - templatefile[1]); - exit(2); - } - length_request4 = cc >> 1; - for (i = 0; i < cc; i += 2) - (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]); - if (xidoffset[1] >= 0) - xid_request4 = (size_t) xidoffset[1]; - else - xid_request4 = DHCP_OFF_XID; - if (xid_request4 + 4 > length_request4) { - fprintf(stderr, - "xid (at %zu) outside the template (length %zu)?\n", - xid_request4, length_request4); - exit(2); - } - if (rndoffset[1] >= 0) - random_request4 = (size_t) rndoffset[1]; - else - random_request4 = DHCP_OFF_CHADDR + 6; - if (random_request4 > length_request4) { - fprintf(stderr, - "random (at %zu) outside the template (length %zu)?\n", - random_request4, length_request4); - exit(2); - } - if (elpoffset >= 0) - elapsed_request4 = (size_t) elpoffset; - else - elapsed_request4 = DHCP_OFF_SECS; - if (elapsed_request4 + 2 > length_request4) { - fprintf(stderr, - "secs (at %zu) outside the template (length %zu)?\n", - elapsed_request4, length_request4); - exit(2); - } - serverid_request4 = (size_t) sidoffset; - if (serverid_request4 + 6 > length_request4) { - fprintf(stderr, - "server-id option (at %zu) outside the template " - "(length %zu)?\n", - serverid_request4, length_request4); - exit(2); - } - reqaddr_request4 = (size_t) ripoffset; - if (reqaddr_request4 + 4 > length_request4) { - fprintf(stderr, - "requested-ip-address option (at %zu) outside " - "the template (length %zu)?\n", - reqaddr_request4, length_request4); - exit(2); - } -} - -/* - * send the DHCPv4 REQUEST third packet - * (the transaction ID is odd) - * (TODO: check for errors in the OFFER) - */ - -void -send_request4(struct exchange *x0) -{ - struct exchange *x; - struct xlist *bucket; - uint32_t hash; - ssize_t ret; - - x = (struct exchange *) malloc(sizeof(*x)); - if (x == NULL) { - locallimit++; - perror("send2"); - return; - } - - memcpy(x, x0, sizeof(*x)); - x->order2 = xscount2++; - x->xid |= 1; - hash = x->xid >> 1; - - ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain); - hash &= hashsize2 - 1; - bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket)); - ISC_TAILQ_INSERT_TAIL(bucket, x, hchain); - - memcpy(obuf, template_request4, length_request4); - /* xid */ - memcpy(obuf + xid_request4, &x->xid, 4); - /* random */ - randomize(random_request4, x->rnd); - /* secs */ - if (elapsed_request4 > 0) { - int secs; - - secs = x->ts1.tv_sec - x->ts0.tv_sec; - if (x->ts1.tv_nsec < x->ts0.tv_nsec) - secs += 1; - if (secs > 0) { - obuf[elapsed_request4] = secs >> 8; - obuf[elapsed_request4 + 1] = secs & 0xff; - } - } - /* server ID */ - memcpy(obuf + serverid_request4, x->sid, x->sidlen); - /* requested IP address */ - memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4); - - /* timestamp */ - ret = clock_gettime(CLOCK_REALTIME, &x->ts2); - if (ret < 0) { - perror("clock_gettime(send2)"); - fatal = 1; - return; - } - ret = sendto(sock, obuf, length_request4, 0, - (struct sockaddr *) &serveraddr, - sizeof(struct sockaddr_in)); - if (ret >= 0) - return; - if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || - (errno == ENOBUFS) || (errno == ENOMEM)) - locallimit++; - perror("send2"); -} - -/* - * send the DHCPv4 DISCOVER first packet - * (for 4-exchange, the transaction ID xid is even) - */ - -int -send4(void) -{ - struct exchange *x; - struct xlist *bucket; - uint32_t hash; - ssize_t ret; - - x = (struct exchange *) malloc(sizeof(*x)); - if (x == NULL) - return -ENOMEM; - - memset(x, 0, sizeof(*x)); - x->order0 = xscount0++; - hash = x->rnd = (uint32_t) random(); - if (simple == 0) - x->xid = hash << 1; - else - x->xid = hash; - - ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain); - hash &= hashsize0 - 1; - bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket)); - ISC_TAILQ_INSERT_TAIL(bucket, x, hchain); - - memcpy(obuf, template_discover4, length_discover4); - /* xid */ - memcpy(obuf + xid_discover4, &x->xid, 4); - /* random */ - x->rnd = randomize(random_discover4, x->rnd); - /* timestamp */ - ret = clock_gettime(CLOCK_REALTIME, &last); - if (ret < 0) { - perror("clock_gettime(send)"); - fatal = 1; - return -errno; - } - x->ts0 = last; - errno = 0; - ret = sendto(sock, obuf, length_discover4, 0, - (struct sockaddr *) &serveraddr, - sizeof(struct sockaddr_in)); - if (ret == (ssize_t) length_discover4) - return 0; - return -errno; -} - -/* - * scan a DHCPv4 OFFER to get its server-id option - */ - -int -scan_for_srvid4(struct exchange *x, size_t cc) -{ - size_t off = DHCP_OFF_OPTIONS; - - for (;;) { - if (off + DHCP_OPTLEN_SRVID > cc) { - fprintf(stderr, "truncated\n"); - return -1; - } - if (ibuf[off] == DHCP_OPT_DHCP_SRVID) - break; - if (ibuf[off] == DHCP_OPT_END) { - fprintf(stderr, "server-id not found\n"); - return -1; - } - if (ibuf[off] == DHCP_OPT_PAD) { - off++; - continue; - } - off += 2 + ibuf[off + 1]; - } - /* check length */ - if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) { - fprintf(stderr, - "bad server-id length (%hhu)\n", - ibuf[off + 1]); - return -1; - } - /* cache it in the global variables when required and not yet done */ - if ((usefirst != 0) && (gsrvid == NULL)) { - memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID); - gsrvid = gsrvidbuf; - gsrvidlen = DHCP_OPTLEN_SRVID; - } - x->sid = ibuf + off; - x->sidlen = DHCP_OPTLEN_SRVID; - return 0; -} - -/* - * receive a DHCPv4 packet - */ - -void -receive4(void) -{ - struct exchange *x, *t; - struct xlist *bucket; - struct timespec now; - ssize_t cc; - uint32_t xid, hash; - int checklost = 0; - double delta; - - cc = recv(sock, ibuf, sizeof(ibuf), 0); - if (cc < 0) { - if ((errno == EAGAIN) || - (errno == EWOULDBLOCK) || - (errno == EINTR)) - return; - perror("recv"); - fatal = 1; - return; - } - /* enforce a reasonable length */ - if (cc < BOOTP_MIN_LEN) { - tooshort++; - return; - } - /* must be a BOOTP REPLY */ - if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY) - return; - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - perror("clock_gettime(receive)"); - fatal = 1; - return; - } - memcpy(&xid, ibuf + xid_discover4, 4); - /* 4-packet exchange even/odd xid */ - if (simple == 0) { - if ((xid & 1) != 0) { - receive_reply(xid, &now); - return; - } - hash = (xid >> 1) & (hashsize0 - 1); - } else - hash = xid & (hashsize0 - 1); - /* now it is the second packet, get the bucket which is needed */ - bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket)); - /* try the 'next to be received' cache */ - if ((xnext0 != NULL) && (xnext0->xid == xid)) { - x = xnext0; - goto found; - } - /* if the rate is not illimited, garbage collect up to 3 - timed-out exchanges */ - if (rate != 0) - checklost = 3; - /* look for the exchange */ - ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) { - double waited; - - if (x->xid == xid) - goto found; - if (checklost <= 0) - continue; - /* check for a timed-out exchange */ - waited = now.tv_sec - x->ts0.tv_sec; - waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9; - if (waited < losttime[0]) { - checklost = 0; - continue; - } - /* garbage collect timed-out exchange */ - checklost--; - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent0, x, gchain); - free(x); - collected[0] += 1; - } - /* no match? very late or not for us */ - orphans++; - return; - - /* got it: update stats and move to the received queue */ - found: - xrcount0++; - x->ts1 = now; - delta = x->ts1.tv_sec - x->ts0.tv_sec; - delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9; - if (delta < dmin0) - dmin0 = delta; - if (delta > dmax0) - dmax0 = delta; - dsum0 += delta; - dsumsq0 += delta * delta; - xnext0 = ISC_TAILQ_NEXT(x, gchain); - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent0, x, gchain); - ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain); - /* if the exchange is not finished, go to the second part */ - if (simple == 0) { - int ret = 0; - - /* the server-ID option is needed */ - if ((usefirst != 0) && (gsrvid != NULL)) { - x->sid = gsrvid; - x->sidlen = gsrvidlen; - } else - ret = scan_for_srvid4(x, cc); - if (ret >= 0) - send_request4(x); - } -} - -/* - * get the DHCPv6 socket descriptor - */ - -void -getsock6(void) -{ - struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr; - int ret; - - /* update local port */ - if (localport != 0) { - uint16_t lp = htons((uint16_t) localport); - - ((struct sockaddr_in6 *) &localaddr)->sin6_port = lp; - } - sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (sock < 0) { - perror("socket"); - exit(1); - } - ret = bind(sock, - (struct sockaddr *) &localaddr, - sizeof(struct sockaddr_in6)); - if (ret < 0) { - perror("Failed to bind v6 socket to local-link address"); - exit(1); - } - /* perform the multicast stuff when the destination is multicast */ - if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) { - int hops = 1; - - ret = setsockopt(sock, - IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &hops, sizeof(hops)); - if (ret < 0) { - perror("setsockopt(IPV6_MULTICAST_HOPS)"); - exit(1); - } - } - if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) { - unsigned idx = if_nametoindex(localname); - - if (idx == 0) { - fprintf(stderr, - "if_nametoindex(%s) failed\n", - localname); - exit(1); - } - ret = setsockopt(sock, - IPPROTO_IPV6, IPV6_MULTICAST_IF, - &idx, sizeof(idx)); - if (ret < 0) { - perror("setsockopt(IPV6_MULTICAST_IF)"); - exit(1); - } - } -} - -/* - * build a DHCPv6 SOLICIT template - * (implicit parameter is the DUID, don't assume an Ethernet link) - */ - -void -build_template_solicit6(void) -{ - uint8_t *p = template_solicit6; - - xid_solicit6 = DHCP6_OFF_XID; - /* message type */ - p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT; - /* options */ - p += DHCP6_OFF_OPTIONS; - /* elapsed time */ - p[1] = DHCP6_OPT_ELAPSED_TIME; - p[3] = 2; - p += 6; - /* rapid commit */ - if (rapidcommit != 0) { - p[1] = DHCP6_OPT_RAPID_COMMIT; - p += 4; - } - /* client ID */ - p[1] = DHCP6_OPT_CLIENTID; - p[3] = duid_length; - memcpy(p + 4, duid_prefix, duid_length); - p += 4 + duid_length; - random_solicit6 = p - template_solicit6; - /* option request option */ - p[1] = DHCP6_OPT_ORO; - p[3] = 4; - p[5] = DHCP6_OPT_NAME_SERVERS; - p[7] = DHCP6_OPT_DOMAIN_SEARCH; - p += 8; - /* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */ - p[1] = DHCP6_OPT_IA_NA; - p[3] = 12; - p[7] = 1; - p[10] = 3600 >> 8; - p[11] = 3600 & 0xff; - p[14] = 5400 >> 8; - p[15] = 5400 & 0xff; - p += 16; - /* set length */ - length_solicit6 = p - template_solicit6; -} - -/* - * get a DHCPv6 first packet (usually a SOLICIT) template - * from the file given in the command line (-T) - * and xid/rnd offsets (-X and -O) - */ - -void -get_template_solicit6(void) -{ - uint8_t *p = template_solicit6; - int fd, cc, i, j; - - fd = open(templatefile[0], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "open(%s): %s\n", - templatefile[0], strerror(errno)); - exit(2); - } - cc = read(fd, tbuf, sizeof(tbuf)); - (void) close(fd); - if (cc < 0) { - fprintf(stderr, "read(%s): %s\n", - templatefile[0], strerror(errno)); - exit(1); - } - if (cc < 10) { - fprintf(stderr, "file '%s' too short\n", templatefile[0]); - exit(2); - } - if (cc > 8193) { - fprintf(stderr,"file '%s' too large\n", templatefile[0]); - exit(2); - } - j = 0; - for (i = 0; i < cc; i++) { - if (isspace((int) tbuf[i])) - continue; - if (!isxdigit((int) tbuf[i])) { - fprintf(stderr, - "illegal char[%d]='%c' in file '%s'\n", - i, (int) tbuf[i], templatefile[0]); - exit(2); - } - tbuf[j] = tbuf[i]; - j++; - } - cc = j; - if ((cc & 1) != 0) { - fprintf(stderr, - "odd number of hexadecimal digits in file '%s'\n", - templatefile[0]); - exit(2); - } - length_solicit6 = cc >> 1; - for (i = 0; i < cc; i += 2) - (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]); - if (xidoffset[0] >= 0) - xid_solicit6 = (size_t) xidoffset[0]; - else - xid_solicit6 = DHCP6_OFF_XID; - if (xid_solicit6 + 3 > length_solicit6) { - fprintf(stderr, - "xid (at %zu) is outside the template (length %zu)?\n", - xid_solicit6, length_solicit6); - exit(2); - } - if (rndoffset[0] >= 0) - random_solicit6 = (size_t) rndoffset[0]; - else - random_solicit6 = 0; - if (random_solicit6 > length_solicit6) { - fprintf(stderr, - "random (at %zu) outside the template (length %zu)?\n", - random_solicit6, length_solicit6); - exit(2); - } -} - -/* - * build a DHCPv6 REQUEST template - * (implicit parameter is the DUID, don't assume an Ethernet link) - */ - -void -build_template_request6(void) -{ - uint8_t *p = template_request6; - - xid_request6 = DHCP6_OFF_XID; - /* message type */ - p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST; - /* options */ - p += DHCP6_OFF_OPTIONS; - /* elapsed time */ - p[1] = DHCP6_OPT_ELAPSED_TIME; - p[3] = 2; - p += 4; - elapsed_request6 = p - template_request6; - p += 2; - /* client ID */ - p[1] = DHCP6_OPT_CLIENTID; - p[3] = duid_length; - memcpy(p + 4, duid_prefix, duid_length); - p += 4 + duid_length; - random_request6 = p - template_request6; - /* option request option */ - p[1] = DHCP6_OPT_ORO; - p[3] = 4; - p[5] = DHCP6_OPT_NAME_SERVERS; - p[7] = DHCP6_OPT_DOMAIN_SEARCH; - p += 8; - /* server ID and IA_NA */ - serverid_request6 = p - template_request6; - reqaddr_request6 = p - template_request6; - /* set length */ - length_request6 = p - template_request6; -} - -/* - * get a DHCPv6 third packet (usually a REQUEST) template - * from the file given in the command line (-T) - * and offsets (-X,-O,-E,-S,-I). - */ - -void -get_template_request6(void) -{ - uint8_t *p = template_request6; - int fd, cc, i, j; - - fd = open(templatefile[1], O_RDONLY); - if (fd < 0) { - fprintf(stderr, "open(%s): %s\n", - templatefile[1], strerror(errno)); - exit(2); - } - cc = read(fd, tbuf, sizeof(tbuf)); - (void) close(fd); - if (cc < 0) { - fprintf(stderr, "read(%s): %s\n", - templatefile[1], strerror(errno)); - exit(1); - } - if (cc < 10) { - fprintf(stderr, "file '%s' too short\n", templatefile[1]); - exit(2); - } - if (cc > 8193) { - fprintf(stderr,"file '%s' too large\n", templatefile[1]); - exit(2); - } - j = 0; - for (i = 0; i < cc; i++) { - if (isspace((int) tbuf[i])) - continue; - if (!isxdigit((int) tbuf[i])) { - fprintf(stderr, - "illegal char[%d]='%c' in file '%s'\n", - i, (int) tbuf[i], templatefile[1]); - exit(2); - } - tbuf[j] = tbuf[i]; - j++; - } - cc = j; - if ((cc & 1) != 0) { - fprintf(stderr, - "odd number of hexadecimal digits in file '%s'\n", - templatefile[1]); - exit(2); - } - length_request6 = cc >> 1; - for (i = 0; i < cc; i += 2) - (void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]); - if (xidoffset[1] >= 0) - xid_request6 = (size_t) xidoffset[1]; - else - xid_request6 = DHCP6_OFF_XID; - if (xid_request6 + 3 > length_request6) { - fprintf(stderr, - "xid (at %zu) is outside the template (length %zu)?\n", - xid_request6, length_request6); - exit(2); - } - if (rndoffset[1] >= 0) - random_request6 = (size_t) rndoffset[1]; - else - random_request6 = 0; - if (random_request6 > length_request6) { - fprintf(stderr, - "random (at %zu) outside the template (length %zu)?\n", - random_request6, length_request6); - exit(2); - } - if (elpoffset >= 0) - elapsed_request6 = (size_t) elpoffset; - if (elapsed_request6 + 2 > length_request6) { - fprintf(stderr, - "secs (at %zu) outside the template (length %zu)?\n", - elapsed_request6, length_request6); - exit(2); - } - serverid_request6 = (size_t) sidoffset; - if (serverid_request6 > length_request6) { - fprintf(stderr, - "server-id option (at %zu) outside the template " - "(length %zu)?\n", - serverid_request6, length_request6); - exit(2); - } - reqaddr_request6 = (size_t) ripoffset; - if (reqaddr_request6 > length_request6) { - fprintf(stderr, - "requested-ip-address option (at %zu) outside " - "the template (length %zu)?\n", - reqaddr_request6, length_request6); - exit(2); - } -} - -/* - * send the DHCPv6 REQUEST third packet - * (the transaction ID is odd) - * (TODO: check for errors in the ADVERTISE) - */ - -void -send_request6(struct exchange *x0) -{ - struct exchange *x; - struct xlist *bucket; - size_t len; - uint32_t hash; - ssize_t ret; - - x = (struct exchange *) malloc(sizeof(*x)); - if (x == NULL) { - locallimit++; - perror("send2"); - return; - } - - memcpy(x, x0, sizeof(*x)); - x->order2 = xscount2++; - x->xid |= 1; - hash = x->xid >> 1; - - ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain); - hash &= hashsize2 - 1; - bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket)); - ISC_TAILQ_INSERT_TAIL(bucket, x, hchain); - - len = length_request6; - memcpy(obuf, template_request6, len); - /* xid */ - obuf[xid_request6] = x->xid >> 16; - obuf[xid_request6 + 1] = x->xid >> 8; - obuf[xid_request6 + 2] = x->xid; - /* random */ - randomize(random_request6, x->rnd); - /* elapsed time */ - if (elapsed_request6 > 0) { - int et; - - et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100; - et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000; - if (et > 65535) { - obuf[elapsed_request6] = 0xff; - obuf[elapsed_request6 + 1] = 0xff; - } else if (et > 0) { - obuf[elapsed_request6] = et >> 8; - obuf[elapsed_request6 + 1] = et & 0xff; - } - } - /* server ID */ - if (serverid_request6 < length_request6) - memmove(obuf + serverid_request6 + x->sidlen, - obuf + serverid_request6, - x->sidlen); - memcpy(obuf + serverid_request6, x->sid, x->sidlen); - len += x->sidlen; - /* IA_NA */ - if (reqaddr_request6 < serverid_request6) { - memmove(obuf + reqaddr_request6 + x->ianalen, - obuf + reqaddr_request6, - x->ianalen); - memcpy(obuf + reqaddr_request6, x->iana, x->ianalen); - } else if (reqaddr_request6 < length_request6) { - memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen, - obuf + reqaddr_request6 + x->sidlen, - x->ianalen); - memcpy(obuf + reqaddr_request6+ x->sidlen, - x->iana, - x->ianalen); - } else - memcpy(obuf + len, x->iana, x->ianalen); - len += x->ianalen; - - /* timestamp */ - ret = clock_gettime(CLOCK_REALTIME, &x->ts2); - if (ret < 0) { - perror("clock_gettime(send2)"); - fatal = 1; - return; - } - ret = sendto(sock, obuf, len, 0, - (struct sockaddr *) &serveraddr, - sizeof(struct sockaddr_in6)); - if (ret >= 0) - return; - if ((errno == EAGAIN) || (errno == EWOULDBLOCK) || - (errno == ENOBUFS) || (errno == ENOMEM)) - locallimit++; - perror("send2"); -} - -/* - * send the DHCPv6 SOLICIT first packet - * (for 4-exchange, the transaction ID xid is even) - */ - -int -send6(void) -{ - struct exchange *x; - struct xlist *bucket; - uint32_t hash; - ssize_t ret; - - x = (struct exchange *) malloc(sizeof(*x)); - if (x == NULL) - return -ENOMEM; - - memset(x, 0, sizeof(*x)); - x->order0 = xscount0++; - hash = x->rnd = (uint32_t) random(); - if (simple == 0) - x->xid = (hash << 1) & 0x00ffffff; - else - x->xid = hash & 0x00ffffff; - - ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain); - hash &= hashsize0 - 1; - bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket)); - ISC_TAILQ_INSERT_TAIL(bucket, x, hchain); - - memcpy(obuf, template_solicit6, length_solicit6); - /* xid */ - obuf[xid_solicit6] = x->xid >> 16; - obuf[xid_solicit6 + 1] = x->xid >> 8; - obuf[xid_solicit6 + 2] = x->xid; - /* random */ - x->rnd = randomize(random_solicit6, x->rnd); - - /* timestamp */ - ret = clock_gettime(CLOCK_REALTIME, &last); - if (ret < 0) { - perror("clock_gettime(send)"); - fatal = 1; - return -errno; - } - x->ts0 = last; - errno = 0; - ret = sendto(sock, obuf, length_solicit6, 0, - (struct sockaddr *) &serveraddr, - sizeof(struct sockaddr_in6)); - if (ret == (ssize_t) length_solicit6) - return 0; - return -errno; -} - -/* - * scan a DHCPv6 ADVERTISE to get its server-id option - */ - -int -scan_for_srvid6(struct exchange *x, size_t cc) -{ - size_t off = DHCP6_OFF_OPTIONS, len; - - for (;;) { - if (off + 4 > cc) { - fprintf(stderr, "server-id not found\n"); - return -1; - } - if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID)) - break; - off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3]; - } - len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3]; - /* cache it in the global variables when required and not yet done */ - if ((usefirst != 0) && (gsrvid == NULL)) { - if (len <= sizeof(gsrvidbuf)) { - memcpy(gsrvidbuf, ibuf + off, len); - gsrvid = gsrvidbuf; - gsrvidlen = len; - } else { - gsrvid = (uint8_t *) malloc(len); - if (gsrvid == NULL) { - perror("malloc(gsrvid"); - return -1; - } - memcpy(gsrvid, ibuf + off, len); - gsrvidlen = len; - } - } - x->sid = ibuf + off; - x->sidlen = len; - return 0; -} - -/* - * scan a DHCPv6 ADVERTISE to get its IA_NA option - * (TODO: check for errors) - */ - -int -scan_for_ia_na(struct exchange *x, size_t cc) -{ - size_t off = DHCP6_OFF_OPTIONS, len; - - for (;;) { - if (off + 4 > cc) { - fprintf(stderr, "ia-na not found\n"); - return -1; - } - if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA)) - break; - off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3]; - } - len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3]; - x->iana = ibuf + off; - x->ianalen = len; - return 0; -} - -/* - * receive a DHCPv6 packet - */ - -void -receive6(void) -{ - struct exchange *x, *t; - struct xlist *bucket; - struct timespec now; - ssize_t cc; - uint32_t xid, hash; - int checklost = 0; - double delta; - - cc = recv(sock, ibuf, sizeof(ibuf), 0); - if (cc < 0) { - if ((errno == EAGAIN) || - (errno == EWOULDBLOCK) || - (errno == EINTR)) - return; - perror("recv"); - fatal = 1; - return; - } - /* enforce a reasonable length */ - if (cc < 22) { - tooshort++; - return; - } - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - perror("clock_gettime(receive)"); - fatal = 1; - return; - } - xid = ibuf[xid_solicit6] << 16; - xid |= ibuf[xid_solicit6 + 1] << 8; - xid |= ibuf[xid_solicit6 + 2]; - /* 4-packet exchange even/odd xid */ - if (simple == 0) { - if ((xid & 1) != 0) { - receive_reply(xid, &now); - return; - } - hash = (xid >> 1) & (hashsize0 - 1); - } else - hash = xid & (hashsize0 - 1); - /* now it is the second packet, get the bucket which is needed */ - bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket)); - /* try the 'next to be received' cache */ - if ((xnext0 != NULL) && (xnext0->xid == xid)) { - x = xnext0; - goto found; - } - /* if the rate is not illimited, garbage collect up to 3 - timed-out exchanges */ - if (rate != 0) - checklost = 3; - /* look for the exchange */ - ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) { - double waited; - - if (x->xid == xid) - goto found; - if (checklost <= 0) - continue; - /* check for a timed-out exchange */ - waited = now.tv_sec - x->ts0.tv_sec; - waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9; - if (waited < losttime[0]) { - checklost = 0; - continue; - } - /* garbage collect timed-out exchange */ - checklost--; - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent0, x, gchain); - free(x); - collected[0] += 1; - } - /* no match? very late or not for us */ - orphans++; - return; - - /* got it: update stats and move to the received queue */ - found: - xrcount0++; - x->ts1 = now; - delta = x->ts1.tv_sec - x->ts0.tv_sec; - delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9; - if (delta < dmin0) - dmin0 = delta; - if (delta > dmax0) - dmax0 = delta; - dsum0 += delta; - dsumsq0 += delta * delta; - xnext0 = ISC_TAILQ_NEXT(x, gchain); - ISC_TAILQ_REMOVE(bucket, x, hchain); - ISC_TAILQ_REMOVE(&xsent0, x, gchain); - ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain); - /* if the exchange is not finished, go to the second part */ - if (simple == 0) { - int ret = 0; - - /* the server-ID option is needed */ - if ((usefirst != 0) && (gsrvid != NULL)) { - x->sid = gsrvid; - x->sidlen = gsrvidlen; - } else - ret = scan_for_srvid6(x, cc); - /* and the IA_NA option too */ - if (ret >= 0) - ret = scan_for_ia_na(x, cc); - if (ret >= 0) - send_request6(x); - } -} - -/* - * decode a base command line parameter - * (currently only MAC address and DUID are supported) - */ - -void -decodebase(void) -{ - char *b0 = base[basecnt]; - - /* MAC address (alias Ethernet address) */ - if ((strncasecmp(b0, "mac=", 4) == 0) || - (strncasecmp(b0, "ether=", 6) == 0)) { - char *b; - - b = strchr(b0, '=') + 1; - if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &mac_prefix[0], &mac_prefix[1], - &mac_prefix[2], &mac_prefix[3], - &mac_prefix[4], &mac_prefix[5]) == 6) { - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) - printf("set MAC to %s\n", b); - return; - } - fprintf(stderr, - "expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n", - (int) (b - b0 - 1), b0); - exit(2); - } - /* DUID */ - if (strncasecmp(b0, "duid=", 5) == 0) { - char *b; - size_t i, l; - - if (duid_prefix != NULL) { - fprintf(stderr, "duid was already set?\n"); - exit(2); - } - b = strchr(b0, '=') + 1; - l = 0; - while (*b != '\0') { - if (!isxdigit((int) *b)) { - fprintf(stderr, - "illegal char '%c' in duid\n", - (int) *b); - exit(2); - } - b++; - l++; - } - if ((l & 1) != 0) { - fprintf(stderr, - "odd number of hexadecimal digits in duid\n"); - exit(2); - } - l /= 2; - if (l > 64) { - fprintf(stderr, "duid too large\n"); - exit(2); - } - duid_prefix = (uint8_t *) malloc(l); - if (duid_prefix == NULL) { - perror("malloc(duid)"); - exit(1); - } - b = strchr(b0, '=') + 1; - for (i = 0; i < l; i++, b += 2) - (void) sscanf(b, "%02hhx", &duid_prefix[i]); - duid_length = l; - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { - b = strchr(b0, '=') + 1; - printf("set DUID[%d] to %s\n", - duid_length, b); - } - return; - } - /* other */ - fprintf(stderr, "not yet supported base '%s'\n", b0); - exit(2); -} - -/* - * get the server socket address from the command line: - * - flags: inherited from main, 0 or AI_NUMERICHOST (for literals) - */ - -void -getserveraddr(const int flags) -{ - struct addrinfo hints, *res; - char *service; - int ret; - - memset(&hints, 0, sizeof(hints)); - if (ipversion == 4) { - hints.ai_family = AF_INET; - service = "67"; - } else { - hints.ai_family = AF_INET6; - service = "547"; - } - hints.ai_socktype = SOCK_DGRAM; - - hints.ai_flags = AI_NUMERICSERV | flags; -#if defined(AI_ADDRCONFIG) - hints.ai_flags |= AI_ADDRCONFIG; -#endif - hints.ai_protocol = IPPROTO_UDP; - - ret = getaddrinfo(servername, service, &hints, &res); - if (ret != 0) { - fprintf(stderr, "bad server=%s: %s\n", - servername, gai_strerror(ret)); - exit(2); - } - if (res->ai_next != NULL) { - fprintf(stderr, "ambiguous server=%s\n", servername); - exit(2); - } - memcpy(&serveraddr, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); -} - -/* - * get the interface from the command line: - * - name of the interface - * - socket address to fill - * (in IPv6, get the first link-local address) - */ - -void -getinterface(const char *name, struct sockaddr_storage *ss) -{ - struct ifaddrs *ifaddr, *ifa; - int ret; - size_t len; - - ret = getifaddrs(&ifaddr); - if (ret < 0) { - perror("getifaddrs"); - exit(1); - } - - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_name == NULL) - continue; - if (strcmp(ifa->ifa_name, name) != 0) - continue; - if ((ifa->ifa_flags & IFF_UP) == 0) - continue; - if ((ifa->ifa_flags & IFF_MULTICAST) == 0) - continue; - if (ifa->ifa_addr == NULL) - continue; - if ((ipversion == 4) && - (ifa->ifa_addr->sa_family != AF_INET)) - continue; - if (ipversion == 6) { - struct sockaddr_in6 *a6; - - a6 = (struct sockaddr_in6 *) ifa->ifa_addr; - if (a6->sin6_family != AF_INET6) - continue; - if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr)) - continue; - } - if (ipversion == 4) - len = sizeof(struct sockaddr_in); - else - len = sizeof(struct sockaddr_in6); - memcpy(ss, ifa->ifa_addr, len); - /* fill the server port */ - if (ipversion == 4) - ((struct sockaddr_in *) ss)->sin_port = htons(67); - else - ((struct sockaddr_in6 *) ss)->sin6_port = htons(546); - return; - } - fprintf(stderr, "can't find interface %s\n", name); - exit(1); -} - -/* - * get the local socket address from the command line - * (if it doesn't work, try an interface name) - */ - -void -getlocaladdr(void) -{ - struct addrinfo hints, *res; - char *service; - int ret; - - memset(&hints, 0, sizeof(hints)); - if (ipversion == 4) { - hints.ai_family = AF_INET; - service = "67"; - } else { - hints.ai_family = AF_INET6; - service = "546"; - } - hints.ai_socktype = SOCK_DGRAM; - hints.ai_flags = AI_NUMERICSERV; -#if defined(AI_ADDRCONFIG) - hints.ai_flags |= AI_ADDRCONFIG; -#endif - hints.ai_protocol = IPPROTO_UDP; - - ret = getaddrinfo(localname, service, &hints, &res); - if ((ret == EAI_NONAME) -#ifdef EAI_NODATA - || (ret == EAI_NODATA) -#endif - ) { - isinterface = 1; - getinterface(localname, &localaddr); - return; - } - if (ret != 0) { - fprintf(stderr, - "bad -l: %s\n", - localname, - gai_strerror(ret)); - exit(2); - } - /* refuse multiple addresses */ - if (res->ai_next != NULL) { - fprintf(stderr, - "ambiguous -l\n", - localname); - exit(2); - } - memcpy(&localaddr, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - return; -} - -/* - * get the local socket address from the server one - */ - -void -getlocal(void) -{ - int ret, s; - socklen_t l; - - if (ipversion == 4) { - l = sizeof(struct sockaddr_in); - s = socket(PF_INET, SOCK_DGRAM, 0); - } else { - l = sizeof(struct sockaddr_in6); - s = socket(PF_INET6, SOCK_DGRAM, 0); - } - if (s < 0) { - perror("socket"); - exit(1); - } - if ((ipversion == 4) && (isbroadcast != 0)) { - int on = 1; - - ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); - if (ret < 0) { - perror("setsockopt(SO_BROADCAST)"); - exit(1); - } - } - ret = connect(s, (struct sockaddr *) &serveraddr, l); - if (ret < 0) { - perror("connect"); - exit(1); - } - l = sizeof(localaddr); - ret = getsockname(s, (struct sockaddr *) &localaddr, &l); - if (ret < 0) { - perror("getsockname"); - exit(1); - } - (void) close(s); - /* fill the local port */ - if (ipversion == 4) - ((struct sockaddr_in *) &localaddr)->sin_port = htons(67); - else - ((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546); -} - -/* - * intermediate reporting - * (note: an in-transit packet can be reported as dropped) - */ - -void -reporting(void) -{ - dreport.tv_sec += report; - - if (xscount2 == 0) { - printf("sent: %llu, received: %llu (drops: %lld)", - (unsigned long long) xscount0, - (unsigned long long) xrcount0, - (long long) (xscount0 - xrcount0)); - if (!ISC_TAILQ_EMPTY(&xrcvd0)) { - double avg; - - avg = dsum0 / xrcount0; - printf(" average: %.3f ms", avg * 1e3); - } - } else { - printf("sent: %llu/%llu received: %llu/%llu " - "(drops: %lld/%lld)", - (unsigned long long) xscount0, - (unsigned long long) xscount2, - (unsigned long long) xrcount0, - (unsigned long long) xrcount2, - (long long) (xscount0 - xrcount0), - (long long) (xscount2 - xrcount2)); - if (!ISC_TAILQ_EMPTY(&xrcvd0)) { - double avg0, avg2; - - avg0 = dsum0 / xrcount0; - if (xrcount2 != 0) - avg2 = dsum2 / xrcount2; - else - avg2 = 0.; - printf(" average: %.3f/%.3f ms", - avg0 * 1e3, avg2 * 1e3); - } - } - printf("\n"); -} - -/* - * SIGCHLD handler - */ - -void -reapchild(int sig) -{ - int status; - - sig = sig; - while (wait3(&status, WNOHANG, NULL) > 0) - /* continue */; -} - -/* - * SIGINT handler - */ - -void -interrupt(int sig) -{ - sig = sig; - interrupted = 1; -} - -/* - * '-v' handler - */ - -void -version(void) -{ - fprintf(stderr, "version 0.01\n"); -} - -/* - * usage (from the wiki) - */ - -void -usage(void) -{ - fprintf(stderr, "%s", -"perfdhcp [-hv] [-4|-6] [-r] [-t] [-R] [-b]\n" -" [-n] [-p] [-d] [-D]\n" -" [-l] [-P] [-a]\n" -" [-L] [-s] [-i] [-B] [-c] [-1]\n" -" [-T] [-X] [-O] [-S] [-I]\n" -" [-x] [-w] [server]\n" -"\f\n" -"The [server] argument is the name/address of the DHCP server to\n" -"contact. For DHCPv4 operation, exchanges are initiated by\n" -"transmitting a DHCP DISCOVER to this address.\n" -"\n" -"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n" -"SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n" -"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n" -"multicast address FF02::1:2), or the special name 'servers' to refer\n" -"to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n" -"argument is optional only in the case that -l is used to specify an\n" -"interface, in which case [server] defaults to 'all'.\n" -"\n" -"The default is to perform a single 4-way exchange, effectively pinging\n" -"the server.\n" -"The -r option is used to set up a performance test, without\n" -"it exchanges are initiated as fast as possible.\n" -"\n" -"Options:\n" -"-1: Take the server-ID option from the first received message.\n" -"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n" -"-6: DHCPv6 operation. This is incompatible with the -4 option.\n" -"-a: When the target sending rate is not yet reached,\n" -" control how many exchanges are initiated before the next pause.\n" -"-b: The base MAC, DUID, IP, etc, used to simulate different\n" -" clients. This can be specified multiple times, each instance is\n" -" in the = form, for instance:\n" -" (and default) MAC=00:0c:01:02:03:04.\n" -"-d: Specify the time after which a request is treated as\n" -" having been lost. The value is given in seconds and may contain a\n" -" fractional component. The default is 1 second.\n" -"-E: Offset of the (DHCPv4) secs field / (DHCPv6)\n" -" elapsed-time option in the (second/request) template.\n" -" The value 0 disables it.\n" -"-h: Print this help.\n" -"-i: Do only the initial part of an exchange: DO or SA, depending on\n" -" whether -6 is given.\n" -"-I: Offset of the (DHCPv4) IP address in the requested-IP\n" -" option / (DHCPv6) IA_NA option in the (second/request) template.\n" -"-l: For DHCPv4 operation, specify the local\n" -" hostname/address to use when communicating with the server. By\n" -" default, the interface address through which traffic would\n" -" normally be routed to the server is used.\n" -" For DHCPv6 operation, specify the name of the network interface\n" -" via which exchanges are initiated.\n" -"-L: Specify the local port to use\n" -" (the value 0 means to use the default).\n" -"-O: Offset of the last octet to randomize in the template.\n" -"-P: Initiate first exchanges back to back at startup.\n" -"-r: Initiate DORA/SARR (or if -i is given, DO/SA)\n" -" exchanges per second. A periodic report is generated showing the\n" -" number of exchanges which were not completed, as well as the\n" -" average response latency. The program continues until\n" -" interrupted, at which point a final report is generated.\n" -"-R: Specify how many different clients are used. With 1\n" -" (the default), all requests seem to come from the same client.\n" -"-s: Specify the seed for randomization, making it repeatable.\n" -"-S: Offset of the server-ID option in the\n" -" (second/request) template.\n" -"-T: The name of a file containing the template to use\n" -" as a stream of hexadecimal digits.\n" -"-v: Report the version number of this program.\n" -"-w: Command to call with start/stop at the beginning/end of\n" -" the program.\n" -"-x: Include extended diagnostics in the output.\n" -" 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" -" * 'r': print randomization details\n" -" * 's': print first server-id\n" -" * 't': when finished, print timers of all successful exchanges\n" -" * 'T': when finished, print templates\n" -"-X: Transaction ID (aka. xid) offset in the template.\n" -"\n" -"DHCPv4 only options:\n" -"-B: Force broadcast handling.\n" -"\n" -"DHCPv6 only options:\n" -"-c: Add a rapid commit option (exchanges will be SA).\n" -"\n" -"The remaining options are used only in conjunction with -r:\n" -"\n" -"-D: Abort the test if more than requests have\n" -" been dropped. Use -D0 to abort if even a single request has been\n" -" dropped. If includes the suffix '%', it specifies a\n" -" maximum percentage of requests that may be dropped before abort.\n" -" In this case, testing of the threshold begins after 10 requests\n" -" have been expected to be received.\n" -"-n: Initiate 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: 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" -"-t: Delay in seconds between two periodic reports.\n" -"\n" -"Errors:\n" -"- tooshort: received a too short message\n" -"- orphans: received a message which doesn't match an exchange\n" -" (duplicate, late or not related)\n" -"- locallimit: reached to local system limits when sending a message.\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 -main(const int argc, char * const argv[]) -{ - int opt, flags = 0, ret, i; - long long r; - char *pc; - extern char *optarg; - extern int optind; - -#define OPTIONS "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:" - - /* decode options */ - while ((opt = getopt(argc, argv, OPTIONS)) != -1) - switch (opt) { - case 'h': - usage(); - exit(0); - - case 'v': - version(); - exit(0); - - case '4': - if (ipversion == 6) { - fprintf(stderr, "IP version already set to 6\n"); - usage(); - exit(2); - } - ipversion = 4; - break; - - case '6': - if (ipversion == 4) { - fprintf(stderr, "IP version already set to 4\n"); - usage(); - exit(2); - } - ipversion = 6; - break; - - case 'r': - rate = atoi(optarg); - if (rate <= 0) { - fprintf(stderr, "rate must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 't': - report = atoi(optarg); - if (report <= 0) { - fprintf(stderr, "report must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'R': - r = atoll(optarg); - if (r < 0) { - fprintf(stderr, - "range must not be a negative integer\n"); - usage(); - exit(2); - } - range = (uint32_t) r; - if ((range != 0) && (range != UINT32_MAX)) { - uint32_t s = range + 1; - uint64_t b = UINT32_MAX + 1, m; - - m = (b / s) * s; - if (m == b) - maxrandom = 0; - else - maxrandom = (uint32_t) m; - } - break; - - case 'b': - if (basecnt > 3) { - fprintf(stderr, "too many bases\n"); - usage(); - exit(2); - } - base[basecnt] = optarg; - decodebase(); - basecnt++; - break; - - case 'n': - numreq[gotnumreq] = atoi(optarg); - if (numreq[gotnumreq] <= 0) { - fprintf(stderr, - "num-request must be a positive integer\n"); - usage(); - exit(2); - } - gotnumreq = 1; - break; - - case 'p': - period = atoi(optarg); - if (period <= 0) { - fprintf(stderr, - "test-period must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'd': - losttime[gotlosttime] = atof(optarg); - if (losttime[gotlosttime] <= 0.) { - fprintf(stderr, - "drop-time must be a positive number\n"); - usage(); - exit(2); - } - gotlosttime = 1; - break; - - case 'D': - pc = strchr(optarg, '%'); - if (pc != NULL) { - *pc = '\0'; - maxpdrop[gotmaxdrop] = atof(optarg); - if ((maxpdrop[gotmaxdrop] <= 0) || - (maxpdrop[gotmaxdrop] >= 100)) { - fprintf(stderr, - "invalid drop-time percentage\n"); - usage(); - exit(2); - } - gotmaxdrop = 1; - break; - } - maxdrop[gotmaxdrop] = atoi(optarg); - if (maxdrop[gotmaxdrop] <= 0) { - fprintf(stderr, - "max-drop must be a positive integer\n"); - usage(); - exit(2); - } - gotmaxdrop = 1; - break; - - case 'l': - localname = optarg; - break; - - case 'P': - preload = atoi(optarg); - if (preload < 0) { - fprintf(stderr, - "preload must not be a negative integer\n"); - usage(); - exit(2); - } - break; - - case 'a': - aggressivity = atoi(optarg); - if (aggressivity <= 0) { - fprintf(stderr, - "aggressivity must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'L': - localport = atoi(optarg); - if (localport < 0) { - fprintf(stderr, - "local-port must not be a negative integer\n"); - usage(); - exit(2); - } - if (localport > (int) UINT16_MAX) { - fprintf(stderr, - "local-port must be lower than %d\n", - (int) UINT16_MAX); - usage(); - exit(2); - } - break; - - case 's': - seeded = 1; - seed = (unsigned int) atol(optarg); - break; - - case 'i': - simple = 1; - break; - - case 'B': - isbroadcast = 1; - break; - - case 'c': - rapidcommit = 1; - break; - - case '1': - usefirst = 1; - break; - - case 'T': - if (templatefile[0] != NULL) { - if (templatefile[1] != NULL) { - fprintf(stderr, - "template-files are already set\n"); - usage(); - exit(2); - } - templatefile[1] = optarg; - } else - templatefile[0] = optarg; - break; - - case 'X': - if (xidoffset[0] >= 0) - i = 1; - else - i = 0; - xidoffset[i] = atoi(optarg); - if (xidoffset[i] <= 0) { - fprintf(stderr, - "xid-offset must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'O': - if (rndoffset[0] >= 0) - i = 1; - else - i = 0; - rndoffset[i] = atoi(optarg); - if (rndoffset[i] < 3) { - fprintf(stderr, - "random-offset must be greater than 3\n"); - usage(); - exit(2); - } - break; - - case 'E': - elpoffset = atoi(optarg); - if (elpoffset < 0) { - fprintf(stderr, - "time-offset must not be a " - "negative integer\n"); - usage(); - exit(2); - } - break; - - case 'S': - sidoffset = atoi(optarg); - if (sidoffset <= 0) { - fprintf(stderr, - "srvid-offset must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'I': - ripoffset = atoi(optarg); - if (ripoffset <= 0) { - fprintf(stderr, - "ip-offset must be a positive integer\n"); - usage(); - exit(2); - } - break; - - case 'x': - diags = optarg; - break; - - case 'w': - wrapped = optarg; - break; - - default: - usage(); - exit(2); - } - - /* adjust some global variables */ - if (ipversion == 0) - ipversion = 4; - if (templatefile[1] != NULL) { - if (xidoffset[1] < 0) - xidoffset[1] = xidoffset[0]; - if (rndoffset[1] < 0) - rndoffset[1] = rndoffset[0]; - } - - /* when required, print the internal view of the command line */ - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { - printf("IPv%d", ipversion); - if (simple != 0) { - if (ipversion == 4) - printf(" DO only"); - else - printf(" SA only"); - } - if (rate != 0) - printf(" rate=%d", rate); - if (report != 0) - printf(" report=%d", report); - if (range != 0) { - if (strchr(diags, 'r') != NULL) - printf(" range=0..%d [0x%x]", - range, - (unsigned int) maxrandom); - else - printf(" range=0..%d", range); - } - if (basecnt != 0) - for (i = 0; i < basecnt; i++) - printf(" base[%d]='%s'", i, base[i]); - if (gotnumreq != 0) - printf(" num-request=%d,%d", numreq[0], numreq[1]); - if (period != 0) - printf(" test-period=%d", period); - printf(" drop-time=%g,%g", losttime[0], losttime[1]); - if ((maxdrop[0] != 0) || (maxdrop[1] != 0)) - printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]); - if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.)) - printf(" max-drop=%2.2f%%,%2.2f%%", - maxpdrop[0], maxpdrop[1]); - if (preload != 0) - printf(" preload=%d", preload); - printf(" aggressivity=%d", aggressivity); - if (localport != 0) - printf(" local-port=%d", localport); - if (seeded) - printf(" seed=%u", seed); - if (isbroadcast != 0) - printf(" broadcast"); - if (rapidcommit != 0) - printf(" rapid-commit"); - if (usefirst != 0) - printf(" use-first"); - if ((templatefile[0] != NULL) && (templatefile[1] == NULL)) - printf(" template-file='%s'", templatefile[0]); - else if (templatefile[1] != NULL) - printf(" template-file='%s','%s'", - templatefile[0], templatefile[1]); - if ((xidoffset[0] >= 0) && (xidoffset[1] < 0)) - printf(" xid-offset=%d", xidoffset[0]); - else if (xidoffset[1] >= 0) - printf(" xid-offset=%d,%d", - xidoffset[0], xidoffset[1]); - if ((rndoffset[0] >= 0) && (rndoffset[1] < 0)) - printf(" xid-offset=%d", rndoffset[0]); - else if (rndoffset[1] >= 0) - printf(" xid-offset=%d,%d", - rndoffset[0], rndoffset[1]); - if (elpoffset >= 0) - printf(" time-offset=%d", elpoffset); - if (sidoffset >= 0) - printf(" srvid-offset=%d", sidoffset); - if (ripoffset >= 0) - printf(" ip-offset=%d", ripoffset); - printf(" diagnotic-selectors='%s'", diags); - if (wrapped != NULL) - printf(" wrapped='%s'", wrapped); - printf("\n"); - } - - /* check DHCPv4 only options */ - if ((ipversion != 4) && (isbroadcast != 0)) { - fprintf(stderr, "-b is not compatible with IPv6 (-6)\n"); - usage(); - exit(2); - } - - /* check DHCPv6 only options */ - if ((ipversion != 6) && (rapidcommit != 0)) { - fprintf(stderr, "-6 (IPv6) must be set to use -c\n"); - usage(); - exit(2); - } - - /* check 4-packet (aka not simple) mode options */ - if ((simple != 0) && (numreq[1] != 0)) { - fprintf(stderr, - "second -n is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (losttime[1] != 1.)) { - fprintf(stderr, - "second -d is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && - ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) { - fprintf(stderr, - "second -D is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (usefirst != 0)) { - fprintf(stderr, - "-1 is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (templatefile[1] != NULL)) { - fprintf(stderr, - "second -T is not " - "compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (xidoffset[1] >= 0)) { - fprintf(stderr, - "second -X is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (rndoffset[1] >= 0)) { - fprintf(stderr, - "second -O= 0)) { - fprintf(stderr, - "-E is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (sidoffset >= 0)) { - fprintf(stderr, - "-S is not compatible with -i\n"); - usage(); - exit(2); - } - if ((simple != 0) && (ripoffset >= 0)) { - fprintf(stderr, - "-I is not compatible with -i\n"); - usage(); - exit(2); - } - - - /* check simple mode options */ - if ((simple == 0) && (rapidcommit != 0)) { - fprintf(stderr, "-i must be set to use -c\n"); - usage(); - exit(2); - } - - /* check rate '-r' options */ - if ((rate == 0) && (report != 0)) { - fprintf(stderr, - "-r must be set to use -t\n"); - usage(); - exit(2); - } - if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) { - fprintf(stderr, - "-r must be set to use -n\n"); - usage(); - exit(2); - } - if ((rate == 0) && (period != 0)) { - fprintf(stderr, - "-r must be set to use -p\n"); - usage(); - exit(2); - } - if ((rate == 0) && - ((maxdrop[0] != 0) || (maxdrop[1] != 0) || - (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) { - fprintf(stderr, - "-r must be set to use -D\n"); - usage(); - exit(2); - } - - /* check (first) template file options */ - if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) { - fprintf(stderr, - "-T must be set to " - "use -X\n"); - usage(); - exit(2); - } - if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) { - fprintf(stderr, - "-T must be set to " - "use -O\n"); - usage(); - exit(2); - } - - /* check (second) template file options */ - if ((templatefile[1] == NULL) && (elpoffset >= 0)) { - fprintf(stderr, - "second/request -T must be set to " - "use -E\n"); - usage(); - exit(2); - } - if ((templatefile[1] == NULL) && (sidoffset >= 0)) { - fprintf(stderr, - "second/request -T must be set to " - "use -S\n"); - usage(); - exit(2); - } - if ((templatefile[1] == NULL) && (ripoffset >= 0)) { - fprintf(stderr, - "second/request -T must be set to " - "use -I\n"); - usage(); - exit(2); - } - - /* check various template file(s) and other condition(s) options */ - if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) { - fprintf(stderr, - "-O must be set when " - "-T and -R are used\n"); - usage(); - exit(2); - } - if ((templatefile[1] != NULL) && (sidoffset < 0)) { - fprintf(stderr, - "-S must be set when second " - "-T is used\n"); - usage(); - exit(2); - } - if ((templatefile[1] != NULL) && (ripoffset < 0)) { - fprintf(stderr, - "-I must be set when second " - "-T is used\n"); - usage(); - exit(2); - } - - /* get the server argument */ - if (optind < argc - 1) { - fprintf(stderr, "extra arguments?\n"); - usage(); - exit(2); - } - if (optind == argc - 1) { - servername = argv[optind]; - /* decode special cases */ - if ((ipversion == 4) && - (strcmp(servername, "all") == 0)) { - flags = AI_NUMERICHOST; - isbroadcast = 1; - servername = "255.255.255.255"; - } else if ((ipversion == 6) && - (strcmp(servername, "all") == 0)) { - flags = AI_NUMERICHOST; - servername = "FF02::1:2"; - } else if ((ipversion == 6) && - (strcmp(servername, "servers") == 0)) { - flags = AI_NUMERICHOST; - servername = "FF05::1:3"; - } - } - - /* handle the local '-l' address/interface */ - if (localname != NULL) { - /* given */ - getlocaladdr(); - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { - if (isinterface) - printf("interface='%s'\n", localname); - else - printf("local-addr='%s'\n", localname); - } - /* get the not given server from it */ - if (servername == NULL) { - if (isinterface && (ipversion == 4)) { - flags = AI_NUMERICHOST; - isbroadcast = 1; - servername = "255.255.255.255"; - } else if (isinterface && (ipversion == 6)) { - flags = AI_NUMERICHOST; - servername = "FF02::1:2"; - } else { - fprintf(stderr, - "without an interface " - "server is required\n"); - usage(); - exit(2); - } - } - } else if (servername == NULL) { - fprintf(stderr, "without an interface server is required\n"); - usage(); - exit(2); - } - /* get the server socket address */ - getserveraddr(flags); - /* finish local/server socket address stuff and print it */ - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) - printf("server='%s'\n", servername); - if (localname == NULL) - getlocal(); - if ((diags != NULL) && (strchr(diags, 'a') != NULL)) { - char addr[NI_MAXHOST]; - - ret = getnameinfo((struct sockaddr *) &localaddr, - sizeof(localaddr), - addr, - NI_MAXHOST, - NULL, - 0, - NI_NUMERICHOST); - if (ret != 0) { - fprintf(stderr, - "can't get the local address: %s\n", - gai_strerror(ret)); - exit(1); - } - printf("local address='%s'\n", addr); - } - - /* initialize exchange structures */ - inits(); - - /* get the socket descriptor and template(s) */ - if (ipversion == 4) { - getsock4(); - if (templatefile[0] == NULL) - build_template_discover4(); - else - get_template_discover4(); - if (simple == 0) { - if (templatefile[1] == NULL) - build_template_request4(); - else - get_template_request4(); - } - } else { - getsock6(); - if (duid_prefix != NULL) { - if (templatefile[0] == NULL) - build_template_solicit6(); - else - get_template_solicit6(); - if (simple == 0) { - if (templatefile[1] == NULL) - build_template_request6(); - else - get_template_request6(); - } - } - } - /* sanity check */ - if ((unsigned) sock > FD_SETSIZE) { - fprintf(stderr, "socket descriptor (%d) too large?!\n", sock); - exit(1); - } - /* make the socket descriptor not blocking */ - flags = fcntl(sock, F_GETFL, 0); - if (flags < 0) { - perror("fcntl(F_GETFL)"); - exit(1); - } - if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { - perror("fcntl(F_SETFL)"); - exit(1); - } - - /* wrapped start */ - if (wrapped != NULL) { - pid_t pid; - - (void) signal(SIGCHLD, reapchild); - pid = fork(); - if (pid < 0) { - perror("fork"); - exit(1); - } else if (pid == 0) - (void) execlp(wrapped, "start", (char *) NULL); - } - - /* boot is done! */ - if (clock_gettime(CLOCK_REALTIME, &boot) < 0) { - perror("clock_gettime(boot)"); - exit(1); - } - - /* compute the next intermediate reporting date */ - if (report != 0) { - dreport.tv_sec = boot.tv_sec + report; - dreport.tv_nsec = boot.tv_nsec; - } - - /* compute the DUID (the current date is needed) */ - if ((ipversion == 6) && (duid_prefix == NULL)) { - uint32_t curdate; - - duid_length = 14; - duid_prefix = (uint8_t *) malloc(duid_length); - if (duid_prefix == NULL) { - perror("malloc(duid)"); - exit(1); - } - duid_prefix[0] = DHCP6_DUID_LLT >> 8; - duid_prefix[1] = DHCP6_DUID_LLT; - duid_prefix[2] = DHCP_HTYPE_ETHER >> 8; - duid_prefix[3] = DHCP_HTYPE_ETHER; - curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH); - memcpy(duid_prefix + 4, &curdate, 4); - memcpy(duid_prefix + 8, mac_prefix, 6); - /* the DUID is in template(s) */ - if (templatefile[0] == NULL) - build_template_solicit6(); - else - get_template_solicit6(); - if (simple == 0) { - if (templatefile[1] == NULL) - build_template_request6(); - else - get_template_request6(); - } - } - - /* seed the random generator */ - if (seeded == 0) - seed = (unsigned int) (boot.tv_sec + boot.tv_nsec); - srandom(seed); - - /* preload the server with at least one packet */ - compsend = preload + 1; - for (i = 0; i <= preload; i++) { - if (ipversion == 4) - ret = send4(); - else - ret = send6(); - if (ret < 0) { - /* failure at the first packet is fatal */ - if (i == 0) { - fprintf(stderr, - "initial send failed: %s\n", - strerror(-ret)); - exit(1); - } - if ((errno == EAGAIN) || - (errno == EWOULDBLOCK) || - (errno == ENOBUFS) || - (errno == ENOMEM)) - locallimit++; - fprintf(stderr, "preload send: %s\n", strerror(-ret)); - break; - } - } - - /* required only before the interrupted flag check */ - (void) signal(SIGINT, interrupt); - - /* main loop */ - for (;;) { - struct timespec now, ts; - fd_set rfds; - - /* immediate loop exit conditions */ - if (interrupted) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("interrupted\n"); - break; - } - if (fatal) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("got a fatal error\n"); - break; - } - - /* get the date and use it */ - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - perror("clock_gettime(now)"); - fatal = 1; - continue; - } - if ((period != 0) && - ((boot.tv_sec + period < now.tv_sec) || - ((boot.tv_sec + period == now.tv_sec) && - (boot.tv_nsec < now.tv_nsec)))) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached test-period\n"); - break; - } - if ((report != 0) && - ((dreport.tv_sec < now.tv_sec) || - ((dreport.tv_sec == now.tv_sec) && - (dreport.tv_nsec < now.tv_nsec)))) - reporting(); - - /* compute the delay for the next send */ - due = last; - if (rate == 1) - due.tv_sec += 1; - else if (rate != 0) - due.tv_nsec += 1010000000 / rate; - else - due.tv_nsec += 1; - while (due.tv_nsec >= 1000000000) { - due.tv_sec += 1; - due.tv_nsec -= 1000000000; - } - ts = due; - ts.tv_sec -= now.tv_sec; - ts.tv_nsec -= now.tv_nsec; - while (ts.tv_nsec < 0) { - ts.tv_sec -= 1; - ts.tv_nsec += 1000000000; - } - /* the send was already due? */ - if (ts.tv_sec < 0) { - ts.tv_sec = ts.tv_nsec = 0; - latesent++; - } - - /* pselect() */ - FD_ZERO(&rfds); - FD_SET(sock, &rfds); - ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL); - if (ret < 0) { - if (errno == EINTR) - continue; - perror("pselect"); - fatal = 1; - continue; - } - - /* packet(s) to receive */ - while (ret == 1) { - if (ipversion == 4) - receive4(); - else - receive6(); - if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0) - ret = 0; - else - multrcvd++; - } - if (fatal) - continue; - - /* check receive loop exit conditions */ - if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached num-request0\n"); - break; - } - if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached num-request2\n"); - break; - } - if ((maxdrop[0] != 0) && - ((int) (xscount0 - xrcount0) > maxdrop[0])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached max-drop%s (absolute)\n", - simple != 0 ? "" : "0"); - break; - } - if ((maxdrop[1] != 0) && - ((int) (xscount2 - xrcount2) > maxdrop[1])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached max-drop2 (absolute)\n"); - break; - } - if ((maxpdrop[0] != 0.) && - (xscount0 > 10) && - (((100. * (xscount0 - xrcount0)) / xscount0) - > maxpdrop[0])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached max-drop%s (percent)\n", - simple != 0 ? "" : "0"); - break; - } - if ((maxpdrop[1] != 0.) && - (xscount2 > 10) && - (((100. * (xscount2 - xrcount2)) / xscount2) - > maxpdrop[1])) { - if ((diags != NULL) && (strchr(diags, 'e') != NULL)) - printf("reached max-drop2 (percent)\n"); - break; - } - - /* compute how many packets to send */ - if (clock_gettime(CLOCK_REALTIME, &now) < 0) { - perror("clock_gettime(now2)"); - fatal = 1; - continue; - } - if ((now.tv_sec > due.tv_sec) || - ((now.tv_sec == due.tv_sec) && - (now.tv_nsec >= due.tv_nsec))) { - double tosend; - - if (rate != 0) { - tosend = (now.tv_nsec - due.tv_nsec) / 1e9; - tosend += now.tv_sec - due.tv_sec; - tosend *= rate; - tosend += 1; - if (tosend > (double) aggressivity) - i = aggressivity; - else - i = (int) tosend; - } else - i = aggressivity; - compsend += i; - /* send packets */ - for (;;) { - if (ipversion == 4) - ret = send4(); - else - ret = send6(); - if (ret < 0) { - if ((errno == EAGAIN) || - (errno == EWOULDBLOCK) || - (errno == ENOBUFS) || - (errno == ENOMEM)) - locallimit++; - fprintf(stderr, - "send: %s\n", strerror(-ret)); - break; - } - i--; - if (i == 0) - break; - /* check for late packets to receive */ - if (recv(sock, ibuf, sizeof(ibuf), - MSG_PEEK) > 0) { - latercvd++; - if (ipversion == 4) - receive4(); - else - receive6(); - } - } - } else - /* there was no packet to send */ - shortwait++; - } - - /* after main loop: finished */ - if (clock_gettime(CLOCK_REALTIME, &finished) < 0) - perror("clock_gettime(finished)"); - - /* wrapped stop */ - if (wrapped != NULL) { - pid_t pid; - - pid = fork(); - if (pid == 0) - (void) execlp(wrapped, "stop", (char *) NULL); - } - - /* main statictics */ - if (xscount2 == 0) - printf("sent: %llu, received: %llu (drops: %lld)\n", - (unsigned long long) xscount0, - (unsigned long long) xrcount0, - (long long) (xscount0 - xrcount0)); - else - printf("sent: %llu/%llu, received: %llu/%llu " - "(drops: %lld/%lld)\n", - (unsigned long long) xscount0, - (unsigned long long) xscount2, - (unsigned long long) xrcount0, - (unsigned long long) xrcount2, - (long long) (xscount0 - xrcount0), - (long long) (xscount2 - xrcount2)); - printf("tooshort: %llu, orphans: %llu, local limits: %llu\n", - (unsigned long long) tooshort, - (unsigned long long) orphans, - (unsigned long long) locallimit); - - /* print the rate */ - if (finished.tv_sec != 0) { - double dall, erate; - - dall = (finished.tv_nsec - boot.tv_nsec) / 1e9; - dall += finished.tv_sec - boot.tv_sec; - erate = xrcount0 / dall; - if (rate != 0) - printf("rate: %f (expected %d)\n", erate, rate); - else - printf("rate: %f\n", erate); - } - - /* rate processing instrumentation */ - if ((diags != NULL) && (strchr(diags, 'i') != NULL)) { - printf("latesent: %llu, compsend: %llu, shortwait: %llu\n" - "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n", - (unsigned long long) latesent, - (unsigned long long) compsend, - (unsigned long long) shortwait, - (unsigned long long) multrcvd, - (unsigned long long) latercvd, - (unsigned long long) collected[0], - (unsigned long long) collected[1]); - } - - /* round-time trip statistics */ - if (xrcount2 != 0) { - double avg0, avg2, stddev0, stddev2; - - avg0 = dsum0 / xrcount0; - avg2 = dsum2 / xrcount2; - stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0); - stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2); - printf("RTT0: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n", - dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3); - printf("RTT2: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n", - dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3); - } else if (xrcount0 != 0) { - double avg, stddev; - - avg = dsum0 / xrcount0; - stddev = sqrt(dsumsq0 / xrcount0 - avg * avg); - printf("RTT%s: min/avg/max/stddev: %.3f/%.3f/%.3f/%.3f ms\n", - simple != 0 ? "" : "0", - dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3); - } - - /* (first) server-ID option content */ - if ((diags != NULL) && (strchr(diags, 's') != NULL) && - !ISC_TAILQ_EMPTY(&xrcvd0)) { - struct exchange *x; - size_t n; - - printf("server-id: "); - x = ISC_TAILQ_FIRST(&xrcvd0); - if (ipversion == 4) - n = 2; - else - n = 4; - for (; n < x->sidlen; n++) - printf("%02hhx", x->sid[n]); - printf("\n"); - } - - /* all time-stamps */ - if ((diags != NULL) && (strchr(diags, 't') != NULL) && - !ISC_TAILQ_EMPTY(&xrcvd0)) { - struct exchange *x; - - printf("\n\n"); - ISC_TAILQ_FOREACH(x, &xrcvd0, gchain) - printf("%ld.%09ld %ld.%09ld\n", - (long) x->ts0.tv_sec, x->ts0.tv_nsec, - (long) x->ts1.tv_sec, x->ts1.tv_nsec); - - } - if ((diags != NULL) && (strchr(diags, 't') != NULL) && - !ISC_TAILQ_EMPTY(&xrcvd2)) { - struct exchange *x; - - printf("--\n"); - ISC_TAILQ_FOREACH(x, &xrcvd2, gchain) - printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n", - (long) x->ts0.tv_sec, x->ts0.tv_nsec, - (long) x->ts1.tv_sec, x->ts1.tv_nsec, - (long) x->ts2.tv_sec, x->ts2.tv_nsec, - (long) x->ts3.tv_sec, x->ts3.tv_nsec); - - } - if ((diags != NULL) && (strchr(diags, 't') != NULL) && - !ISC_TAILQ_EMPTY(&xrcvd0)) - printf("\n\n"); - - /* template(s) */ - if ((diags != NULL) && (strchr(diags, 'T') != NULL)) { - size_t n; - - if (ipversion == 4) { - printf("length = %zu\n", length_discover4); - printf("xid offset = %d\n", DHCP_OFF_XID); - printf("xid length = 4\n"); - printf("random offset = %zu\n", random_discover4); - printf("content:\n"); - for (n = 0; n < length_discover4; n++) { - printf("%s%02hhx", - (n & 15) == 0 ? "" : " ", - template_discover4[n]); - if ((n & 15) == 15) - printf("\n"); - } - if ((n & 15) != 15) - printf("\n"); - if (simple != 0) - goto doneT; - printf("--\n"); - printf("length = %zu\n", length_request4); - printf("xid offset = %d\n", DHCP_OFF_XID); - printf("xid length = 4\n"); - printf("random offset = %zu\n", random_request4); - if (elapsed_request4 > 0) - printf("secs offset = %zu\n", - elapsed_request4); - printf("server-id offset = %zu\n", serverid_request4); - printf("server-id length = %d\n", DHCP_OPTLEN_SRVID); - printf("content:\n"); - printf("requested-ip-address offset = %zu\n", - reqaddr_request4); - printf("requested-ip-address length = %d\n", 4); - for (n = 0; n < length_request4; n++) { - printf("%s%02hhx", - (n & 15) == 0 ? "" : " ", - template_request4[n]); - if ((n & 15) == 15) - printf("\n"); - } - printf("\n"); - } else { - printf("length = %zu\n", length_solicit6); - printf("xid offset = %d\n", DHCP6_OFF_XID); - printf("xid length = 3\n"); - printf("random offset = %zu\n", random_solicit6); - for (n = 0; n < length_solicit6; n++) { - printf("%s%02hhx", - (n & 15) == 0 ? "" : " ", - template_solicit6[n]); - if ((n & 15) == 15) - printf("\n"); - } - if ((n & 15) != 15) - printf("\n"); - if (simple != 0) - goto doneT; - printf("--\n"); - printf("length = %zu\n", length_request6); - printf("xid offset = %d\n", DHCP_OFF_XID); - printf("xid length = 4\n"); - printf("random offset = %zu\n", random_request6); - if (elapsed_request6 > 0) - printf("secs offset = %zu\n", - elapsed_request6); - printf("server-id offset = %zu\n", serverid_request6); - printf("content:\n"); - printf("requested-ip-address offset = %zu\n", - reqaddr_request6); - for (n = 0; n < length_request6; n++) { - printf("%s%02hhx", - (n & 15) == 0 ? "" : " ", - template_request6[n]); - if ((n & 15) == 15) - printf("\n"); - } - printf("\n"); - } - } - doneT: - - /* compute the exit code (and exit) */ - if (fatal) - exit(1); - else if ((xscount0 == xrcount0) && (xscount2 == xrcount2)) - exit(0); - else - exit(3); -} - -#endif /* HAVE_GETIFADDRS */