BIND 10 trac1265, updated. b12941d14b2b30fedb0543c33c551b780ed6340b [1265] Changes addressing last code review.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Nov 22 05:50:21 UTC 2011


The branch, trac1265 has been updated
       via  b12941d14b2b30fedb0543c33c551b780ed6340b (commit)
      from  809dc75718908b39668046c2d3db8bfb51947ecc (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 b12941d14b2b30fedb0543c33c551b780ed6340b
Author: John DuBois <johnd at isc.org>
Date:   Mon Nov 21 21:46:26 2011 -0800

    [1265]
    Changes addressing last code review.

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

Summary of changes:
 tests/tools/perfdhcp/{cloptions.cc => cloptions.c} |   48 ++---
 tests/tools/perfdhcp/cloptions.h                   |    8 +
 tests/tools/perfdhcp/{dkdebug.cc => dkdebug.c}     |    0 
 .../common/rndc.key => tools/perfdhcp/misc.c}      |   17 +-
 .../common/rndc.key => tools/perfdhcp/misc.h}      |   10 +-
 tests/tools/perfdhcp/{perfdhcp.cc => netio.c}      |  215 ++++++++++++--------
 tests/tools/perfdhcp/{perfdhcp.h => netio.h}       |   44 +---
 tests/tools/perfdhcp/perfdhcp.h                    |   61 +------
 tests/tools/perfdhcp/{procconf.cc => procconf.c}   |  190 ++++++++----------
 tests/tools/perfdhcp/procconf.h                    |   19 ++-
 tests/tools/perfdhcp/tests/Makefile.am             |    9 +-
 tests/tools/perfdhcp/tests/sendreceive_unittest.cc |   30 +++-
 12 files changed, 320 insertions(+), 331 deletions(-)
 rename tests/tools/perfdhcp/{cloptions.cc => cloptions.c} (86%)
 rename tests/tools/perfdhcp/{dkdebug.cc => dkdebug.c} (100%)
 copy tests/{system/common/rndc.key => tools/perfdhcp/misc.c} (74%)
 copy tests/{system/common/rndc.key => tools/perfdhcp/misc.h} (85%)
 rename tests/tools/perfdhcp/{perfdhcp.cc => netio.c} (65%)
 copy tests/tools/perfdhcp/{perfdhcp.h => netio.h} (63%)
 rename tests/tools/perfdhcp/{procconf.cc => procconf.c} (68%)

