Commit d6f64f5a authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[1954] Switching to fully compile with C++ to get rid of clang linking errors

parent a5171dfc
......@@ -12,8 +12,13 @@ if USE_STATIC_LINK
AM_LDFLAGS += -static
endif
# We have to suppress errors because we are compiling C code with CXX
# We have to do this to link with new C++ pieces of code
perfdhcp_CXXFLAGS = $(AM_CXXFLAGS)
perfdhcp_CXXFLAGS += -Wno-error -Wno-writestrings
pkglibexec_PROGRAMS = perfdhcp
perfdhcp_SOURCES = perfdhcp.c
perfdhcp_SOURCES = perfdhcp.cc
perfdhcp_SOURCES += command_options.cc command_options.h
perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
......@@ -12,8 +12,6 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#define __STDC_LIMIT_MACROS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
......
/*
* 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 <config.h>
#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 <stdio.h>
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 */
#define __STDC_LIMIT_MACROS
#ifdef __linux__
#define _GNU_SOURCE
#endif
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <ifaddrs.h>
#include <math.h>
#include <netdb.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifndef HAVE_PSELECT
#include <assert.h>
/* 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 = (caddr_t)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 = (caddr_t)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, gch