BIND 10 trac1265, updated. 809dc75718908b39668046c2d3db8bfb51947ecc [1265] Code and tests for network IO

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Nov 18 05:24:26 UTC 2011


The branch, trac1265 has been updated
       via  809dc75718908b39668046c2d3db8bfb51947ecc (commit)
      from  363be148c3c8ba79df67ba698f7dbf45f4a00f59 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 809dc75718908b39668046c2d3db8bfb51947ecc
Author: John DuBois <johnd at isc.org>
Date:   Thu Nov 17 21:20:18 2011 -0800

    [1265]
    Code and tests for network IO

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |    2 +
 tests/tools/perfdhcp/cloptions.cc                  |  279 +++++++++++
 tests/tools/perfdhcp/cloptions.h                   |   49 ++
 tests/tools/perfdhcp/dhcp.h                        |  202 ++++++++
 tests/tools/perfdhcp/dhcp6.h                       |  213 +++++++++
 tests/tools/perfdhcp/dkdebug.cc                    |   65 +++
 tests/tools/perfdhcp/dkdebug.h                     |   88 ++++
 tests/tools/perfdhcp/perfdhcp.cc                   |  372 +++++++++++++++
 tests/tools/perfdhcp/perfdhcp.h                    |  100 ++++
 tests/tools/perfdhcp/procconf.cc                   |  486 ++++++++++++++++++++
 tests/tools/perfdhcp/procconf.h                    |  128 +++++
 tests/tools/perfdhcp/tests/Makefile.am             |    1 -
 .../tools/perfdhcp/tests/sendreceive_unittest.cc   |   27 +-
 13 files changed, 2000 insertions(+), 12 deletions(-)
 create mode 100644 tests/tools/perfdhcp/cloptions.cc
 create mode 100644 tests/tools/perfdhcp/cloptions.h
 create mode 100644 tests/tools/perfdhcp/dhcp.h
 create mode 100644 tests/tools/perfdhcp/dhcp6.h
 create mode 100644 tests/tools/perfdhcp/dkdebug.cc
 create mode 100644 tests/tools/perfdhcp/dkdebug.h
 create mode 100644 tests/tools/perfdhcp/perfdhcp.cc
 create mode 100644 tests/tools/perfdhcp/perfdhcp.h
 create mode 100644 tests/tools/perfdhcp/procconf.cc
 create mode 100644 tests/tools/perfdhcp/procconf.h
 copy src/lib/server_common/tests/run_unittests.cc => tests/tools/perfdhcp/tests/sendreceive_unittest.cc (58%)

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 2692ddb..d65a47a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -935,6 +935,8 @@ AC_CONFIG_FILES([Makefile
                  tests/tools/Makefile
                  tests/tools/badpacket/Makefile
                  tests/tools/badpacket/tests/Makefile
+                 tests/tools/perfdhcp/Makefile
+                 tests/tools/perfdhcp/tests/Makefile
                ])
 AC_OUTPUT([doc/version.ent
            src/bin/cfgmgr/b10-cfgmgr.py
diff --git a/tests/tools/perfdhcp/cloptions.cc b/tests/tools/perfdhcp/cloptions.cc
new file mode 100644
index 0000000..7e06d39
--- /dev/null
+++ b/tests/tools/perfdhcp/cloptions.cc
@@ -0,0 +1,279 @@
+/*
+ * 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 <stdio.h>
+#include "procconf.h"
+#include "perfdhcp.h"
+#include "cloptions.h"
+#include "dkdebug.h"
+
+static void printHelp(const char* progName, const char* usage);
+static void initialize(void);
+
+// The current version information
+const char* VERSION = "perfdhcp v1.0 2011-10-30";
+
+static int v6 = 0;                      // DHCPv6 operation (-6)
+static int initialOnly = 0;             // Do only initial exchange (-i)
+static unsigned rate = 0;               // Request rate (-r)
+static unsigned numRequest = 0;         // Number of requests (-n)
+static double dropTime = 0.0;           // Response timeout (-d)
+static double testPeriod = 0.0;         // Test period (-p)
+static const char* server = NULL;       // Server to contact
+static const char* maxDropOpt = NULL;   // Max dropped responses (-D)
+static const char* localName = NULL;    // Local host/interface (-l)
+
+/*
+ * Return value:
+ * 0 if the command has been satisfied and the program should exit 0.
+ * 2 for usage error, in which case an error message will have been printed.
+ * 1 if argument processing was successful and the program should continue.
+ */
+int
+procArgs(int argc, const char* argv[]) {
+    char usage[] =
+        "Usage:\n\
+perfdhcp [-hv] [-4|-6] [-r<rate>] [-n<num-request>] [-p<test-period>]\n\
+         [-d<drop-time>] [-D<max-drop>] [-l<local-addr|interface>] [-i]\n\
+         [-x<diagnostic-selector>] [server]\n";
+    int v4 = 0;                 /* DHCPv4 operation explicitly requested */
+    const char* msg;            /* Failure message from procOpts() */
+    int help = 0;               /* Help requested */
+    int versionReq = 0;         /* Version requested */
+    const char* diagStr = NULL; /* Diagnostics requested (-x) */
+
+    /* option descriptions */
+    confvar_t optConf[] = {
+        { 'h', NULL,        CF_SWITCH,    &help,        1 },
+        { 'v', NULL,        CF_SWITCH,    &versionReq,  1 },
+        { '4', NULL,        CF_SWITCH,    &v4,          1 },
+        { '6', NULL,        CF_SWITCH,    &v6,          1 },
+        { 'i', NULL,        CF_SWITCH,    &initialOnly, 1 },
+        { 'l', NULL,        CF_NE_STRING, &localName,   0 },
+        { 'r', NULL,        CF_POS_INT,   &rate,        0 },
+        { 'n', NULL,        CF_POS_INT,   &numRequest,  0 },
+        { 'd', NULL,        CF_POS_FLOAT, &dropTime,    0 },
+        { 'p', NULL,        CF_POS_FLOAT, &testPeriod,  0 },
+        { 'D', NULL,        CF_NE_STRING, &maxDropOpt,  0 },
+        { 'x', NULL,        CF_STRING,    &diagStr,     0 },
+        { '\0', NULL,       CF_ENDLIST,   NULL,         0 }
+    };
+
+    /* diagnostic map */
+    const struct dkdesc diagLetters[] = {
+        { 's', DK_SOCK },
+        { 'm', DK_MSG },
+        { 'p', DK_PACKET },
+        { 'a', DK_ALL },
+        { '\0', 0 }
+    };
+
+    initialize();
+    /* Process command line options */
+    msg = procOpts(&argc, &argv, optConf, NULL, PROGNAME, NULL);
+    if (msg != NULL) {
+        fprintf(stderr, "%s: %s\n", PROGNAME, msg);
+        return(2);
+    }
+
+    if (help) {
+        printHelp(PROGNAME, usage);
+        return(0);
+    }
+    if (versionReq) {
+        printf("%s\n", VERSION);
+        return(0);
+    }
+    if (diagStr != NULL) {
+        char c;
+        if ((c = dk_setup(diagStr, diagLetters)) != '\0') {
+            fprintf(stderr,
+                    "%s: Invalid selector character given with -x: '%c'\n",
+                    PROGNAME, c);
+            return(2);
+        }
+    }
+
+    if (v4 && v6) {
+        fprintf(stderr, "%s: Must not give -4 and -6 together.\n", PROGNAME);
+        return(2);
+    }
+    switch (argc) {
+    case 0:
+        if (v6 && localName != NULL) {
+            server = "all";
+        } else {
+            if (v6) {
+                fprintf(stderr,
+                        "%s: Use -l to specify an interface name.\n\%s\n",
+                        PROGNAME, usage);
+            }
+            else {
+                fprintf(stderr, "%s: Must specify a DHCP server.\n\%s\n",
+                        PROGNAME, usage);
+            }
+            return(2);
+        }
+        break;
+    case 1:
+        server = argv[0];
+        break;
+    default:
+        fprintf(stderr, "%s: Too many arguments.\n\%s\n", PROGNAME, usage);
+        return(2);
+    }
+    return(1);
+}
+
+/*
+ * Initialize values set by procArgs().
+ * Initialized though they are static to allow for repeated calls for testing.
+ */
+static void
+initialize(void) {
+    v6 = 0;
+    initialOnly = 0;
+    rate = 0;
+    numRequest = 0;
+    dropTime = 0.0;
+    testPeriod = 0.0;
+    maxDropOpt = NULL;
+    localName = NULL;
+    server = NULL;
+}
+
+static void
+printHelp(const char* progName, const char* usage) {
+    printf(
+        "%s: Execute a performance test against a DHCP server.\n\
+%s\n\
+The [server] argument is the name/address of the DHCP server to contact.\n\
+For DHCPv4 operation, exchanges are initiated by transmitting a DHCP\n\
+DISCOVER to this address.  For DHCPv6 operation, exchanges are initiated\n\
+by transmitting a DHCP SOLICIT to this address.  In the DHCPv6 case, the\n\
+special name \"all\" can be used to refer to\n\
+All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2), or\n\
+the special name \"servers\" to refer to All_DHCP_Servers (the multicast\n\
+address FF05::1:3).  The [server] argument is optional only in the case\n\
+that -l is used to specify an interface, in which case [server] defaults\n\
+to \"all\".\n\
+\n\
+Exchanges are initiated by transmitting a DHCP SOLICIT to\n\
+All_DHCP_Relay_Agents_and_Servers (the multicast address FF02::1:2) via\n\
+this interface.\n\
+\n\
+The default is to perform a single 4-way exchange, effectively pinging the\n\
+server.  The -r option is used to set up a performance test.\n\
+\n\
+Options:\n\
+-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n\
+-6: DHCPv6 operation. This is incompatible with the -4 option.\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\
+-l<local-addr|interface>: 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 normally be\n\
+    routed to the server is used.\n\
+    For DHCPv6 operation, specify the name of the network interface via\n\
+    which exchanges are initiated.  This must be specified unless a server\n\
+    name is given, in which case the interface through which traffic would\n\
+    normally be routed to the server is used.\n\
+-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA) exchanges per\n\
+    second.  A periodic report is generated showing the number of exchanges\n\
+    which were not completed, as well as the average response latency.  The\n\
+    program continues until interrupted, at which point a final report is\n\
+    generated.\n\
+-v: Report the version number of this program.\n\
+-x<diagnostic-selector>: Include extended diagnostics in the output.\n\
+    <diagnostic-selector> is a string of characters, each specifying an\n\
+    operation for which verbose output is desired.  The selector characters\n\
+    are:\n\
+    [TO BE ADDED]\n\
+\n\
+The remaining options are used only in conjunction with -r:\n\
+\n\
+-d<drop-time>: Specify the time after which a request is treated as having\n\
+    been lost.  The value is given in seconds and may contain a fractional\n\
+    component.  The default is 1 second.\n\
+-D<max-drop>: Abort the test if more than <max-drop> requests have been\n\
+    dropped.  Use -D0 to abort if even a single request has been dropped. \n\
+    If <max-drop> includes the suffix \"%%\", it specifies a maximum\n\
+    percentage of requests that may be dropped before abort.  In this\n\
+    case, testing of the threshold begins after 10 requests have been\n\
+    expected to be received.\n\
+-n<num-request>: Initiate <num-request> transactions.  No report is\n\
+    generated until all transactions have been initiated/waited-for, after\n\
+    which a report is generated and the program terminates.\n\
+-p<test-period>: Send requests for the given test period, which is\n\
+    specified in the same manner as -d.  This can be used as an\n\
+    alternative to -n, or both options can be given, in which case the\n\
+    testing is completed when either limit is reached.\n\
+\n\
+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 exchanges\n\
+  are not successfully completed.\n",
+        progName, usage);
+}
+
+int
+isV6(void) {
+    return v6;
+}
+
+int
+getInitialOnly(void) {
+    return initialOnly;
+}
+
+unsigned
+getRate(void) {
+    return rate;
+}
+
+unsigned
+getNumRequest(void) {
+    return numRequest;
+}
+
+double
+getDropTime(void) {
+    return dropTime;
+}
+
+double
+getTestPeriod(void) {
+    return testPeriod;
+}
+
+const char *
+getServer(void) {
+    return server;
+}
+
+const char *
+getLocalName(void) {
+    return localName;
+}
+
+const char *
+getMaxDrop(void) {
+    return maxDropOpt;
+}
diff --git a/tests/tools/perfdhcp/cloptions.h b/tests/tools/perfdhcp/cloptions.h
new file mode 100644
index 0000000..e54bd13
--- /dev/null
+++ b/tests/tools/perfdhcp/cloptions.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef CLOPTIONS_H
+#define CLOPTIONS_H
+
+#include <stdint.h>
+#include "procconf.h"
+
+/*
+ * Process the options/arguments passed to the program.
+ *
+ * Input varibles:
+ * argc, argv: Command line data.
+ *
+ * Return value:
+ * 0 if the command has been satisfied and the program should exit 0.
+ * 2 for usage error, in which case an error message will have been printed.
+ * 1 if argument processing was successful and the program should continue.
+ */
+int procArgs(int argc, const char* argv[]);
+
+/*
+ * These functions return values set by command line options
+ */
+int isV6(void);                // DHCPv6 operation (-6)
+int getInitialOnly(void);       // Do only initial exchange (-i)
+unsigned getRate(void);         // Request rate (-r)
+unsigned getNumRequest(void);   // Number of requests (-n)
+double getDropTime(void);       // Response timeout (-d)
+double getTestPeriod(void);     // Test period (-p)
+const char* getServer(void);    // Server to contact
+const char* getLocalName(void); // Local host/interface (-l)
+const char* getMaxDrop(void);   // Max dropped responses (-D)
+
+#endif
diff --git a/tests/tools/perfdhcp/dhcp.h b/tests/tools/perfdhcp/dhcp.h
new file mode 100644
index 0000000..1af2adf
--- /dev/null
+++ b/tests/tools/perfdhcp/dhcp.h
@@ -0,0 +1,202 @@
+/* dhcp.h
+
+   Protocol structures... */
+
+/*
+ * Copyright (c) 2004-2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ *   Internet Systems Consortium, Inc.
+ *   950 Charter Street
+ *   Redwood City, CA 94063
+ *   <info at isc.org>
+ *   https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises.  To learn more
+ * about Internet Systems Consortium, see ``https://www.isc.org''.
+ * To learn more about Vixie Enterprises, see ``http://www.vix.com''.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#define DHCP_UDP_OVERHEAD	(20 + /* IP header */			\
+			        8)   /* UDP header */
+#define DHCP_SNAME_LEN		64
+#define DHCP_FILE_LEN		128
+#define DHCP_FIXED_NON_UDP	236
+#define DHCP_FIXED_LEN		(DHCP_FIXED_NON_UDP + DHCP_UDP_OVERHEAD)
+						/* Everything but options. */
+#define BOOTP_MIN_LEN		300
+
+#define DHCP_MTU_MAX		1500
+#define DHCP_MTU_MIN            576
+
+#define DHCP_MAX_OPTION_LEN	(DHCP_MTU_MAX - DHCP_FIXED_LEN)
+#define DHCP_MIN_OPTION_LEN     (DHCP_MTU_MIN - DHCP_FIXED_LEN)
+
+struct dhcp_packet {
+ u_int8_t  op;		/* 0: Message opcode/type */
+	u_int8_t  htype;	/* 1: Hardware addr type (net/if_types.h) */
+	u_int8_t  hlen;		/* 2: Hardware addr length */
+	u_int8_t  hops;		/* 3: Number of relay agent hops from client */
+	u_int32_t xid;		/* 4: Transaction ID */
+	u_int16_t secs;		/* 8: Seconds since client started looking */
+	u_int16_t flags;	/* 10: Flag bits */
+	struct in_addr ciaddr;	/* 12: Client IP address (if already in use) */
+	struct in_addr yiaddr;	/* 16: Client IP address */
+	struct in_addr siaddr;	/* 18: IP address of next server to talk to */
+	struct in_addr giaddr;	/* 20: DHCP relay agent IP address */
+	unsigned char chaddr [16];	/* 24: Client hardware address */
+	char sname [DHCP_SNAME_LEN];	/* 40: Server name */
+	char file [DHCP_FILE_LEN];	/* 104: Boot filename */
+	unsigned char options [DHCP_MAX_OPTION_LEN];
+				/* 212: Optional parameters
+			  (actual length dependent on MTU). */
+};
+
+/* BOOTP (rfc951) message types */
+#define	BOOTREQUEST	1
+#define BOOTREPLY	2
+
+/* Possible values for flags field... */
+#define BOOTP_BROADCAST 32768L
+
+/* Possible values for hardware type (htype) field... */
+#define HTYPE_ETHER	1               /* Ethernet 10Mbps              */
+#define HTYPE_IEEE802	6               /* IEEE 802.2 Token Ring...	*/
+#define HTYPE_FDDI	8		/* FDDI...			*/
+
+/* Magic cookie validating dhcp options field (and bootp vendor
+   extensions field). */
+#define DHCP_OPTIONS_COOKIE	"\143\202\123\143"
+
+/* DHCP Option codes: */
+
+#define DHO_PAD					0
+#define DHO_SUBNET_MASK				1
+#define DHO_TIME_OFFSET				2
+#define DHO_ROUTERS				3
+#define DHO_TIME_SERVERS			4
+#define DHO_NAME_SERVERS			5
+#define DHO_DOMAIN_NAME_SERVERS			6
+#define DHO_LOG_SERVERS				7
+#define DHO_COOKIE_SERVERS			8
+#define DHO_LPR_SERVERS				9
+#define DHO_IMPRESS_SERVERS			10
+#define DHO_RESOURCE_LOCATION_SERVERS		11
+#define DHO_HOST_NAME				12
+#define DHO_BOOT_SIZE				13
+#define DHO_MERIT_DUMP				14
+#define DHO_DOMAIN_NAME				15
+#define DHO_SWAP_SERVER				16
+#define DHO_ROOT_PATH				17
+#define DHO_EXTENSIONS_PATH			18
+#define DHO_IP_FORWARDING			19
+#define DHO_NON_LOCAL_SOURCE_ROUTING		20
+#define DHO_POLICY_FILTER			21
+#define DHO_MAX_DGRAM_REASSEMBLY		22
+#define DHO_DEFAULT_IP_TTL			23
+#define DHO_PATH_MTU_AGING_TIMEOUT		24
+#define DHO_PATH_MTU_PLATEAU_TABLE		25
+#define DHO_INTERFACE_MTU			26
+#define DHO_ALL_SUBNETS_LOCAL			27
+#define DHO_BROADCAST_ADDRESS			28
+#define DHO_PERFORM_MASK_DISCOVERY		29
+#define DHO_MASK_SUPPLIER			30
+#define DHO_ROUTER_DISCOVERY			31
+#define DHO_ROUTER_SOLICITATION_ADDRESS		32
+#define DHO_STATIC_ROUTES			33
+#define DHO_TRAILER_ENCAPSULATION		34
+#define DHO_ARP_CACHE_TIMEOUT			35
+#define DHO_IEEE802_3_ENCAPSULATION		36
+#define DHO_DEFAULT_TCP_TTL			37
+#define DHO_TCP_KEEPALIVE_INTERVAL		38
+#define DHO_TCP_KEEPALIVE_GARBAGE		39
+#define DHO_NIS_DOMAIN				40
+#define DHO_NIS_SERVERS				41
+#define DHO_NTP_SERVERS				42
+#define DHO_VENDOR_ENCAPSULATED_OPTIONS		43
+#define DHO_NETBIOS_NAME_SERVERS		44
+#define DHO_NETBIOS_DD_SERVER			45
+#define DHO_NETBIOS_NODE_TYPE			46
+#define DHO_NETBIOS_SCOPE			47
+#define DHO_FONT_SERVERS			48
+#define DHO_X_DISPLAY_MANAGER			49
+#define DHO_DHCP_REQUESTED_ADDRESS		50
+#define DHO_DHCP_LEASE_TIME			51
+#define DHO_DHCP_OPTION_OVERLOAD		52
+#define DHO_DHCP_MESSAGE_TYPE			53
+#define DHO_DHCP_SERVER_IDENTIFIER		54
+#define DHO_DHCP_PARAMETER_REQUEST_LIST		55
+#define DHO_DHCP_MESSAGE			56
+#define DHO_DHCP_MAX_MESSAGE_SIZE		57
+#define DHO_DHCP_RENEWAL_TIME			58
+#define DHO_DHCP_REBINDING_TIME			59
+#define DHO_VENDOR_CLASS_IDENTIFIER		60
+#define DHO_DHCP_CLIENT_IDENTIFIER		61
+#define DHO_NWIP_DOMAIN_NAME			62
+#define DHO_NWIP_SUBOPTIONS			63
+#define DHO_USER_CLASS				77
+#define DHO_FQDN				81
+#define DHO_DHCP_AGENT_OPTIONS			82
+#define DHO_AUTHENTICATE			90  /* RFC3118, was 210 */
+#define DHO_CLIENT_LAST_TRANSACTION_TIME	91
+#define DHO_ASSOCIATED_IP			92
+#define DHO_SUBNET_SELECTION			118 /* RFC3011! */
+#define DHO_DOMAIN_SEARCH			119 /* RFC3397 */
+#define DHO_VIVCO_SUBOPTIONS			124
+#define DHO_VIVSO_SUBOPTIONS			125
+
+#define DHO_END					255
+
+/* DHCP message types. */
+#define DHCPDISCOVER		1
+#define DHCPOFFER		2
+#define DHCPREQUEST		3
+#define DHCPDECLINE		4
+#define DHCPACK			5
+#define DHCPNAK			6
+#define DHCPRELEASE		7
+#define DHCPINFORM		8
+#define DHCPLEASEQUERY		10
+#define DHCPLEASEUNASSIGNED	11
+#define DHCPLEASEUNKNOWN	12
+#define DHCPLEASEACTIVE		13
+
+
+/* Relay Agent Information option subtypes: */
+#define RAI_CIRCUIT_ID	1
+#define RAI_REMOTE_ID	2
+#define RAI_AGENT_ID	3
+#define RAI_LINK_SELECT	5
+
+/* FQDN suboptions: */
+#define FQDN_NO_CLIENT_UPDATE		1
+#define FQDN_SERVER_UPDATE		2
+#define FQDN_ENCODED			3
+#define FQDN_RCODE1			4
+#define FQDN_RCODE2			5
+#define FQDN_HOSTNAME			6
+#define FQDN_DOMAINNAME			7
+#define FQDN_FQDN			8
+#define FQDN_SUBOPTION_COUNT		8
+
+/* Enterprise Suboptions: */
+#define VENDOR_ISC_SUBOPTIONS		2495
+
+#endif /* DHCP_H */
+
diff --git a/tests/tools/perfdhcp/dhcp6.h b/tests/tools/perfdhcp/dhcp6.h
new file mode 100644
index 0000000..03db160
--- /dev/null
+++ b/tests/tools/perfdhcp/dhcp6.h
@@ -0,0 +1,213 @@
+/* dhcp6.h
+
+   DHCPv6 Protocol structures... */
+
+/*
+ * Copyright (c) 2006-2009 by Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and 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.
+ *
+ *   Internet Systems Consortium, Inc.
+ *   950 Charter Street
+ *   Redwood City, CA 94063
+ *   <info at isc.org>
+ *   https://www.isc.org/
+ */
+
+
+/* DHCPv6 Option codes: */
+
+#define D6O_CLIENTID				1 /* RFC3315 */
+#define D6O_SERVERID				2
+#define D6O_IA_NA				3
+#define D6O_IA_TA				4
+#define D6O_IAADDR				5
+#define D6O_ORO					6
+#define D6O_PREFERENCE				7
+#define D6O_ELAPSED_TIME			8
+#define D6O_RELAY_MSG				9
+/* Option code 10 unassigned. */
+#define D6O_AUTH				11
+#define D6O_UNICAST				12
+#define D6O_STATUS_CODE				13
+#define D6O_RAPID_COMMIT			14
+#define D6O_USER_CLASS				15
+#define D6O_VENDOR_CLASS			16
+#define D6O_VENDOR_OPTS				17
+#define D6O_INTERFACE_ID			18
+#define D6O_RECONF_MSG				19
+#define D6O_RECONF_ACCEPT			20
+#define D6O_SIP_SERVERS_DNS			21 /* RFC3319 */
+#define D6O_SIP_SERVERS_ADDR			22 /* RFC3319 */
+#define D6O_NAME_SERVERS			23 /* RFC3646 */
+#define D6O_DOMAIN_SEARCH			24 /* RFC3646 */
+#define D6O_IA_PD				25 /* RFC3633 */
+#define D6O_IAPREFIX				26 /* RFC3633 */
+#define D6O_NIS_SERVERS				27 /* RFC3898 */
+#define D6O_NISP_SERVERS			28 /* RFC3898 */
+#define D6O_NIS_DOMAIN_NAME			29 /* RFC3898 */
+#define D6O_NISP_DOMAIN_NAME			30 /* RFC3898 */
+#define D6O_SNTP_SERVERS			31 /* RFC4075 */
+#define D6O_INFORMATION_REFRESH_TIME		32 /* RFC4242 */
+#define D6O_BCMCS_SERVER_D			33 /* RFC4280 */
+#define D6O_BCMCS_SERVER_A			34 /* RFC4280 */
+/* 35 is unassigned */
+#define D6O_GEOCONF_CIVIC			36 /* RFC4776 */
+#define D6O_REMOTE_ID				37 /* RFC4649 */
+#define D6O_SUBSCRIBER_ID			38 /* RFC4580 */
+#define D6O_CLIENT_FQDN				39 /* RFC4704 */
+#define D6O_PANA_AGENT				40 /* paa-option */
+#define D6O_NEW_POSIX_TIMEZONE			41 /* RFC4833 */
+#define D6O_NEW_TZDB_TIMEZONE			42 /* RFC4833 */
+#define D6O_ERO					43 /* RFC4994 */
+#define D6O_LQ_QUERY				44 /* RFC5007 */
+#define D6O_CLIENT_DATA				45 /* RFC5007 */
+#define D6O_CLT_TIME				46 /* RFC5007 */
+#define D6O_LQ_RELAY_DATA			47 /* RFC5007 */
+#define D6O_LQ_CLIENT_LINK			48 /* RFC5007 */
+
+/* 
+ * Status Codes, from RFC 3315 section 24.4, and RFC 3633, 5007.
+ */
+#define STATUS_Success		 0
+#define STATUS_UnspecFail	 1
+#define STATUS_NoAddrsAvail	 2
+#define STATUS_NoBinding	 3
+#define STATUS_NotOnLink	 4 
+#define STATUS_UseMulticast	 5 
+#define STATUS_NoPrefixAvail	 6
+#define STATUS_UnknownQueryType	 7
+#define STATUS_MalformedQuery	 8
+#define STATUS_NotConfigured	 9
+#define STATUS_NotAllowed	10
+
+/* 
+ * DHCPv6 message types, defined in section 5.3 of RFC 3315 
+ */
+#define DHCPV6_SOLICIT		    1
+#define DHCPV6_ADVERTISE	    2
+#define DHCPV6_REQUEST		    3
+#define DHCPV6_CONFIRM		    4
+#define DHCPV6_RENEW		    5
+#define DHCPV6_REBIND		    6
+#define DHCPV6_REPLY		    7
+#define DHCPV6_RELEASE		    8
+#define DHCPV6_DECLINE		    9
+#define DHCPV6_RECONFIGURE	   10
+#define DHCPV6_INFORMATION_REQUEST 11
+#define DHCPV6_RELAY_FORW	   12
+#define DHCPV6_RELAY_REPL	   13
+#define DHCPV6_LEASEQUERY	   14
+#define DHCPV6_LEASEQUERY_REPLY    15
+
+extern const char *dhcpv6_type_names[];
+extern const int dhcpv6_type_name_max;
+
+/* DUID type definitions (RFC3315 section 9).
+ */
+#define DUID_LLT	1
+#define DUID_EN		2
+#define DUID_LL		3
+
+/* Offsets into IA_*'s where Option spaces commence.  */
+#define IA_NA_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+#define IA_TA_OFFSET  4 /* IAID only, 4 octets */
+#define IA_PD_OFFSET 12 /* IAID, T1, T2, all 4 octets each */
+
+/* Offset into IAADDR's where Option spaces commence. */
+#define IAADDR_OFFSET 24
+
+/* Offset into IAPREFIX's where Option spaces commence. */
+#define IAPREFIX_OFFSET 25
+
+/* Offset into LQ_QUERY's where Option spaces commence. */
+#define LQ_QUERY_OFFSET 17
+
+/* 
+ * DHCPv6 well-known multicast addressess, from section 5.1 of RFC 3315 
+ */
+#define All_DHCP_Relay_Agents_and_Servers "FF02::1:2"
+#define All_DHCP_Servers "FF05::1:3"
+
+/*
+ * DHCPv6 Retransmission Constants (RFC3315 section 5.5, RFC 5007)
+ */
+
+#define SOL_MAX_DELAY     1
+#define SOL_TIMEOUT       1
+#define SOL_MAX_RT      120
+#define REQ_TIMEOUT       1
+#define REQ_MAX_RT       30
+#define REQ_MAX_RC       10
+#define CNF_MAX_DELAY     1
+#define CNF_TIMEOUT       1
+#define CNF_MAX_RT        4
+#define CNF_MAX_RD       10
+#define REN_TIMEOUT      10
+#define REN_MAX_RT      600
+#define REB_TIMEOUT      10
+#define REB_MAX_RT      600
+#define INF_MAX_DELAY     1
+#define INF_TIMEOUT       1
+#define INF_MAX_RT      120
+#define REL_TIMEOUT       1
+#define REL_MAX_RC        5
+#define DEC_TIMEOUT       1
+#define DEC_MAX_RC        5
+#define REC_TIMEOUT       2
+#define REC_MAX_RC        8
+#define HOP_COUNT_LIMIT  32
+#define LQ6_TIMEOUT       1
+#define LQ6_MAX_RT       10
+#define LQ6_MAX_RC        5
+
+/* 
+ * Normal packet format, defined in section 6 of RFC 3315 
+ */
+struct dhcpv6_packet {
+	unsigned char msg_type;
+	unsigned char transaction_id[3];
+	unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Offset into DHCPV6 Reply packets where Options spaces commence. */
+#define REPLY_OPTIONS_INDEX 4
+
+/* 
+ * Relay packet format, defined in section 7 of RFC 3315 
+ */
+struct dhcpv6_relay_packet {
+	unsigned char msg_type;
+	unsigned char hop_count;
+	unsigned char link_address[16];
+	unsigned char peer_address[16];
+	unsigned char options[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* Leasequery query-types (RFC 5007) */
+
+#define LQ6QT_BY_ADDRESS	1
+#define LQ6QT_BY_CLIENTID	2
+
+/*
+ * DUID time starts 2000-01-01.
+ * This constant is the number of seconds since 1970-01-01,
+ * when the Unix epoch began.
+ */
+#define DUID_TIME_EPOCH 946684800
+
+/* Information-Request Time option (RFC 4242) */
+
+#define IRT_DEFAULT	86400
+#define IRT_MINIMUM	600
+
diff --git a/tests/tools/perfdhcp/dkdebug.cc b/tests/tools/perfdhcp/dkdebug.cc
new file mode 100644
index 0000000..88f62dd
--- /dev/null
+++ b/tests/tools/perfdhcp/dkdebug.cc
@@ -0,0 +1,65 @@
+/*
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include "dkdebug.h"
+
+/*
+ * The set of diagnostic bits set by dk_setup(), and used by the other
+ * functions to test offered diagnostics against.
+ */
+static unsigned dk_diag_mask = 0;
+
+char
+dk_setup(const char* diag_str, const struct dkdesc* diags) {
+    dk_diag_mask = 0;
+    int i;
+
+    for (; *diag_str != '\0'; diag_str++) {
+        for (i = 0; diags[i].keyletter != '\0'; i++) {
+            if (diags[i].keyletter == *diag_str) {
+                dk_diag_mask |= diags[i].mask;
+                break;
+            }
+        }
+        if (diags[i].keyletter == '\0') {
+            return(*diag_str);
+        }
+    }
+    return('\0');
+}
+
+void
+dkprintf(unsigned diag_req, const char format[], ...) {
+    va_list ap;
+
+    va_start(ap,format);
+    vdkprintf(diag_req, format, ap);
+    va_end(ap);
+}
+
+void
+vdkprintf(unsigned diag_req, const char format[], va_list ap) {
+    if (diag_req & dk_diag_mask) {
+        vfprintf(stderr, format, ap);
+    }
+}
+
+int
+dk_set(unsigned diag_req) {
+    return((diag_req & dk_diag_mask) != 0);
+}
diff --git a/tests/tools/perfdhcp/dkdebug.h b/tests/tools/perfdhcp/dkdebug.h
new file mode 100644
index 0000000..9f4d213
--- /dev/null
+++ b/tests/tools/perfdhcp/dkdebug.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+/*
+ * This module implements a mask-style diagnostic printing/selection system.
+ * Each diagnostic is enabled by including an associated keyletter in a
+ * selector string given at initialization time (typically as a command-line
+ * option).
+ */
+
+#ifndef DKDEBUG_H
+#define DKDEBUG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+
+#define DK_ALL (~0),   /* Select all diagnostics */
+
+/*
+ * Elements of this type are used to map the available diagnostic keyletters to
+ * mask bits.
+ */
+struct dkdesc {
+    char keyletter;
+    unsigned mask;
+};
+
+/*
+ * Initialize diagnostic mask.
+ *
+ * Input variables:
+ *
+ * diag_str is a string giving the keyletters for diagnostics to enable.
+ *
+ * diags describes the available diagnostics, mapping each keyletter to any
+ * number of mask bits.  It should be terminated with an element with keyletter
+ * set to the null character.
+ *
+ * Return value:
+ * If an invalid character is given in diag_str, that character; otherwise a
+ * null character.
+ */
+char dk_setup(const char* diag_str, const struct dkdesc* diags);
+
+/*
+ * The remaining functions test the mask bitset diag_req against the currently
+ * enabled diagnostics, as set by dk_setup().  If any bits set in diag_req are
+ * among the enabled diagnostics, the diagnostic operation is enabled.
+ */
+
+/*
+ * If diagnostic operation is enabled, use the remaining arguments to print
+ * like fprintf(stderr, )
+ */
+void dkprintf(unsigned diag_req, const char format[], ...);
+
+/*
+ * If diagnostic operation is enabled, use the remaining arguments to print
+ * like vfprintf(stderr, )
+ */
+void vdkprintf(unsigned diag_req, const char format[], va_list ap);
+
+/*
+ * If diagnostic operation is enabled, return 1; else return false.
+ */
+int dk_set(unsigned diag_req);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/tools/perfdhcp/perfdhcp.cc b/tests/tools/perfdhcp/perfdhcp.cc
new file mode 100644
index 0000000..72aa8d5
--- /dev/null
+++ b/tests/tools/perfdhcp/perfdhcp.cc
@@ -0,0 +1,372 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <net/if_arp.h>
+#include <ifaddrs.h>
+#include <strings.h>
+#include <string.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <errno.h>
+#include <time.h>
+#include "perfdhcp.h"
+#include "cloptions.h"
+#include "dkdebug.h"
+#include "dhcp.h"
+#define FLEXIBLE_ARRAY_MEMBER 500
+#include "dhcp6.h"
+
+static int socketSetup(int addr_fam, const char* localAddr, const char* port,
+                       const char* type, struct sockaddr_storage* l_addr);
+static struct addrinfo* getAddrs(int addr_fam, const char* hostname,
+                                 const char* port);
+static void printAddrInfo(FILE* f, const struct addrinfo* addr);
+static char* addrAndPortToNames(const struct sockaddr_storage* addr, char* buf,
+                                size_t bufsize);
+static void printSockaddr6Info(FILE* f, const struct sockaddr_in6* sa);
+
+#define ADDR_NAME_BUFSIZE (NI_MAXHOST + 30)
+
+static struct sockaddr_storage send_laddr;     // send socket local end details
+static struct sockaddr_storage recv_laddr;     // recv socket local end details
+static int send_fd = -1, recv_fd = -1;         // Sockets for DHCP server comm
+static struct addrinfo* server_addr;           // DHCP server address
+
+/*
+ * Set up network communications to talk to a DHCP server.
+ * This sets up sockets and sets the globals used by dhcpSend().
+ *
+ * Input variables:
+ * sendPort is the local port from which to send communications.
+ * recvPort is the local port on which to listen for communications.
+ *     If null, the receive socket is made the same as the send socket.
+ * serverPort is the port on the server to communicate with.
+ *
+ * Globals (see above):
+ * Sets: server_addr, send_fd, send_laddr, recv_fd and recv_laddr.
+ */
+void
+dhcpSetup(const char* sendPort, const char* recvPort, const char *serverPort) {
+    const char* localAddr = getLocalName();
+    int v6 = isV6();
+    int family = v6 ? AF_INET6 : AF_INET;
+
+    if (!isV6() && localAddr == NULL) {
+        localAddr = "0.0.0.0";
+    }
+    send_fd = socketSetup(family, localAddr, sendPort, "Send",
+                          &send_laddr);
+    if (recvPort == NULL) {
+        recv_fd = send_fd;
+        memcpy(&recv_laddr, &send_laddr, sizeof(recv_laddr));
+
+    } else {
+        recv_fd = socketSetup(family, localAddr, recvPort, "Recv",
+                              &recv_laddr);
+    }
+    server_addr = getAddrs(family, getServer(), serverPort);
+}
+
+/*
+ * dhcpSend: Send a DHCP packet.
+ *
+ * Input variables:
+ * dhcp_packet: DHCP message to send.
+ * num_octets: Size of message.
+ *
+ * Globals:
+ * send_fd: Socket to send message on.
+ * server_addr: Remote address to send message to.
+ * send_laddr: Local address of socket, for informational messages only.
+ *
+ * Return value:
+ * If the send succeeds, 1.
+ * If it fails (in full or part), an error message is printed to stderr and 0
+ * is returned.
+ */
+int
+dhcpSend(const unsigned char* msg, size_t num_octets) {
+    ssize_t num_written;                // Octets written to socket
+    char addrbuf[ADDR_NAME_BUFSIZE];    // For information messages
+    char addrbuf2[ADDR_NAME_BUFSIZE];   // For information messages
+
+    if (dk_set(DK_MSG)) {
+        fprintf(stderr,
+                "Sending %zu octets to socket fd %u, local %s remote %s",
+                num_octets, send_fd,
+                addrAndPortToNames((struct sockaddr_storage*)&send_laddr,
+                                   addrbuf, sizeof(addrbuf)),
+                addrAndPortToNames((struct sockaddr_storage*)server_addr,
+				   addrbuf2, sizeof(addrbuf2)));
+    }
+    num_written = sendto(send_fd, msg, num_octets, 0, server_addr->ai_addr,
+                         sizeof(struct sockaddr_storage));
+    if (num_written < 0) {
+        fprintf(stderr, "%s: Send failed: %s\n", PROGNAME, strerror(errno));
+        return (0);
+    }
+    if ((size_t) num_written != num_octets) {
+        fprintf(stderr, "%s: Only %zd of %zu octets written\n", PROGNAME,
+                num_written, num_octets);
+        return (0);
+    }
+    return (1);
+}
+
+/*
+ * dhcpReceive: Receive a DHCP packet.
+ *
+ * Input variables:
+ *
+ * If msgsize is nonzero, it gives the size of the receive buffer.  If it is
+ * zero, the reeive buffer is taken to have the size of either a hdcpv6_packet
+ * or a dhcp_packet, depending on whether v6 operation is in effect.
+ *
+ * Globals:
+ * recv_fd is the socket to receive on.
+ * recv_laddr is the socket's address, solely for informational messages.
+ *
+ * If the receive fails, an error message is printed to stderr.
+ *
+ * Output variables:
+ * msg points to storage for the received message.
+ *
+ * Return value:
+ * The return value from recvfrom: -1 on error, 0 if remote closed, else the
+ * number of octets received.
+ */
+int
+dhcpReceive(void* msg, size_t msgsize) {
+    ssize_t octetsReceived;             // Number of octets received
+    struct sockaddr_storage sourceAddr; // Address of sender
+    socklen_t addrSize;                 // size of above
+    char addrbuf[ADDR_NAME_BUFSIZE];    // For informational messages
+
+    if (dk_set(DK_SOCK)) {
+        fprintf(stderr, "Waiting for response on socket fd %u, %s", recv_fd,
+                 addrAndPortToNames(&recv_laddr, addrbuf, sizeof(addrbuf)));
+    }
+    addrSize = sizeof(sourceAddr);
+    if (msgsize == 0)
+        msgsize = isV6() ? sizeof(struct dhcpv6_packet) :
+                sizeof(struct dhcp_packet);
+    octetsReceived = recvfrom(recv_fd, msg, msgsize, 0,
+                              (struct sockaddr*)&sourceAddr, &addrSize);
+    if (dk_set(DK_MSG)) {
+        fprintf(stderr, "Got %zd octets from fd %u, %s", octetsReceived,
+                recv_fd,
+                addrAndPortToNames(&sourceAddr, addrbuf, sizeof(addrbuf)));
+    }
+    if (octetsReceived < 0) {
+        fprintf(stderr, "%s: Receive failed: %s\n", PROGNAME, strerror(errno));
+    } else if (octetsReceived == 0) {
+        fprintf(stderr, "%s: Remote closed connection.\n", PROGNAME);
+    }
+    return (octetsReceived);
+}
+
+/*
+ * Create a socket for communication with DHCP server:
+ * - Create socket
+ * - Bind it to given local address and UDP port
+ *
+ * Input variables:
+ * addr_fam is the address family to use, e.g. AF_INET
+ * localAddr is the local address to bind to.
+ * If it is null and the address family is AF_INET6, the local address, flow
+ *     info, and scope are instead taken from l_addr.
+ *
+ * port is the port to bind to.
+ *
+ * type is a string giving the purpose of the socket, for verbose output.
+ *
+ * Output variables:
+ * Socket details are stored in l_addr.
+ *
+ * Return value: The network fd.
+ *
+ * On error, a message is printed to stderr and the program exits with status
+ * 1.
+ */
+static int
+socketSetup(int addr_fam, const char* localAddr, const char* port,
+            const char* type, struct sockaddr_storage* l_addr) {
+    char addrbuf[ADDR_NAME_BUFSIZE];    // Buffer for error messages
+    int net_fd;                         // Socket
+    struct addrinfo* addrs;             // Port and possibly local addr
+
+    addrs = getAddrs(addr_fam, localAddr, port);
+    if (addr_fam == AF_INET6 && localAddr == NULL) {
+        if (dk_set(DK_SOCK)) {
+            fprintf(stderr, "local address:\n");
+            printSockaddr6Info(stderr, (struct sockaddr_in6*)l_addr);
+        }
+        memcpy(&((struct sockaddr_in6*)addrs->ai_addr)->sin6_addr,
+               &((struct sockaddr_in6*)l_addr)->sin6_addr,
+               sizeof(struct in6_addr));
+        ((struct sockaddr_in6*)addrs->ai_addr)->sin6_flowinfo =
+            ((struct sockaddr_in6*)l_addr)->sin6_flowinfo;
+        ((struct sockaddr_in6*)addrs->ai_addr)->sin6_scope_id =
+            ((struct sockaddr_in6*)l_addr)->sin6_scope_id;
+    }
+    if (dk_set(DK_SOCK)) {
+        printAddrInfo(stderr, addrs);
+        fprintf(stderr, "Creating socket from addrinfo:\n");
+        printAddrInfo(stderr, addrs);
+    }
+    net_fd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
+    if (net_fd < 0) {
+        fprintf(stderr, "%s: socket creation failed: %s\n", PROGNAME,
+                strerror(errno));
+        exit(1);
+    }
+    if (bind(net_fd, addrs->ai_addr, addrs->ai_addrlen) == -1) {
+        int s_errno = errno;
+        fprintf(stderr, "%s: Could not bind to %s: %s\n", PROGNAME,
+                addrAndPortToNames((struct sockaddr_storage*)addrs->ai_addr,
+                addrbuf, sizeof(addrbuf)), strerror(s_errno));
+        exit(1);
+    }
+    if (dk_set(DK_SOCK)) {
+        fprintf(stderr, "%s fd %d bound to %s\n", type, net_fd,
+                 addrAndPortToNames((struct sockaddr_storage*)addrs->ai_addr,
+                                    addrbuf, sizeof(addrbuf)));
+    }
+    memcpy(l_addr, addrs->ai_addr, sizeof(struct sockaddr_storage));
+    freeaddrinfo(addrs);
+    return (net_fd);
+}
+
+/*
+ * getAddrs: generate an addrinfo list from a given hostname and UDP port.
+ * If getaddrinfo() fails for any reason with the provided information, an
+ * error message is printed to stderr and the program exits with status 2.
+ *
+ * Input variables:
+ * hostname: The host name to look up.  This can be either a name or an IPv4
+ *     dotted-quad address, or null to not fill in the address.
+ * port: The port to include in addrinfo.  This can be either a service name or
+ *     an ASCII decimal number, or null to not fill in the port number.
+ *
+ * Globals:
+ * PROGNAME, for error messages.
+ *
+ * Return value:
+ * A pointer to the addrinfo list.  This must be freed by the caller with
+ * freeaddrinfo().
+ */
+static struct addrinfo*
+getAddrs(int addr_fam, const char* hostname, const char* port) {
+    struct addrinfo* ai;        // list produced by getaddrinfo
+    struct addrinfo hints;      // hints for getaddrinfo
+    int ret;                    // return value from getaddrinfo
+
+    memset(&hints, '\0', sizeof(hints));
+    hints.ai_family = addr_fam;
+    hints.ai_socktype = SOCK_DGRAM;
+    hints.ai_protocol = IPPROTO_UDP;
+
+    ret = getaddrinfo(hostname, port, &hints, &ai);
+    if (ret != 0 || ai == NULL) {
+        /*
+         * getaddrinfo as tested returns error if lookup fails,
+         * but the man page doesn't quite promise that.
+         * But be extra sure we always have an answer if we return.
+         */
+        fprintf(stderr, "%s: getaddrinfo: %s/%s: %s\n", PROGNAME,
+                hostname == NULL ? "" : hostname, port == NULL ? "" : port,
+                ret != 0 ? gai_strerror(ret) : "hostname/port lookup failure");
+        exit(2);
+    }
+    return (ai);
+}
+
+/*
+ * Print details of a socket address
+ *
+ * Input variables:
+ *
+ * stream is the stdio stream to print to.
+ *
+ * addr contains the information to display.
+ */
+static void
+printAddrInfo(FILE* stream, const struct addrinfo* addr) {
+    fprintf(stream, "Addrinfo:\n");
+    fprintf(stream, "flags: 0x%x;  family: %d;  socktype: %d;  proto: %d;\n",
+            addr->ai_flags, addr->ai_family, addr->ai_socktype,
+            addr->ai_protocol);
+    fprintf(stream, "addrlen: %u;  addr: %p;  canonname: %s;  next: %p\n",
+            addr->ai_addrlen, addr->ai_addr, addr->ai_canonname,
+            addr->ai_next);
+    if (addr->ai_family == AF_INET6) {
+        printSockaddr6Info(stream, (struct sockaddr_in6*)addr->ai_addr);
+    }
+}
+
+/*
+ * addrAndPortToNames(): Convert the address and port associated with a socket
+ * into a hostname and port name and store them in a buffer.
+ *
+ * Input variables:
+ * addr is the socket to operate on.
+ * bufsize is the size of the buffer.
+ *
+ * Output variables:
+ * buf is the buffer to store the names in.
+ *
+ * Return value:
+ * buf is returned.
+ * If getnameinfo fails, buf will contain "untranslatable".  In general, this
+ * shouldn't happen; in the case of lookup failure for a component, its numeric
+ * value will be used.
+ *
+ * TODO: Use gai_strerror() to report errors.
+ */
+static char*
+addrAndPortToNames(const struct sockaddr_storage* addr, char* buf,
+                   size_t bufsize) {
+    char servbuf[30];   // Port name
+
+    if (getnameinfo((struct sockaddr*)addr, sizeof(struct sockaddr_storage),
+                    buf, bufsize, servbuf, 30, 0) != 0) {
+        strncpy(buf, "untranslatable", bufsize-1);
+    } else {
+        size_t len = strlen(buf);
+        if (len < bufsize) {
+            snprintf(buf + len, bufsize - len, " port %s", servbuf);
+        }
+    }
+    return (buf);
+}
+
+/*
+ * Print information about an IPv6 socket.
+ */
+static void
+printSockaddr6Info(FILE* f, const struct sockaddr_in6* sa) {
+    char addrbuf[ADDR_NAME_BUFSIZE];    // For host & port name
+
+    fprintf(f, "IPv6 sockaddr info:\n");
+    fprintf(f, "family: %u;  flowinfo: 0x%x;  scope-id: %u  addr: %s\n",
+            sa->sin6_family, sa->sin6_flowinfo, sa->sin6_scope_id,
+            addrAndPortToNames((struct sockaddr_storage*)sa, addrbuf,
+                               sizeof(addrbuf)));
+}
diff --git a/tests/tools/perfdhcp/perfdhcp.h b/tests/tools/perfdhcp/perfdhcp.h
new file mode 100644
index 0000000..39a7b5e
--- /dev/null
+++ b/tests/tools/perfdhcp/perfdhcp.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#ifndef PERFDHCP_H
+#define PERFDHCP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PROGNAME "perfdhcp"
+
+/*
+ * The masks associated with keyletters, used in dkdesc structures for setup
+ * and passed in the diag_req argument to the output/test functions to
+ * determine which diagnostics they are enabled for.
+ */
+enum {
+    DK_SOCK   = 1,
+    DK_MSG    = 2,
+    DK_PACKET = 4
+};
+
+/*
+ * Set up network communications to talk to a DHCP server.
+ * This sets up sockets and sets the globals used by dhcpSend().
+ *
+ * Input variables:
+ * sendPort is the local port from which to send communications.
+ * recvPort is the local port on which to listen for communications.
+ *     If null, the receive socket is made the same as the send socket.
+ * serverPort is the port on the server to communicate with.
+ *
+ * Globals (see above):
+ * Sets: server_addr, send_fd, send_laddr, recv_fd and recv_laddr.
+ */
+void dhcpSetup(const char* sendPort, const char* recvPort,
+               const char *serverPort);
+
+/*
+ * dhcpSend: Send a DHCP packet.
+ *
+ * Input variables:
+ * dhcp_packet: DHCP message to send.
+ * num_octets: Size of message.
+ *
+ * Globals:
+ * send_fd: Socket to send message on.
+ * server_addr: Remote address to send message to.
+ * send_laddr: Local address of socket, for informational messages only.
+ *
+ * Return value:
+ * If the send succeeds, 1.
+ * If it fails (in full or part), an error message is printed to stderr and 0
+ * is returned.
+ */
+int dhcpSend(const unsigned char* msg, size_t num_octets);
+
+/*
+ * dhcpReceive: Receive a DHCP packet.
+ *
+ * Input variables:
+ *
+ * If msgsize is nonzero, it gives the size of the receive buffer.  If it is
+ * zero, the reeive buffer is taken to have the size of either a hdcpv6_packet
+ * or a dhcp_packet, depending on whether v6 operation is in effect.
+ *
+ * Globals:
+ * recv_fd is the socket to receive on.
+ * recv_laddr is the socket's address, solely for informational messages.
+ *
+ * If the receive fails, an error message is printed to stderr.
+ *
+ * Output variables:
+ * msg points to storage for the received message.
+ *
+ * Return value:
+ * The return value from recvfrom: -1 on error, 0 if remote closed, else the
+ * number of octets received.
+ */
+int dhcpReceive(void* msg, size_t msgsize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/tests/tools/perfdhcp/procconf.cc b/tests/tools/perfdhcp/procconf.cc
new file mode 100644
index 0000000..ac7a445
--- /dev/null
+++ b/tests/tools/perfdhcp/procconf.cc
@@ -0,0 +1,486 @@
+/*
+ * 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 <string.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include "procconf.h"
+
+static char errmsg[256];    /* for returning error descriptions */
+static const char* pc_name;
+static const char* pc_usage;
+
+#define INTERNAL_ERROR -1
+#define USAGE_ERROR -2
+
+/*
+ * error: printf-style interface to print an error message or write it into
+ * global errmsg.  If a usage message has been given (the indicator that errors
+ * should be handled internally), the message is printed to stderr and the
+ * program is exited.  If not, it is written into errmsg.
+ * Input variables:
+ * errtype is the error type.  If USAGE_ERROR, the program's usage is included
+ *     in error messages, and the exit status is 2; otherwise the exit status
+ *     is 1.
+ * format and the remaining arguments are as for printf().
+ */
+static void
+error(const int errtype, const char* format, ...) {
+    va_list ap;
+
+    va_start(ap,format);
+    if (pc_usage != NULL) { /* error messages should be printed directly */
+        fprintf(stderr, "%s: ", pc_name);
+        vfprintf(stderr, format, ap);
+        putc('\n', stderr);
+        if (errtype == USAGE_ERROR) {
+            fputs(pc_usage, stderr);
+        }
+        exit(errtype == USAGE_ERROR ? 2 : 1);
+    } else {
+        vsnprintf(errmsg, sizeof(errmsg), format, ap);
+    }
+    va_end(ap);
+}
+
+/*
+ * Allocate memory, with error checking.
+ * On allocation failure, error() is called; see its description.
+ * The memory is zeroed before being returned.
+ */
+static void*
+pc_malloc(size_t size) {
+    void* ret = calloc(1, size);
+    if (ret == NULL) {
+        error(INTERNAL_ERROR, "Out of memory");
+    }
+    return(ret);
+}
+
+/*
+ * Generate an error message describing an error in the value passed with an
+ * option.  This is a front end for error(); see its usage for details.
+ * Input variables:
+ * expected: A description of what was expected for the value.
+ * varDesc: The descriptor for the option.
+ * value: The value that was given with the option.
+ * detail: If non-null, additional detail to append regardign the problem.
+ */
+static void
+opterror(const char* expected, const char* value, const confvar_t* varDesc,
+         const char* detail) {
+    if (detail == NULL) {
+        detail = "";
+    }
+    error(USAGE_ERROR,
+          "Invalid value given for option -%c: expected %s, got: %s%s",
+          varDesc->outind, expected, value, detail);
+}
+
+/*
+ * Add an option flag or value assignment to the options database.
+ * This does all option-type-specific processing, and generates linked lists
+ * of option structures.
+ *
+ * Input variables:
+ * value is the string value assigned to the option, for options that take
+ *     values.
+ * varDesc is the description structure for this option.
+ *
+ * Output variables:
+ * The option data is stored in a malloced confval structure, which is appended
+ * to the option list.  The first element of the list is pointed to by first.
+ * first should be NULL if the list contains no elements; in this case it is
+ * set to point to the new structure.  Otherwise the structure is appended to
+ * the element pointed to by last (it is updated to point to that structure).
+ * The 'next' element of the new structure is set to NULL; this is the list
+ * termination indicator when traversing it.
+ *
+ * Return value:
+ * 0 if option was ignored.
+ * 1 if option was processed & added to option chain.
+ * On error, a string describing the error is stored in the global errmsg and
+ * -1 is returned.
+ */
+static int
+addOptVal(const char* value, const confvar_t* varDesc,
+          confval** first, confval** last) {
+    const void* addr;           /* address, if any at which to store value */
+    confval data;               /* data for this option/value */
+    confval *ret_data;          /* allocated structure to add to list */
+    int seen = *first != NULL;  /* has this option been seen before? */
+    char* ptr;        /* character that terminated processing in strtox() */
+    int err;                    /* bad numeric value found? */
+
+    /* if first instance of this option, store result to given addr */
+    addr = seen ? NULL : varDesc->addr;
+    switch (varDesc->type) {
+    case CF_CHAR:
+        if (strlen(value) > 1) {    /* length 0 is OK; gives null char */
+            opterror("a single character", value, varDesc, NULL);
+            return(-1);
+        }
+        data.value.charval = *value;
+        if (addr != NULL) {
+            *(char*)addr = *value;
+        }
+        break;
+    case CF_STRING:
+    case CF_NE_STRING:
+        if (varDesc->type == CF_NE_STRING && *value == '\0') {
+            opterror("a non-empty string", value, varDesc, NULL);
+            return(-1);
+        }
+        data.value.string = value;
+        if (addr != NULL) {
+            *(const char**)addr = value;
+        }
+        break;
+    case CF_INT:
+    case CF_NON_NEG_INT:
+    case CF_POS_INT:
+        /* todo: check for out-of-range result */
+        errno = 0;
+        data.value.intval = strtol(value, &ptr, 0);
+        if (errno == ERANGE) {
+            opterror("an integer", value, varDesc,
+                     " (out of range)");
+            return(-1);
+        }
+        err = *value == '\0' || *ptr != '\0';
+        switch (varDesc->type) {
+        case CF_INT:
+            if (err) {
+                opterror("an integer", value, varDesc, NULL);
+                return(-1);
+            }
+            break;
+        case CF_NON_NEG_INT:
+            if (err || data.value.intval < 0) {
+                opterror("a non-negative integer", value, varDesc,
+                         NULL);
+                return(-1);
+            }
+            data.value.nnint = data.value.intval;
+            break;
+        case CF_POS_INT:
+            if (err || data.value.intval <= 0) {
+                opterror("a positive integer", value, varDesc, NULL);
+                return(-1);
+            }
+            data.value.nnint = data.value.intval;
+            break;
+        default:
+            /* To avoid complaints from -Wall */
+            ;
+        }
+        if (addr != NULL) {
+            *(int*)addr = data.value.intval;
+        }
+        break;
+    case CF_FLOAT:
+    case CF_NON_NEG_FLOAT:
+    case CF_POS_FLOAT:
+        /* todo: check for out-of-range result */
+        errno = 0;
+        data.value.floatval = strtod(value, &ptr);
+        if (errno == ERANGE) {
+            opterror("a number", value, varDesc, " (out of range)");
+            return(-1);
+        }
+        err = *value == '\0' || *ptr != '\0';
+        switch (varDesc->type) {
+        case CF_FLOAT:
+            if (err) {
+                opterror("a number", value, varDesc, NULL);
+                return(-1);
+            }
+            break;
+        case CF_NON_NEG_FLOAT:
+            if (err || data.value.floatval < 0) {
+                opterror("a non-negative number", value, varDesc,
+                         NULL);
+                return(-1);
+            }
+            break;
+        case CF_POS_FLOAT:
+            if (err || data.value.floatval <= 0) {
+                opterror("a positive number", value, varDesc, NULL);
+                return(-1);
+            }
+            break;
+        default:
+            /* To avoid complaints from -Wall */
+            ;
+        }
+        if (addr != NULL) {
+            *(double*)addr = data.value.floatval;
+        }
+        break;
+    case CF_SWITCH:
+        data.value.switchval = varDesc->value;
+        value = "1";    /* for debugging */
+        if (addr != NULL) {
+            *(int*)addr = varDesc->value;
+        }
+        break;
+    case CF_ENDLIST:
+        /* To avoid complaints from -Wall */
+        ;
+    }
+    data.strval = value;
+    data.next = NULL;
+    if ((ret_data = (confval*)pc_malloc(sizeof(confval))) == NULL) {
+        return(-1);
+    }
+    *ret_data = data;
+    if (seen) {
+        (*last)->next = ret_data;
+    } else {
+        *first = ret_data;
+    }
+    *last = ret_data;
+    return(1);
+}
+
+/*
+ * Input variables:
+ * argc, argv: Command line data.
+ * optConf[]: Option description structures.
+ *
+ * Output variables:
+ * See addOptVal().
+ * After processing, argc will be the number of non-option arguments and argv
+ * will start with the first non-option argument.
+ *
+ * Return value:
+ * On success, the number of options processed.
+ * On error, a string describing the error is stored in the global errmsg and
+ * -1 is returned.
+ */
+static int
+procCmdLineArgs(int* argc, const char** argv[], const confvar_t optConf[],
+                confval** perOptRecordsFirst, confval** perOptRecordsLast) {
+    char* p;
+    extern char* optarg;    /* For getopt */
+    extern int optind;      /* For getopt */
+    extern int optopt;      /* For getopt */
+    char optstr[514];       /* List of option chars, for getopt */
+    unsigned optCharToConf[256];        /* Map option char/num to confvar */
+    int optchar;            /* value returned by getopt() */
+    int confNum;       /* to iterate over confvars */
+    int count = 0;          /* number of options processed */
+
+    p = optstr;
+    *(p++) = ':';
+    for (confNum = 0; optConf[confNum].type != CF_ENDLIST; confNum++) {
+        unsigned outind = optConf[confNum].outind;
+        if (outind < 256 && isprint(outind)) {
+            *(p++) = (char)outind;
+            switch (optConf[confNum].type) {
+            case CF_SWITCH:
+                break;
+            default:
+                *(p++) = ':';
+                break;
+            }
+            optCharToConf[outind] = confNum;
+        }
+    }
+
+    *p = '\0';
+    optind = 1;
+    while ((optchar = getopt(*argc, const_cast<char**>(*argv), optstr)) != -1)
+    {
+        int ind;
+        int ret;
+
+        if (optchar == '?') {
+            error(USAGE_ERROR, "Unknown option character '%c'", optopt);
+            return(-1);
+        } else if (optchar == ':') {
+            error(USAGE_ERROR, "No value given for option -%c", optopt);
+            return(-1);
+        }
+        ind = optCharToConf[optchar];
+        switch (ret = addOptVal(optarg, &optConf[ind],
+                                &perOptRecordsFirst[ind],
+                                &perOptRecordsLast[ind])) {
+        case 1:
+            count++;
+            break;
+        case 0:
+            break;
+        default:
+            return(ret);
+        }
+    }
+    *argc -= optind;
+    *argv += optind;
+    return(count);
+}
+
+/*
+ * Input variables:
+ * argc, argv: Command line data.
+ * optConf[]: Option description structures.
+ * name: Name of program, for messages.
+ * usage: Usage message.  If non-null, on error a message is printed to stderr
+ *    and the program exits.
+ *
+ * Output variables:
+ * Option values are stored at the value given by any confvar that has a
+ * non-null address. 
+ * If confdatda is not null, the processed option values are stored in
+ * confdata.
+ * A pointer to the start of the values for each option is stored in
+ * confdata->optVals[].values at the same offset as the option appears in
+ * confdata[].
+ * For any option for option characters/indexes that have been used,
+ * confdata->map[index] is set to the same data.
+ * After processing, argc will have been adjusted to be the number of
+ * non-option arguments and argv will have been adjusted to start with the
+ * first non-option argument.
+ * The malloced data structures returned in confdata are:
+ *   optVals
+ *   optVals[0].values
+ *   If any option characters/indexes are used, map.  If not used, this will be
+ *     a null pointer.
+ *
+ * Return value:
+ * On success, NULL.
+ * On error, a message describing the problem.
+ */
+const char*
+procOpts(int* argc, const char** argv[], const confvar_t optConf[],
+         confdata_t* confdata, const char name[],
+         const char usage[]) {
+    /*
+     * First & last records in the linked lists maintained for each option.
+     * These will point to arrays indexed by option number, giving one pointer
+     * (each) per option, used to maintain/return the list of values seen for
+     * that option (see the description of first & last in addOptVal()
+     */
+    confval** perOptRecordsFirst;
+    confval** perOptRecordsLast;
+
+    /* Number of configuration options given in optConf */
+    unsigned numConf;
+    unsigned maxOptIndex = 0;   /* The highest option index number seen */
+    /* number of option instances + assignments given */
+    int numOptsFound;
+    int optNum;    /* to iterate through the possible options */
+    int i;         /* index into the global list of option value structures */
+    confval** valuePointers;    /* global list of value structures */
+
+    pc_name = name;
+    pc_usage = usage;
+    for (numConf = 0; optConf[numConf].type != CF_ENDLIST; numConf++) {
+        unsigned outind = optConf[numConf].outind;
+
+        if ((outind & ~CF_NOTFLAG) > maxOptIndex) {
+            maxOptIndex = outind & ~CF_NOTFLAG;
+        }
+    }
+    if (numConf == 0) {
+        error(INTERNAL_ERROR, "Empty confvar list");
+        return(errmsg);
+    }
+    if ((perOptRecordsFirst = (confval**)pc_malloc(sizeof(confval*) * numConf))
+            == NULL || (perOptRecordsLast =
+            (confval**)pc_malloc(sizeof(confval*) * numConf)) == NULL) {
+        return(errmsg);
+    }
+
+    numOptsFound = procCmdLineArgs(argc, argv, optConf, perOptRecordsFirst,
+                                   perOptRecordsLast);
+    free(perOptRecordsLast);
+    perOptRecordsLast = NULL;
+
+    if (numOptsFound < 0)
+    {
+        free(perOptRecordsFirst);
+        return(errmsg);
+    }
+    if (confdata == NULL) {
+        free(perOptRecordsFirst);
+        return NULL;
+    }
+
+    /*
+     * All options have been read & initial processing done.
+     * An array of pointers is now generated for the options.
+     */
+    if ((valuePointers =
+            (confval**)pc_malloc(sizeof(confval*) * numOptsFound)) == NULL ||
+            (confdata->optVals =
+            (cf_option*)pc_malloc(sizeof(cf_option) * numConf)) == NULL) {
+        return(errmsg);
+    }
+    /* If option characters / indexes are used, allocate a map for them */
+    if (maxOptIndex == 0) {
+	confdata->map = NULL;
+    } else {
+        if ((confdata->map = (cf_option**)pc_malloc(sizeof(cf_option) *
+             (maxOptIndex+1))) == NULL) {
+            return(errmsg);
+        }
+    }
+
+    /*
+     * Store the linked lists of option values into arrays.
+     * Pointers to all option instances are stored in valuePointers,
+     * with the values for each particular option being contiguous.
+     */
+    i = 0;
+    for (optNum = 0; optNum < numConf; optNum++) {
+        unsigned outind = optConf[optNum].outind;
+        confval* optval;
+
+        confdata->optVals[optNum].num = 0;
+        confdata->optVals[optNum].values = &valuePointers[i];
+        if (outind != 0) {
+            confdata->map[outind & ~CF_NOTFLAG] = &confdata->optVals[optNum];
+        }
+        for (optval = perOptRecordsFirst[optNum]; optval != NULL;
+                 optval = optval->next) {
+            confdata->optVals[optNum].num++;
+            valuePointers[i++] = optval;
+        }
+    }
+    free(perOptRecordsFirst);
+    return(NULL);
+}
+
+/*
+ * Free the malloced data stored in confdata elements by ProcOpts()
+ */
+void
+confdataFree(confdata_t *confdata) {
+    if (confdata->map != NULL) {
+        free(confdata->map);
+        confdata->map = NULL;
+    }
+    free(confdata->optVals[0].values);
+    free(confdata->optVals);
+    confdata->optVals = NULL;
+}
diff --git a/tests/tools/perfdhcp/procconf.h b/tests/tools/perfdhcp/procconf.h
new file mode 100644
index 0000000..fd29c00
--- /dev/null
+++ b/tests/tools/perfdhcp/procconf.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+/*
+ * This header gives the interface types and prototypes for the procconf
+ * module, which processes command line options.
+ */
+
+#ifndef PROCCONF_H
+#define PROCCONF_H
+
+#include <limits.h> /* for UINT_MAX */
+
+/*
+ * These are used to specify the option type expected
+ */
+typedef enum {
+    CF_CHAR,          /* A single character */
+    CF_STRING,        /* A string */
+    CF_NE_STRING,     /* A non-empty string */
+    CF_INT,           /* An integer */
+    CF_NON_NEG_INT,   /* A non-negative integer */
+    CF_POS_INT,       /* A positive integer */
+    CF_FLOAT,         /* A floating-point value */
+    CF_NON_NEG_FLOAT, /* A non-negative floating point value */
+    CF_POS_FLOAT,     /* A positive floating point value */
+    CF_SWITCH,        /* An option that does not take a value */
+    CF_ENDLIST        /* End of option list */
+} cf_type;
+
+/*
+ * This is to be OR'd into a confvar_t outind to indicate that an index
+ * number that is given is not to be interpreted as a command line option
+ * character.
+ */
+#define CF_NOTFLAG (UINT_MAX & ~(UINT_MAX >> 1))
+
+/*
+ * Structure for passing varname/value pairs.
+ * This gives the variable names to search for, and allows variable names to
+ * mapped to characters so that the characters may be used as indexes into the
+ * results array.
+ */
+typedef struct {
+    unsigned outind;    /* Single-character option, or option output index */
+    char* varname;  /* Long name, for config file and long option */
+    cf_type type;   /* Option type */
+    const void* addr;   /* Address of variable associated with this option */
+    int value;      /* Value to assign to switch options */
+} confvar_t;
+
+/*
+ * Structure for returning assigned values.
+ */
+typedef struct confval_struct {
+    const char* strval; /* String form of value */
+    unsigned index; /* Relative position of this instance */
+    union {
+        int intval;
+        unsigned int nnint;
+        double floatval;
+        const char* string;
+        int switchval;
+        char charval;
+    } value;
+    struct confval_struct* next;
+} confval;
+
+/* Information about the values assigned to a particular option */
+typedef struct {
+    int num;        /* number of instances of this option */
+    confvar_t* confvar; /* which option descriptor this corresponds to */
+    confval** values;   /* Start of pointers to values for this option */
+} cf_option;
+
+typedef struct {
+    cf_option* optVals; /* All option values */
+    cf_option** map;    /* Option values indexed by option-char/option-index */
+} confdata_t;
+
+/*
+ * Input variables:
+ * argc, argv: Command line data.
+ * optConf[]: Option description structures.  The array should end with an
+ *    element with a type of CF_ENDLIST.
+ * name: Name of program, for messages.
+ * usage: Usage message.  If non-null, on error a message is printed to stderr
+ *    and the program exits.
+ *
+ * Output variables:
+ * The processed option values are stored in confdata.
+ * A pointer to the start of the values for each option is stored in
+ * confdata->optVals[].values at the same offset as the option appears in
+ * confdata[].
+ * For any option for option characters/indexes have been used,
+ * confdata->map[index] is set to the same data.
+ * After processing, argc will have been adjusted to be the number of
+ * non-option arguments and argv will have been adjusted to start with the
+ * first non-option argument.
+ *
+ * Return value:
+ * On success, NULL.
+ * On error, a message describing the problem.
+ */
+const char*
+procOpts(int* argc, const char** argv[], const confvar_t optConf[],
+         confdata_t* confdata, const char name[],
+         const char usage[]);
+
+/*
+ * Free the malloced data stored in confdata elements by ProcOpts()
+ */
+void confdataFree(confdata_t *confdata);
+
+#endif
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index 9f38c43..f490a55 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -14,7 +14,6 @@ TESTS =
 if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
-run_unittests_SOURCES += cloptions_unittest.cc
 run_unittests_SOURCES += sendreceive_unittest.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/cloptions.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/procconf.cc
diff --git a/tests/tools/perfdhcp/tests/sendreceive_unittest.cc b/tests/tools/perfdhcp/tests/sendreceive_unittest.cc
new file mode 100644
index 0000000..562844e
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/sendreceive_unittest.cc
@@ -0,0 +1,35 @@
+// 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 <stdint.h>
+#include <gtest/gtest.h>
+
+#include "../cloptions.h"
+#include "../perfdhcp.h"
+
+TEST(SendReceiveTest, do) {
+    const char* argv[] = { "perfdhcp", "127.0.0.1" };
+    const char message[] = "This is a test";
+    char buf[sizeof(message)];
+    int numOctets;
+
+    EXPECT_EQ(1, procArgs(2, argv));
+    dhcpSetup("20942", NULL, "20942");
+    EXPECT_EQ(1, dhcpSend((const unsigned char*)message, sizeof(message)));
+    numOctets = dhcpReceive(buf, sizeof(buf));
+    EXPECT_EQ(sizeof(message), numOctets);
+    if (numOctets == sizeof(message)) {
+        EXPECT_STREQ(message, buf);
+    }
+}




More information about the bind10-changes mailing list