-----------------------------------------------------------------------
diff --git a/tests/tools/perfdhcp/cloptions.c b/tests/tools/perfdhcp/cloptions.c
new file mode 100644
index 0000000..8b48da7
--- /dev/null
+++ b/tests/tools/perfdhcp/cloptions.c
@@ -0,0 +1,277 @@
+/*
+ * 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
+static const char* VERSION = "perfdhcp v1.0 2011-10-30";
+
+const char* PROGNAME = "perfdhcp";    // Program name, for messages
+
+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 version_req = 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,    &version_req,  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) {
+        reporterr("%s", msg);
+        return(2);
+    }
+
+    if (help) {
+        printHelp(PROGNAME, usage);
+        return(0);
+    }
+    if (version_req) {
+        printf("%s\n", VERSION);
+        return(0);
+    }
+    if (diagstr != NULL) {
+        char c;
+        if ((c = dk_setup(diagstr, diagLetters)) != '\0') {
+            reporterr("Invalid selector character given with -x: '%c'", c);
+            return(2);
+        }
+    }
+
+    if (v4 && v6) {
+        reporterr("Must not give -4 and -6 together.");
+        return(2);
+    }
+    switch (argc) {
+    case 0:
+        if (v6 && localName != NULL) {
+            server = "all";
+        } else {
+            if (v6) {
+                reporterr("Use -l to specify an interface name.\n%s", usage);
+                return(2);
+            }
+            else {
+                reporterr("Must specify a DHCP server.\n%s", usage);
+            }
+            return(2);
+        }
+        break;
+    case 1:
+        server = argv[0];
+        break;
+    default:
+        reporterr("Too many arguments.\n%s", 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 it defaults to the interface through which\n\
+    traffic would normally be routed to the server.\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.cc b/tests/tools/perfdhcp/cloptions.cc
deleted file mode 100644
index 7e06d39..0000000
--- a/tests/tools/perfdhcp/cloptions.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <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
index e54bd13..981d7e2 100644
--- a/tests/tools/perfdhcp/cloptions.h
+++ b/tests/tools/perfdhcp/cloptions.h
@@ -17,6 +17,10 @@
 #ifndef CLOPTIONS_H
 #define CLOPTIONS_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <stdint.h>
 #include "procconf.h"
 
@@ -46,4 +50,8 @@ const char* getServer(void);    // Server to contact
 const char* getLocalName(void); // Local host/interface (-l)
 const char* getMaxDrop(void);   // Max dropped responses (-D)
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/tests/tools/perfdhcp/dkdebug.c b/tests/tools/perfdhcp/dkdebug.c
new file mode 100644
index 0000000..88f62dd
--- /dev/null
+++ b/tests/tools/perfdhcp/dkdebug.c
@@ -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.cc b/tests/tools/perfdhcp/dkdebug.cc
deleted file mode 100644
index 88f62dd..0000000
--- a/tests/tools/perfdhcp/dkdebug.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <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/misc.c b/tests/tools/perfdhcp/misc.c
new file mode 100644
index 0000000..7fbadc5
--- /dev/null
+++ b/tests/tools/perfdhcp/misc.c
@@ -0,0 +1,29 @@
+/*
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include "perfdhcp.h"
+
+void reporterr(const char* format, ...) {
+     va_list ap;
+
+     va_start(ap, format);
+     fprintf(stderr, "%s: ", PROGNAME);
+     vfprintf(stderr, format, ap);
+     fprintf(stderr, "\n");
+     va_end(ap);
+}
diff --git a/tests/tools/perfdhcp/misc.h b/tests/tools/perfdhcp/misc.h
new file mode 100644
index 0000000..6335576
--- /dev/null
+++ b/tests/tools/perfdhcp/misc.h
@@ -0,0 +1,22 @@
+/*
+ * 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 PERF_MISC_H
+#define PERF_MISC_H
+
+void reporterr(const char* format, ...);
+
+#endif
diff --git a/tests/tools/perfdhcp/netio.c b/tests/tools/perfdhcp/netio.c
new file mode 100644
index 0000000..f5b490c
--- /dev/null
+++ b/tests/tools/perfdhcp/netio.c
@@ -0,0 +1,415 @@
+/*
+ * 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 "netio.h"
+#include "misc.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* local_addr, const char* port,
+                       const char* type, struct sockaddr_storage* l_addr);
+static struct addrinfo* getAddresses(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);
+static int getLinkLocalAddr(const char if_name[], struct sockaddr_storage* addr);
+
+#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;     // Socket on which to send messages to DHCP server
+static recv_fd = -1;      // Socket on which to receive messages to DHCP server
+static struct addrinfo* server_addr = NULL;    // DHCP server address
+
+/*
+ * Set up network communications to talk to a DHCP server.
+ * This sets up sockets and sets the file-scoped variables used by dhcpSend().
+ *
+ * File-scoped variables:
+ * Sets: server_addr, send_fd, send_laddr, recv_fd and recv_laddr.
+ */
+void
+dhcpSetup(const char* send_port, const char* recv_port, const char *server_port) {
+    const char* local_addr = NULL;       // Local name to bind to
+    const char* interface_name = NULL;   // Interface to get link-local address from
+    int family = isV6() ? AF_INET6 : AF_INET;
+
+    if (isV6()) {
+        interface_name = getLocalName();     
+        if (interface_name != NULL) {
+            getLinkLocalAddr(interface_name, &send_laddr);
+            memcpy(&recv_laddr, &send_laddr, sizeof(recv_laddr));
+            // Do not set local_addr, so that socketSetup() will take
+            // address etc. from send_laddr.
+        } else {
+            local_addr = "::";   // IPv6 anylocal
+        }
+    } else {
+        local_addr = getLocalName();     
+        if (local_addr == NULL) {
+            local_addr = "0.0.0.0";
+        }
+    }
+    send_fd = socketSetup(family, local_addr, send_port, "Send",
+                          &send_laddr);
+    if (recv_port == NULL) {
+        recv_fd = send_fd;
+        memcpy(&recv_laddr, &send_laddr, sizeof(recv_laddr));
+
+    } else {
+        recv_fd = socketSetup(family, local_addr, recv_port, "Recv",
+                              &recv_laddr);
+    }
+    server_addr = getAddresses(family, getServer(), server_port);
+}
+
+/*
+ * Shut down connections opened by dhcpSetup()
+ */
+void
+netShutdown(void) {
+    if (send_fd != -1) {
+        close(send_fd);
+    }
+    if (recv_fd != -1 && recv_fd != send_fd) {
+        close(recv_fd);
+    }
+    recv_fd = send_fd = -1;
+    if (server_addr != NULL) {
+        freeaddrinfo(server_addr);
+        server_addr = NULL;
+    }
+}
+
+/*
+ * dhcpSend: Send a DHCP packet.
+ *
+ * File-scoped variables:
+ * 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.
+ */
+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\n",
+                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) {
+        reporterr("Send failed: %s", strerror(errno));
+        return (0);
+    }
+    if ((size_t) num_written != num_octets) {
+        reporterr("Only %zd of %zu octets written", num_written, num_octets);
+        return (0);
+    }
+    return (1);
+}
+
+/*
+ * dhcpReceive: Receive a DHCP packet.
+ *
+ * File-scoped variables:
+ * recv_fd is the socket to receive on.
+ * recv_laddr is the socket's address, solely for informational messages.
+ *
+ */
+int
+dhcpReceive(void* msg, size_t msgsize) {
+    ssize_t octets_received;             // Number of octets received
+    struct sockaddr_storage source_addr; // 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\n", recv_fd,
+                 addrAndPortToNames(&recv_laddr, addrbuf, sizeof(addrbuf)));
+    }
+    addrsize = sizeof(source_addr);
+    if (msgsize == 0) {
+        msgsize = isV6() ? sizeof(struct dhcpv6_packet) :
+                sizeof(struct dhcp_packet);
+    }
+    octets_received = recvfrom(recv_fd, msg, msgsize, 0,
+                              (struct sockaddr*)&source_addr, &addrsize);
+    if (dk_set(DK_MSG)) {
+        fprintf(stderr, "Got %zd octets from fd %u, %s\n", octets_received,
+                recv_fd,
+                addrAndPortToNames(&source_addr, addrbuf, sizeof(addrbuf)));
+    }
+    if (octets_received < 0) {
+        reporterr("Receive failed: %s", strerror(errno));
+    } else if (octets_received == 0) {
+        reporterr("Remote closed connection.");
+    }
+    return (octets_received);
+}
+
+/*
+ * 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
+ * local_addr 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.  This allows the flow
+ *     info and scope to be specified.
+ *
+ * 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* local_addr, 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 = getAddresses(addr_fam, local_addr, port);
+    if (addr_fam == AF_INET6 && local_addr == NULL) {
+        if (dk_set(DK_SOCK)) {
+            fprintf(stderr, "local address:\n");
+            printSockaddr6Info(stderr, (struct sockaddr_in6*)l_addr);
+        }
+
+        // Get the local address, flow info, and scope from 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)) {
+        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) {
+        reporterr("socket creation failed: %s", strerror(errno));
+        exit(1);
+    }
+    if (bind(net_fd, addrs->ai_addr, addrs->ai_addrlen) == -1) {
+        int s_errno = errno;
+        reporterr("Could not bind to %s: %s", 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);
+}
+
+/*
+ * getAddresses: 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 a name, an IPv4 dotted-quad
+ *     address, an IPv6 hexadecimal 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.
+ *
+ * Return value:
+ * A pointer to the addrinfo list.  This must be freed by the caller with
+ * freeaddrinfo().
+ */
+static struct addrinfo*
+getAddresses(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.
+         * Be extra sure we always have an answer if we return.
+         */
+        reporterr("getaddrinfo: host %s port %s: %s",
+                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)));
+}
+
+/*
+ * Find the link-local address for an interface.
+ *
+ * Input variables:
+ * if_name is the name of the interface to search for.
+ *
+ * Output variables:
+ * The link-local address for the interface is stored in addr.
+ *
+ * Return value:
+ * 1 on success, 0 if no link-local address is found.
+ *
+ * If retrieval of the interface address list fails, an error message is
+ * printed and the program is exited with status 2.
+ */
+static int
+getLinkLocalAddr(const char if_name[], struct sockaddr_storage* addr) {
+    struct ifaddrs* ifaddr;     // interface address list
+    struct ifaddrs* ifa;        // For iterating through above
+
+    if (getifaddrs(&ifaddr) == -1) {
+        reporterr("Could not get interface addresses: %s", strerror(errno));
+        exit(2);
+    }
+
+    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
+        if (ifa->ifa_addr->sa_family == AF_INET6 && strcmp(ifa->ifa_name, if_name) == 0 &&
+                (ntohs(((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr.__in6_u.__u6_addr16[0]) & 0xffc0) == 0xfe80) {
+            break;
+        }
+    }
+    if (ifa != NULL) {
+        memcpy(addr, ifa->ifa_addr, sizeof(struct sockaddr_storage));
+    }
+    freeifaddrs(ifaddr);
+    return (ifa != NULL);
+}
diff --git a/tests/tools/perfdhcp/netio.h b/tests/tools/perfdhcp/netio.h
new file mode 100644
index 0000000..3dc002d
--- /dev/null
+++ b/tests/tools/perfdhcp/netio.h
@@ -0,0 +1,80 @@
+/*
+ * 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
+
+/*
+ * Set up network communications to talk to a DHCP server.
+ * This sets up sockets and sets the variables used by dhcpSend().
+ *
+ * Input variables:
+ * send_port is the local port from which to send communications.
+ * recv_port is the local port on which to listen for communications.
+ *     If null, the receive socket is made the same as the send socket.
+ * server_port is the port on the server to communicate with.
+ */
+void dhcpSetup(const char* send_port, const char* recv_port,
+               const char *server_port);
+
+/*
+ * Shut down connections opened by dhcpSetup()
+ */
+void netShutdown(void);
+
+/*
+ * dhcpSend: Send a DHCP packet.
+ *
+ * Input variables:
+ * dhcp_packet: DHCP message to send.
+ * num_octets: Size of message.
+ *
+ * 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 receive buffer is taken to have the size of either a hdcpv6_packet
+ * or a dhcp_packet, depending on whether v6 operation is in effect.
+ *
+ * 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/perfdhcp.cc b/tests/tools/perfdhcp/perfdhcp.cc
deleted file mode 100644
index 72aa8d5..0000000
--- a/tests/tools/perfdhcp/perfdhcp.cc
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <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
index 39a7b5e..15fc95e 100644
--- a/tests/tools/perfdhcp/perfdhcp.h
+++ b/tests/tools/perfdhcp/perfdhcp.h
@@ -21,8 +21,6 @@
 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
@@ -34,64 +32,7 @@ enum {
     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);
+extern const char* PROGNAME;    // Program name, for messages
 
 #ifdef __cplusplus
 }
diff --git a/tests/tools/perfdhcp/procconf.c b/tests/tools/perfdhcp/procconf.c
new file mode 100644
index 0000000..47e0537
--- /dev/null
+++ b/tests/tools/perfdhcp/procconf.c
@@ -0,0 +1,466 @@
+/*
+ * 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 <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
+ * file-scoped variable 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 file-scoped
+ * variable 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 file-scoped
+ * variable errmsg and -1 is returned.
+ */
+
+static int
+procCmdLineArgs(int* argc, const char*** argv, const confvar_t optconf[],
+                confval** per_opt_records_first, confval** per_opt_records_last) {
+    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 optchar_to_conf[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;
+            }
+            optchar_to_conf[outind] = confNum;
+        }
+    }
+
+    *p = '\0';
+    optind = 1;
+    // The reality is that getopt does *not* modify the strings,
+    // and GNU getopt *does* modify the pointers!
+    while ((optchar = getopt(*argc, (char* const *)*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 = optchar_to_conf[optchar];
+        switch (ret = addOptVal(optarg, &optconf[ind],
+                                &per_opt_records_first[ind],
+                                &per_opt_records_last[ind])) {
+        case 1:
+            count++;
+            break;
+        case 0:
+            break;
+        default:
+            return(ret);
+        }
+    }
+    *argc -= optind;
+    *argv += optind;
+    return(count);
+}
+
+/*
+ * Process a set of command line arguments.
+ *
+ * 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.
+ */
+const char*
+procOpts(int* argc, char const*** 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** per_opt_records_first;
+    confval** per_opt_records_last;
+
+    /* 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** value_pointers;    /* 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 ((per_opt_records_first = (confval**)pc_malloc(sizeof(confval*) * numconf))
+            == NULL || (per_opt_records_last =
+            (confval**)pc_malloc(sizeof(confval*) * numconf)) == NULL) {
+        return(errmsg);
+    }
+
+    numoptsfound = procCmdLineArgs(argc, argv, optconf, per_opt_records_first,
+                                   per_opt_records_last);
+    free(per_opt_records_last);
+    per_opt_records_last = NULL;
+
+    if (numoptsfound < 0)
+    {
+        free(per_opt_records_first);
+        return(errmsg);
+    }
+    if (confdata == NULL) {
+        free(per_opt_records_first);
+        return NULL;
+    }
+
+    /*
+     * All options have been read & initial processing done.
+     * An array of pointers is now generated for the options.
+     */
+    if ((value_pointers =
+            (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 value_pointers,
+     * 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 = &value_pointers[i];
+        if (outind != 0) {
+            confdata->map[outind & ~CF_NOTFLAG] = &confdata->optVals[optnum];
+        }
+        for (optval = per_opt_records_first[optnum]; optval != NULL;
+                 optval = optval->next) {
+            confdata->optVals[optnum].num++;
+            value_pointers[i++] = optval;
+        }
+    }
+    free(per_opt_records_first);
+    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.cc b/tests/tools/perfdhcp/procconf.cc
deleted file mode 100644
index ac7a445..0000000
--- a/tests/tools/perfdhcp/procconf.cc
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <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
index fd29c00..e0c08e6 100644
--- a/tests/tools/perfdhcp/procconf.h
+++ b/tests/tools/perfdhcp/procconf.h
@@ -22,6 +22,10 @@
 #ifndef PROCCONF_H
 #define PROCCONF_H
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #include <limits.h> /* for UINT_MAX */
 
 /*
@@ -94,19 +98,22 @@ typedef struct {
 /*
  * Input variables:
  * argc, argv: Command line data.
- * optConf[]: Option description structures.  The array should end with an
+ * 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.
+ * If confdata 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 have been used,
+ * For any option for which an option character/index has been used,
  * confdata->map[index] is set to the same data.
+ * Option values are stored at the value given by any confvar that has a
+ * non-null address.
  * 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.
@@ -116,7 +123,7 @@ typedef struct {
  * On error, a message describing the problem.
  */
 const char*
-procOpts(int* argc, const char** argv[], const confvar_t optConf[],
+procOpts(int* argc, char const*** argv, const confvar_t optconf[],
          confdata_t* confdata, const char name[],
          const char usage[]);
 
@@ -125,4 +132,8 @@ procOpts(int* argc, const char** argv[], const confvar_t optConf[],
  */
 void confdataFree(confdata_t *confdata);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index f490a55..f19f7d9 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -15,10 +15,11 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.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
-run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/dkdebug.cc
-run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perfdhcp.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/cloptions.c
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/procconf.c
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/dkdebug.c
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/netio.c
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/misc.c
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
diff --git a/tests/tools/perfdhcp/tests/sendreceive_unittest.cc b/tests/tools/perfdhcp/tests/sendreceive_unittest.cc
index 562844e..5fcd96f 100644
--- a/tests/tools/perfdhcp/tests/sendreceive_unittest.cc
+++ b/tests/tools/perfdhcp/tests/sendreceive_unittest.cc
@@ -16,15 +16,16 @@
 #include <gtest/gtest.h>
 
 #include "../cloptions.h"
-#include "../perfdhcp.h"
+#include "../netio.h"
 
-TEST(SendReceiveTest, do) {
+TEST(SendReceiveTest, v4) {
+    // Invoke command line processor to set up for IPv4 operation, localhost
     const char* argv[] = { "perfdhcp", "127.0.0.1" };
-    const char message[] = "This is a test";
-    char buf[sizeof(message)];
+    const char message[] = "This is a test for IPv4";
+    char buf[1024];
     int numOctets;
 
-    EXPECT_EQ(1, procArgs(2, argv));
+    EXPECT_EQ(1, procArgs(sizeof(argv)/sizeof(char *), argv));
     dhcpSetup("20942", NULL, "20942");
     EXPECT_EQ(1, dhcpSend((const unsigned char*)message, sizeof(message)));
     numOctets = dhcpReceive(buf, sizeof(buf));
@@ -32,4 +33,23 @@ TEST(SendReceiveTest, do) {
     if (numOctets == sizeof(message)) {
         EXPECT_STREQ(message, buf);
     }
+    netShutdown();
+}
+
+TEST(SendReceiveTest, v6) {
+    // Invoke command line processor to set up for IPv6 operation, localhost
+    const char* argv[] = { "perfdhcp", "-6", "::1" };
+    const char message[] = "This is a test for IPv6";
+    char buf[1024];
+    int numOctets;
+
+    EXPECT_EQ(1, procArgs(sizeof(argv)/sizeof(char *), 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);
+    }
+    netShutdown();
 }




More information about the bind10-changes mailing list