BIND 10 master, updated. 6c192e5c0903f349b4d80cf2bb6cd964040ae7da [master] Merge branch 'trac1960'

BIND 10 source code commits bind10-changes at lists.isc.org
Wed Sep 26 08:50:40 UTC 2012


The branch, master has been updated
       via  6c192e5c0903f349b4d80cf2bb6cd964040ae7da (commit)
       via  d570aa48262648cfccdf96e01b1c7c66a9d0839d (commit)
       via  85c7fb2e4627e84c2d17c59432509a221034e6ea (commit)
       via  d98f7060780fc89f05c1610d3e88403969dfc819 (commit)
       via  efeb8e81af78b9bca08785646948da46aec3d1c8 (commit)
       via  d82d52518ab96e3c7aece6c2c4ebcb599b993770 (commit)
       via  1841fbb5bc6e588c311f22360bb59462a244c644 (commit)
       via  2538cdc2251cc27c38193680af90f2e7e94ffb57 (commit)
       via  4f202471b02965f0bd3c402d1bffa503c5ee6527 (commit)
       via  7f5343e069bf811dc88bea5fad759bbe7fe4f78c (commit)
       via  6ec1f3e61319b6da0ed8567bc53c1c5cf7455c9a (commit)
       via  f4c07bf162d7ed4495496f7574242ccd2add5926 (commit)
       via  40ee3e7167ec6660de797955af57ec9f5ec67c79 (commit)
       via  4de75117f84ad23f96392677338e733d6e1e87bd (commit)
       via  5da8f0ce275404a225759f9673daffa4d9da386a (commit)
       via  b1b7f7957f6ae64bf1621368548127a4ccf5acc3 (commit)
       via  9967f37261be7acaecff0dc8d3fe6b1fc49fc4d0 (commit)
       via  7ece465effe5156585f24bb60e836211c8aacab0 (commit)
       via  6517287182efdd74a30184c4806634b0de5ef049 (commit)
       via  f13266d9802fa12a8bf223d94b6fe97923ca384a (commit)
       via  861b38913123f90f4ab68d105d1111b2e42a6481 (commit)
       via  f07fdbfa1a7d0f592c0b3d9e1a10e287dc608dd1 (commit)
       via  a897e5cf38d32a41107f537bc8dace96813b06d8 (commit)
      from  b3419fdfa017ef3fd88aae1fc800ebb72ceaad02 (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 6c192e5c0903f349b4d80cf2bb6cd964040ae7da
Merge: b3419fd d570aa4
Author: Marcin Siodelski <marcin at isc.org>
Date:   Wed Sep 26 09:58:50 2012 +0200

    [master] Merge branch 'trac1960'
    
    Conflicts:
    	tests/tools/perfdhcp/Makefile.am
    	tests/tools/perfdhcp/tests/command_options_unittest.cc

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

Summary of changes:
 configure.ac                                       |   14 -
 src/lib/dhcp/dhcp4.h                               |    3 +
 src/lib/dhcp/iface_mgr.cc                          |    2 +-
 tests/tools/perfdhcp/Makefile.am                   |   35 +-
 tests/tools/perfdhcp/command_options.cc            |  454 +--
 tests/tools/perfdhcp/command_options.h             |  160 +-
 tests/tools/perfdhcp/main.cc                       |   28 +-
 tests/tools/perfdhcp/perfdhcp.c                    | 3559 --------------------
 tests/tools/perfdhcp/stats_mgr.h                   |  136 +-
 tests/tools/perfdhcp/templates/Makefile.am         |    4 +-
 tests/tools/perfdhcp/test_control.cc               |  497 ++-
 tests/tools/perfdhcp/test_control.h                |  239 +-
 .../tools/perfdhcp/tests/command_options_helper.h  |    5 +-
 .../perfdhcp/tests/command_options_unittest.cc     |   84 +-
 tests/tools/perfdhcp/tests/stats_mgr_unittest.cc   |    3 +-
 .../tools/perfdhcp/tests/test_control_unittest.cc  |   64 +-
 16 files changed, 1156 insertions(+), 4131 deletions(-)
 delete mode 100644 tests/tools/perfdhcp/perfdhcp.c

-----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 6c83bac..31306e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -990,20 +990,6 @@ CPPFLAGS="$CPPFLAGS -DASIO_DISABLE_THREADS=1"
 # Check for functions that are not available on all platforms
 AC_CHECK_FUNCS([pselect])
 
-# perfdhcp: If the clock_gettime() function does not exist on the system,
-# use an alternative supplied in the code based on gettimeofday().
-CLOCK_GETTIME_LDFLAGS=
-AC_CHECK_LIB([rt], [clock_gettime], [CLOCK_GETTIME_LDFLAGS=-lrt], [])
-AC_SUBST([CLOCK_GETTIME_LDFLAGS])
-
-# perfdhcp: if getifaddrs() does not exist, have the code output a message
-# that it can't be run on this version of the operating system.  For the
-# systems on which BIND 10 is built, this means Solaris 10. (Replacements
-# for this function are long and involved, and the function is reported present
-# on Solaris 11 and later, either in the libsocket or libnsl libraries.)
-AC_SEARCH_LIBS([getifaddrs], [socket nsl],
-               [AC_DEFINE([HAVE_GETIFADDRS], [1], [getifaddrs() present])])
-
 # /dev/poll issue: ASIO uses /dev/poll by default if it's available (generally
 # the case with Solaris).  Unfortunately its /dev/poll specific code would
 # trigger the gcc's "missing-field-initializers" warning, which would
diff --git a/src/lib/dhcp/dhcp4.h b/src/lib/dhcp/dhcp4.h
index e058960..002e74f 100644
--- a/src/lib/dhcp/dhcp4.h
+++ b/src/lib/dhcp/dhcp4.h
@@ -39,6 +39,9 @@
 namespace isc {
 namespace dhcp {
 
+/* IPv4 Broadcast address */
+#define DHCP_IPV4_BROADCAST_ADDRESS "255.255.255.255"
+
 /* BOOTP (rfc951) message types */
 enum BOOTPTypes {
     BOOTREQUEST = 1,
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index a2f807e..1ff2fc6 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -483,7 +483,7 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
     // If remote address is broadcast address we have to
     // allow this on the socket.
     if (remote_addr.getAddress().is_v4() &&
-        (remote_addr == IOAddress("255.255.255.255"))) {
+        (remote_addr == IOAddress(DHCP_IPV4_BROADCAST_ADDRESS))) {
         // Socket has to be open prior to setting the broadcast
         // option. Otherwise set_option will complain about
         // bad file descriptor.
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 816eeeb..08a21a4 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -12,36 +12,33 @@ AM_CXXFLAGS = $(B10_CXXFLAGS)
 # But older GCC compilers don't have the flag.
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
-AM_LDFLAGS = $(CLOCK_GETTIME_LDFLAGS)
-AM_LDFLAGS += -lm
+AM_LDFLAGS = -lm
 if USE_STATIC_LINK
 AM_LDFLAGS += -static
 endif
 
-pkglibexec_PROGRAMS = perfdhcp2
-perfdhcp2_SOURCES = main.cc
-perfdhcp2_SOURCES += command_options.cc command_options.h
-perfdhcp2_SOURCES += localized_option.h
-perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h
-perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h
-perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h
-perfdhcp2_SOURCES += stats_mgr.h
-perfdhcp2_SOURCES += test_control.cc test_control.h
+bin_PROGRAMS = perfdhcp
+perfdhcp_SOURCES = main.cc
+perfdhcp_SOURCES += command_options.cc command_options.h
+perfdhcp_SOURCES += localized_option.h
+perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h
+perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h
+perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h
+perfdhcp_SOURCES += stats_mgr.h
+perfdhcp_SOURCES += test_control.cc test_control.h
 libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 
-perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS)
+perfdhcp_CXXFLAGS = $(AM_CXXFLAGS)
 if USE_CLANGPP
 # Disable unused parameter warning caused by some of the
 # Boost headers when compiling with clang.
-perfdhcp2_CXXFLAGS += -Wno-unused-parameter
+perfdhcp_CXXFLAGS += -Wno-unused-parameter
 endif
 
-perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
-perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
+perfdhcp_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 
-#pkglibexec_PROGRAMS  = perfdhcp
-#perfdhcp_SOURCES  = perfdhcp.c
 
 # ... and the documentation
-EXTRA_DIST = perfdhcp_internals.dox
+EXTRA_DIST = perfdhcp_internals.dox
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 91c5819..9aa6abf 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -18,13 +18,10 @@
 #include <stdint.h>
 #include <unistd.h>
 
-#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 #include <exceptions/exceptions.h>
-#include <dhcp/dhcp6.h>
 #include <dhcp/iface_mgr.h>
 #include "command_options.h"
 
@@ -86,10 +83,9 @@ CommandOptions::reset() {
     wrapped_.clear();
     server_name_.clear();
     generateDuidTemplate();
-    commandline_.clear();
 }
 
-void
+bool
 CommandOptions::parse(int argc, char** const argv) {
     // Reset internal variables used by getopt
     // to eliminate undefined behavior when
@@ -127,18 +123,23 @@ CommandOptions::parse(int argc, char** const argv) {
     // Reset values of class members
     reset();
 
-    initialize(argc, argv);
-    validate();
+    // Informs if program has been run with 'h' or 'v' option.
+    bool help_or_version_mode = initialize(argc, argv);
+    if (!help_or_version_mode) {
+        validate();
+    }
+    return (help_or_version_mode);
 }
 
-void
+bool
 CommandOptions::initialize(int argc, char** argv) {
     char opt = 0;               // Subsequent options returned by getopt()
     std::string drop_arg;       // Value of -D<value>argument
     size_t percent_loc = 0;     // Location of % sign in -D<value>
     double drop_percent = 0;    // % value (1..100) in -D<value%>
     int num_drops = 0;          // Max number of drops specified in -D<value>
-    int num_req = 0;            // Max number of dropped requests in -n<max-drops>
+    int num_req = 0;            // Max number of dropped
+                                // requests in -n<max-drops>
     int offset_arg = 0;         // Temporary variable holding offset arguments
     std::string sarg;           // Temporary variable for string args
 
@@ -147,16 +148,13 @@ CommandOptions::initialize(int argc, char** argv) {
 
     // In this section we collect argument values from command line
     // they will be tuned and validated elsewhere
-    while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
+    while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
+                        "s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
         stream << " -" << opt;
         if (optarg) {
             stream << " " << optarg;
         }  
         switch (opt) {
-        case 'v':
-            version();
-            return;
-
         case '1':
             use_first_ = true;
             break;
@@ -172,11 +170,13 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'a':
-            aggressivity_ = positiveInteger("value of aggressivity: -a<value> must be a positive integer");
+            aggressivity_ = positiveInteger("value of aggressivity: -a<value>"
+                                            " must be a positive integer");
             break;
 
         case 'b':
-            check(base_.size() > 3, "-b<value> already specified, unexpected occurence of 5th -b<value>");
+            check(base_.size() > 3, "-b<value> already specified,"
+                  " unexpected occurence of 5th -b<value>");
             base_.push_back(optarg);
             decodeBase(base_.back());
             break;
@@ -190,53 +190,64 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'd':
-            check(drop_time_set_ > 1, "maximum number of drops already specified, "
+            check(drop_time_set_ > 1,
+                  "maximum number of drops already specified, "
                   "unexpected 3rd occurence of -d<value>");
             try {
-                drop_time_[drop_time_set_] = boost::lexical_cast<double>(optarg);
+                drop_time_[drop_time_set_] =
+                    boost::lexical_cast<double>(optarg);
             } catch (boost::bad_lexical_cast&) {
                 isc_throw(isc::InvalidParameter,
-                          "value of drop time: -d<value> must be positive number");
+                          "value of drop time: -d<value>"
+                          " must be positive number");
             }
-            check(drop_time_[drop_time_set_] <= 0., "drop-time must be a positive number");
+            check(drop_time_[drop_time_set_] <= 0.,
+                  "drop-time must be a positive number");
             drop_time_set_ = true;
             break;
 
         case 'D':
             drop_arg = std::string(optarg);
             percent_loc = drop_arg.find('%');
-            check(max_pdrop_.size() > 1 || max_drop_.size() > 1, "values of maximum drops: -D<value> already "
+            check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
+                  "values of maximum drops: -D<value> already "
                   "specified, unexpected 3rd occurence of -D,value>");
             if ((percent_loc) != std::string::npos) {
                 try {
-                    drop_percent = boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
+                    drop_percent =
+                        boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
                 } catch (boost::bad_lexical_cast&) {
                     isc_throw(isc::InvalidParameter,
-                              "value of drop percentage: -D<value%> must be 0..100");
+                              "value of drop percentage: -D<value%>"
+                              " must be 0..100");
                 }
                 check((drop_percent <= 0) || (drop_percent >= 100),
                   "value of drop percentage: -D<value%> must be 0..100");
                 max_pdrop_.push_back(drop_percent);
             } else {
-                num_drops = positiveInteger("value of max drops number: -d<value> must be a positive integer");
+                num_drops = positiveInteger("value of max drops number:"
+                                            " -d<value> must be a positive integer");
                 max_drop_.push_back(num_drops);
             }
             break;
 
         case 'E':
-            elp_offset_ = nonNegativeInteger("value of time-offset: -E<value> must not be a negative integer");
+            elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
+                                             " must not be a negative integer");
             break;
 
         case 'h':
             usage();
-            return;
+            return (true);
 
         case 'i':
             exchange_mode_ = DO_SA;
             break;
 
         case 'I':
-            rip_offset_ = positiveInteger("value of ip address offset: -I<value> must be a positive integer");
+            rip_offset_ = positiveInteger("value of ip address offset:"
+                                          " -I<value> must be a"
+                                          " positive integer");
             break;
 
         case 'l':
@@ -245,43 +256,55 @@ CommandOptions::initialize(int argc, char** argv) {
             break;
 
         case 'L':
-             local_port_ = nonNegativeInteger("value of local port: -L<value> must not be a negative integer");
-             check(local_port_ > static_cast<int>(std::numeric_limits<uint16_t>::max()),
+             local_port_ = nonNegativeInteger("value of local port:"
+                                              " -L<value> must not be a"
+                                              " negative integer");
+             check(local_port_ >
+                   static_cast<int>(std::numeric_limits<uint16_t>::max()),
                   "local-port must be lower than " +
                   boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
             break;
 
         case 'n':
-            num_req = positiveInteger("value of num-request: -n<value> must be a positive integer");
+            num_req = positiveInteger("value of num-request:"
+                                      " -n<value> must be a positive integer");
             if (num_request_.size() >= 2) {
-                isc_throw(isc::InvalidParameter,"value of maximum number of requests: -n<value> "
-                          "already specified, unexpected 3rd occurence of -n<value>");
+                isc_throw(isc::InvalidParameter,
+                          "value of maximum number of requests: -n<value> "
+                          "already specified, unexpected 3rd occurence"
+                          " of -n<value>");
             }
             num_request_.push_back(num_req);
             break;
 
         case 'O':
             if (rnd_offset_.size() < 2) {
-                offset_arg = positiveInteger("value of random offset: -O<value> must be greater than 3");
+                offset_arg = positiveInteger("value of random offset: "
+                                             "-O<value> must be greater than 3");
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "random offsets already specified, unexpected 3rd occurence of -O<value>");
+                          "random offsets already specified,"
+                          " unexpected 3rd occurence of -O<value>");
             }
-            check(offset_arg < 3, "value of random random-offset: -O<value> must be greater than 3 ");
+            check(offset_arg < 3, "value of random random-offset:"
+                  " -O<value> must be greater than 3 ");
             rnd_offset_.push_back(offset_arg);
             break;
 
         case 'p':
-            period_ = positiveInteger("value of test period: -p<value> must be a positive integer");
+            period_ = positiveInteger("value of test period:"
+                                      " -p<value> must be a positive integer");
             break;
 
         case 'P':
-            preload_ = nonNegativeInteger("number of preload packets: -P<value> must not be "
+            preload_ = nonNegativeInteger("number of preload packets:"
+                                          " -P<value> must not be "
                                           "a negative integer");
             break;
 
         case 'r':
-            rate_ = positiveInteger("value of rate: -r<value> must be a positive integer");
+            rate_ = positiveInteger("value of rate:"
+                                    " -r<value> must be a positive integer");
             break;
 
         case 'R':
@@ -290,42 +313,58 @@ CommandOptions::initialize(int argc, char** argv) {
 
         case 's':
             seed_ = static_cast<unsigned int>
-                (nonNegativeInteger("value of seed: -s <seed> must be non-negative integer"));
+                (nonNegativeInteger("value of seed:"
+                                    " -s <seed> must be non-negative integer"));
             seeded_ = seed_ > 0 ? true : false;
             break;
 
         case 'S':
-            sid_offset_ = positiveInteger("value of server id offset: -S<value> must be a positive integer");
+            sid_offset_ = positiveInteger("value of server id offset:"
+                                          " -S<value> must be a"
+                                          " positive integer");
             break;
 
         case 't':
-            report_delay_ = positiveInteger("value of report delay: -t<value> must be a positive integer");
+            report_delay_ = positiveInteger("value of report delay:"
+                                            " -t<value> must be a"
+                                            " positive integer");
             break;
 
         case 'T':
             if (template_file_.size() < 2) {
-                sarg = nonEmptyString("template file name not specified, expected -T<filename>");
+                sarg = nonEmptyString("template file name not specified,"
+                                      " expected -T<filename>");
                 template_file_.push_back(sarg);
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "template files are already specified, unexpected 3rd -T<filename> occurence");
+                          "template files are already specified,"
+                          " unexpected 3rd -T<filename> occurence");
             }
             break;
 
+        case 'v':
+            version();
+            return (true);
+
         case 'w':
-            wrapped_ = nonEmptyString("command for wrapped mode: -w<command> must be specified");
+            wrapped_ = nonEmptyString("command for wrapped mode:"
+                                      " -w<command> must be specified");
             break;
 
         case 'x':
-            diags_ = nonEmptyString("value of diagnostics selectors: -x<value> must be specified");
+            diags_ = nonEmptyString("value of diagnostics selectors:"
+                                    " -x<value> must be specified");
             break;
 
         case 'X':
             if (xid_offset_.size() < 2) {
-                offset_arg = positiveInteger("value of transaction id: -X<value> must be a positive integer");
+                offset_arg = positiveInteger("value of transaction id:"
+                                             " -X<value> must be a"
+                                             " positive integer");
             } else {
                 isc_throw(isc::InvalidParameter,
-                          "transaction ids already specified, unexpected 3rd -X<value> occurence");
+                          "transaction ids already specified,"
+                          " unexpected 3rd -X<value> occurence");
             }
             xid_offset_.push_back(offset_arg);
             break;
@@ -335,8 +374,6 @@ CommandOptions::initialize(int argc, char** argv) {
         }
     }
 
-    std::cout << "Running: " << stream.str() << std::endl;
-
     // If the IP version was not specified in the
     // command line, assume IPv4.
     if (ipversion_ == 0) {
@@ -364,26 +401,30 @@ CommandOptions::initialize(int argc, char** argv) {
     check(optind < argc -1, "extra arguments?");
     if (optind == argc - 1) {
         server_name_ = argv[optind];
+        stream << " " << server_name_;
         // Decode special cases
         if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
-            broadcast_ = 1;
-            // 255.255.255.255 is IPv4 broadcast address
-            server_name_ = "255.255.255.255";
+            broadcast_ = true;
+            // Use broadcast address as server name.
+            server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
         } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
-            server_name_ = "FF02::1:2";
-        } else if ((ipversion_ == 6) && (server_name_.compare("servers") == 0)) {
-            server_name_ = "FF05::1:3";
+            server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
+        } else if ((ipversion_ == 6) &&
+                   (server_name_.compare("servers") == 0)) {
+            server_name_ = ALL_DHCP_SERVERS;
         }
     }
 
+    std::cout << "Running: " << stream.str() << std::endl;
+
     // Handle the local '-l' address/interface
     if (!localname_.empty()) {
         if (server_name_.empty()) {
             if (is_interface_ && (ipversion_ == 4)) {
-                broadcast_ = 1;
-                server_name_ = "255.255.255.255";
+                broadcast_ = true;
+                server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
             } else if (is_interface_ && (ipversion_ == 6)) {
-                server_name_ = "FF02::1:2";
+                server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
             }
         }
     }
@@ -397,11 +438,13 @@ CommandOptions::initialize(int argc, char** argv) {
     if (duid_template_.size() == 0) {
         generateDuidTemplate();
     }
+    return (false);
 }
 
 void
 CommandOptions::initClientsNum() {
-    const std::string errmsg = "value of -R <value> must be non-negative integer";
+    const std::string errmsg =
+        "value of -R <value> must be non-negative integer";
 
     // Declare clients_num as as 64-bit signed value to
     // be able to detect negative values provided
@@ -410,11 +453,7 @@ CommandOptions::initClientsNum() {
     long long clients_num = 0;
     try {
         clients_num = boost::lexical_cast<long long>(optarg);
-    } catch (boost::bad_lexical_cast&) {
-        isc_throw(isc::InvalidParameter, errmsg.c_str());
-    }
-    check(clients_num < 0, errmsg);
-    try {
+        check(clients_num < 0, errmsg);
         clients_num_ = boost::lexical_cast<uint32_t>(optarg);
     } catch (boost::bad_lexical_cast&) {
         isc_throw(isc::InvalidParameter, errmsg);
@@ -444,7 +483,8 @@ CommandOptions::decodeBase(const std::string& base) {
         decodeDuid(b);
     } else {
         isc_throw(isc::InvalidParameter,
-                  "base value not provided as -b<value>, expected -b mac=<mac> or -b duid=<duid>");
+                  "base value not provided as -b<value>,"
+                  " expected -b mac=<mac> or -b duid=<duid>");
     }
 }
 
@@ -452,7 +492,9 @@ void
 CommandOptions::decodeMac(const std::string& base) {
     // Strip string from mac=
     size_t found = base.find('=');
-    static const char* errmsg = "expected -b<base> format for mac address is -b mac=00::0C::01::02::03::04";
+    static const char* errmsg = "expected -b<base> format for"
+        " mac address is -b mac=00::0C::01::02::03::04 or"
+        " -b mac=00:0C:01:02:03:04";
     check(found == std::string::npos, errmsg);
 
     // Decode mac address to vector of uint8_t
@@ -485,7 +527,8 @@ CommandOptions::decodeDuid(const std::string& base) {
     // Strip argument from duid=
     std::vector<uint8_t> duid_template;
     size_t found = base.find('=');
-    check(found == std::string::npos, "expected -b<base> format for duid is -b duid=<duid>");
+    check(found == std::string::npos, "expected -b<base>"
+          " format for duid is -b duid=<duid>");
     std::string b = base.substr(found + 1);
 
     // DUID must have even number of digits and must not be longer than 64 bytes
@@ -501,7 +544,8 @@ CommandOptions::decodeDuid(const std::string& base) {
             ui = convertHexString(b.substr(i, 2));
         } catch (isc::InvalidParameter&) {
             isc_throw(isc::InvalidParameter,
-                      "invalid characters in DUID provided, exepected hex digits");
+                      "invalid characters in DUID provided,"
+                      " exepected hex digits");
         }
         duid_template.push_back(static_cast<uint8_t>(ui));
     }
@@ -527,7 +571,7 @@ CommandOptions::generateDuidTemplate() {
     duid_template_[1] = DUID_LLT & 0xff;
     duid_template_[2] = HWTYPE_ETHERNET >> 8;
     duid_template_[3] = HWTYPE_ETHERNET & 0xff;
-    
+
     // As described in RFC3315: 'the time value is the time
     // that the DUID is generated represented in seconds
     // since midnight (UTC), January 1, 2000, modulo 2^32.'
@@ -559,7 +603,8 @@ CommandOptions::convertHexString(const std::string& text) const {
     text_stream >> std::hex >> ui >> std::dec;
     // Check if for some reason we have overflow - this should never happen!
     if (ui > 0xFF) {
-        isc_throw(isc::InvalidParameter, "Can't convert more than two hex digits to byte");
+        isc_throw(isc::InvalidParameter, "Can't convert more than"
+                  " two hex digits to byte");
     }
     return ui;
 }
@@ -591,29 +636,29 @@ CommandOptions::validate() const {
           "-S<srvid-offset> is not compatible with -i\n");
     check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
           "-I<ip-offset> is not compatible with -i\n");
-	check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
+    check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
           "-i must be set to use -c\n");
-	check((getRate() == 0) && (getReportDelay() != 0),
+    check((getRate() == 0) && (getReportDelay() != 0),
           "-r<rate> must be set to use -t<report>\n");
-	check((getRate() == 0) && (getNumRequests().size() > 0),
+    check((getRate() == 0) && (getNumRequests().size() > 0),
           "-r<rate> must be set to use -n<num-request>\n");
-	check((getRate() == 0) && (getPeriod() != 0),
+    check((getRate() == 0) && (getPeriod() != 0),
           "-r<rate> must be set to use -p<test-period>\n");
-	check((getRate() == 0) &&
+    check((getRate() == 0) &&
           ((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
           "-r<rate> must be set to use -D<max-drop>\n");
-	check((getTemplateFiles().size() < getTransactionIdOffset().size()),
+    check((getTemplateFiles().size() < getTransactionIdOffset().size()),
           "-T<template-file> must be set to use -X<xid-offset>\n");
-	check((getTemplateFiles().size() < getRandomOffset().size()),
+    check((getTemplateFiles().size() < getRandomOffset().size()),
           "-T<template-file> must be set to use -O<random-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
+    check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
           "second/request -T<template-file> must be set to use -E<time-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
+    check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
           "second/request -T<template-file> must be set to "
           "use -S<srvid-offset>\n");
-	check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
-			"second/request -T<template-file> must be set to "
-			"use -I<ip-offset>\n");
+    check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
+          "second/request -T<template-file> must be set to "
+          "use -I<ip-offset>\n");
 
 }
 
@@ -666,7 +711,7 @@ CommandOptions::printCommandLine() const {
         } else {
             std::cout << "SOLICIT-ADVERETISE only" << std::endl;
         }
-    } 
+    }
     if (rate_ != 0) {
         std::cout << "rate[1/s]=" << rate_ <<  std::endl;
     }
@@ -675,7 +720,7 @@ CommandOptions::printCommandLine() const {
     }
     if (clients_num_ != 0) {
         std::cout << "clients=" << clients_num_ << std::endl;
-    } 
+    }
     for (int i = 0; i < base_.size(); ++i) {
         std::cout << "base[" << i << "]=" << base_[i] <<  std::endl;
     }
@@ -751,127 +796,126 @@ CommandOptions::printCommandLine() const {
 
 void
 CommandOptions::usage() const {
-	fprintf(stdout, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-"    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-"    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-"    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-"    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-"    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-"    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact.  For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-"    control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
-"    clients.  This can be specified multiple times, each instance is\n"
-"    in the <type>=<value> form, for instance:\n"
-"    (and default) mac=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-"    having been lost.  The value is given in seconds and may contain a\n"
-"    fractional component.  The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-"    elapsed-time option in the (second/request) template.\n"
-"    The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-"    whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-"    option / (DHCPv6) IA_NA option in the (second/request) template.\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\n"
-"    normally be routed to the server is used.\n"
-"    For DHCPv6 operation, specify the name of the network interface\n"
-"    via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-"    (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-"    exchanges per second.  A periodic report is generated showing the\n"
-"    number of exchanges which were not completed, as well as the\n"
-"    average response latency.  The program continues until\n"
-"    interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-"    (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-"    (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-"    as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-"    the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-"    <diagnostic-selector> is a string of single-keywords specifying\n"
-"    the operations for which verbose output is desired.  The selector\n"
-"    keyletters are:\n"
-"   * 'a': print the decoded command line arguments\n"
-"   * 'e': print the exit reason\n"
-"   * 'i': print rate processing details\n"
-"   * 'r': print randomization details\n"
-"   * 's': print first server-id\n"
-"   * 't': when finished, print timers of all successful exchanges\n"
-"   * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-"    been dropped.  Use -D0 to abort if even a single request has been\n"
-"    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
-"    maximum percentage of requests that may be dropped before abort.\n"
-"    In this case, testing of the threshold begins after 10 requests\n"
-"    have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions.  No report is\n"
-"    generated until all transactions have been initiated/waited-for,\n"
-"    after which a report is generated and the program terminates.\n"
-"-p<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"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-"   (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-"  exchanges are not successfully completed.\n");
+    std::cout <<
+        "perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
+        "    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
+        "    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
+        "    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
+        "    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
+        "    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
+        "    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
+        "\n"
+        "The [server] argument is the name/address of the DHCP server to\n"
+        "contact.  For DHCPv4 operation, exchanges are initiated by\n"
+        "transmitting a DHCP DISCOVER to this address.\n"
+        "\n"
+        "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
+        "SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
+        "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
+        "multicast address FF02::1:2), or the special name 'servers' to refer\n"
+        "to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
+        "argument is optional only in the case that -l is used to specify an\n"
+        "interface, in which case [server] defaults to 'all'.\n"
+        "\n"
+        "The default is to perform a single 4-way exchange, effectively pinging\n"
+        "the server.\n"
+        "The -r option is used to set up a performance test, without\n"
+        "it exchanges are initiated as fast as possible.\n"
+        "\n"
+        "Options:\n"
+        "-1: Take the server-ID option from the first received message.\n"
+        "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
+        "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
+        "-a<aggressivity>: When the target sending rate is not yet reached,\n"
+        "    control how many exchanges are initiated before the next pause.\n"
+        "-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
+        "    clients.  This can be specified multiple times, each instance is\n"
+        "    in the <type>=<value> form, for instance:\n"
+        "    (and default) mac=00:0c:01:02:03:04.\n"
+        "-d<drop-time>: Specify the time after which a requeqst is treated as\n"
+        "    having been lost.  The value is given in seconds and may contain a\n"
+        "    fractional component.  The default is 1 second.\n"
+        "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
+        "    elapsed-time option in the (second/request) template.\n"
+        "    The value 0 disables it.\n"
+        "-h: Print this help.\n"
+        "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
+        "    whether -6 is given.\n"
+        "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
+        "    option / (DHCPv6) IA_NA option in the (second/request) template.\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\n"
+        "    normally be routed to the server is used.\n"
+        "    For DHCPv6 operation, specify the name of the network interface\n"
+        "    via which exchanges are initiated.\n"
+        "-L<local-port>: Specify the local port to use\n"
+        "    (the value 0 means to use the default).\n"
+        "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
+        "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
+        "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
+        "    exchanges per second.  A periodic report is generated showing the\n"
+        "    number of exchanges which were not completed, as well as the\n"
+        "    average response latency.  The program continues until\n"
+        "    interrupted, at which point a final report is generated.\n"
+        "-R<range>: Specify how many different clients are used. With 1\n"
+        "    (the default), all requests seem to come from the same client.\n"
+        "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
+        "-S<srvid-offset>: Offset of the server-ID option in the\n"
+        "    (second/request) template.\n"
+        "-T<template-file>: The name of a file containing the template to use\n"
+        "    as a stream of hexadecimal digits.\n"
+        "-v: Report the version number of this program.\n"
+        "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
+        "    the program.\n"
+        "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
+        "    <diagnostic-selector> is a string of single-keywords specifying\n"
+        "    the operations for which verbose output is desired.  The selector\n"
+        "    keyletters are:\n"
+        "   * 'a': print the decoded command line arguments\n"
+        "   * 'e': print the exit reason\n"
+        "   * 'i': print rate processing details\n"
+        "   * 's': print first server-id\n"
+        "   * 't': when finished, print timers of all successful exchanges\n"
+        "   * 'T': when finished, print templates\n"
+        "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
+        "\n"
+        "DHCPv4 only options:\n"
+        "-B: Force broadcast handling.\n"
+        "\n"
+        "DHCPv6 only options:\n"
+        "-c: Add a rapid commit option (exchanges will be SA).\n"
+        "\n"
+        "The remaining options are used only in conjunction with -r:\n"
+        "\n"
+        "-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
+        "    been dropped.  Use -D0 to abort if even a single request has been\n"
+        "    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
+        "    maximum percentage of requests that may be dropped before abort.\n"
+        "    In this case, testing of the threshold begins after 10 requests\n"
+        "    have been expected to be received.\n"
+        "-n<num-request>: Initiate <num-request> transactions.  No report is\n"
+        "    generated until all transactions have been initiated/waited-for,\n"
+        "    after which a report is generated and the program terminates.\n"
+        "-p<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"
+        "-t<report>: Delay in seconds between two periodic reports.\n"
+        "\n"
+        "Errors:\n"
+        "- tooshort: received a too short message\n"
+        "- orphans: received a message which doesn't match an exchange\n"
+        "   (duplicate, late or not related)\n"
+        "- locallimit: reached to local system limits when sending a message.\n"
+        "\n"
+        "Exit status:\n"
+        "The exit status is:\n"
+        "0 on complete success.\n"
+        "1 for a general error.\n"
+        "2 if an error is found in the command line arguments.\n"
+        "3 if there are no general failures in operation, but one or more\n"
+        "  exchanges are not successfully completed.\n";
 }
 
 void
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index b9e2b9e..4668f73 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -57,7 +57,8 @@ public:
     /// \param argc Argument count passed to main().
     /// \param argv Argument value array passed to main().
     /// \throws isc::InvalidParameter if parse fails.
-    void parse(int argc, char** const argv);
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool parse(int argc, char** const argv);
 
     /// \brief Returns IP version.
     ///
@@ -230,7 +231,7 @@ public:
     ///
     /// \return server name.
     std::string getServerName() const { return server_name_; }
-    
+
     /// \brief Print command line arguments.
     void printCommandLine() const;
 
@@ -261,7 +262,8 @@ private:
     /// \param argc Argument count passed to main().
     /// \param argv Argument value array passed to main().
     /// \throws isc::InvalidParameter if command line options initialization fails.
-    void initialize(int argc, char** argv);
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool initialize(int argc, char** argv);
 
     /// \brief Validates initialized options.
     ///
@@ -349,7 +351,7 @@ private:
     /// \param base Base string given as -b duid=0F1234.
     /// \throws isc::InvalidParameter if DUID is invalid.
     void decodeDuid(const std::string& base);
-    
+
     /// \brief Generates DUID-LLT (based on link layer address).
     ///
     /// Function generates DUID based on link layer address and
@@ -364,65 +366,97 @@ private:
     /// \throw isc::InvalidParameter if string does not represent hex byte.
     uint8_t convertHexString(const std::string& hex_text) const;
 
-    uint8_t ipversion_;                      ///< IP protocol version to be used, expected values are:
-                                             ///< 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
-    ExchangeMode exchange_mode_;             ///< Packet exchange mode (e.g. DORA/SARR)
-    int rate_;                               ///< Rate in exchange per second
-    int report_delay_;                       ///< Delay between generation of two consecutive
-                                             ///< performance reports
-    uint32_t clients_num_;                   ///< Number of simulated clients (aka randomization range).
-    std::vector<uint8_t> mac_template_;      ///< MAC address template used to generate unique DUIDs
-                                             ///< for simulated clients.
-    std::vector<uint8_t> duid_template_;     ///< DUID template used to generate unique DUIDs for
-                                             ///< simulated clients
-    std::vector<std::string> base_;          ///< Collection of base values specified with -b<value>
-                                             ///< options. Supported "bases" are mac=<mac> and duid=<duid>
-    std::vector<int> num_request_;           ///< Number of 2 or 4-way exchanges to perform
-    int period_;                             ///< Test period in seconds
-    uint8_t drop_time_set_;                  ///< Indicates number of -d<value> parameters specified by user.
-                                             ///< If this value goes above 2, command line parsing fails.
-    std::vector<double> drop_time_;          ///< Time to elapse before request is lost. The fisrt value of
-                                             ///< two-element vector refers to DO/SA exchanges,
-                                             ///< second value refers to RA/RR. Default values are { 1, 1 }
-    std::vector<int> max_drop_;              ///< Maximum number of drops request before aborting test.
-                                             ///< First value of two-element vector specifies maximum
-                                             ///< number of drops for DO/SA exchange, second value
-                                             ///< specifies maximum number of drops for RA/RR.
-    std::vector<double> max_pdrop_;          ///< Maximal percentage of lost requests before aborting test.
-                                             ///< First value of two-element vector specifies percentage for
-                                             ///< DO/SA exchanges, second value for RA/RR.
-    std::string localname_;                  ///< Local address or interface specified with -l<value> option.
-    bool is_interface_;                      ///< Indicates that specified value with -l<value> is
-                                             ///< rather interface (not address)
-    int preload_;                            ///< Number of preload packets. Preload packets are used to
-                                             ///< initiate communication with server before doing performance
-                                             ///< measurements.
-    int aggressivity_;                       ///< Number of exchanges sent before next pause.
-    int local_port_;                         ///< Local port number (host endian)
-    bool seeded_;                            ///< Indicates that randomization seed was provided.
-    uint32_t seed_;                          ///< Randomization seed.
-    bool broadcast_;                         ///< Indicates that we use broadcast address.
-    bool rapid_commit_;                      ///< Indicates that we do rapid commit option.
-    bool use_first_;                         ///< Indicates that we take server id from first received packet.
-    std::vector<std::string> template_file_; ///< Packet template file names. These files store template packets
-                                             ///< that are used for initiating echanges. Template packets
-                                             ///< read from files are later tuned with variable data.
-    std::vector<int> xid_offset_;            ///< Offset of transaction id in template files. First vector
-                                             ///< element points to offset for DISCOVER/SOLICIT messages,
-                                             ///< second element points to trasaction id offset for
-                                             ///< REQUEST messages
-    std::vector<int> rnd_offset_;            ///< Random value offset in templates. Random value offset
-                                             ///< points to last octet of DUID. Up to 4 last octets of
-                                             ///< DUID are randomized to simulate differnt clients.
-    int elp_offset_;                         ///< Offset of elapsed time option in template packet.
-    int sid_offset_;                         ///< Offset of server id option in template packet.
-    int rip_offset_;                         ///< Offset of requested ip data in template packet/
-    std::string diags_;                      ///< String representing diagnostic selectors specified
-                                             ///< by user with -x<value>.
-    std::string wrapped_;                    ///< Wrapped command specified as -w<value>. Expected
-                                             ///< values are start and stop.
-    std::string server_name_;                ///< Server name specified as last argument of command line.
-    std::string commandline_;                ///< Entire command line as typed in by the user.
+    /// IP protocol version to be used, expected values are:
+    /// 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
+    uint8_t ipversion_;
+    /// Packet exchange mode (e.g. DORA/SARR)
+    ExchangeMode exchange_mode_;
+    /// Rate in exchange per second
+    int rate_;
+    /// Delay between generation of two consecutive
+    /// performance reports
+    int report_delay_;
+    /// Number of simulated clients (aka randomization range).
+    uint32_t clients_num_;
+    /// MAC address template used to generate unique MAC
+    /// addresses for simulated clients.
+    std::vector<uint8_t> mac_template_;
+    /// DUID template used to generate unique DUIDs for
+    /// simulated clients
+    std::vector<uint8_t> duid_template_;
+    /// Collection of base values specified with -b<value>
+    /// options. Supported "bases" are mac=<mac> and duid=<duid>
+    std::vector<std::string> base_;
+    /// Number of 2 or 4-way exchanges to perform.
+    std::vector<int> num_request_;
+    /// Test period in seconds
+    int period_;
+    /// Indicates number of -d<value> parameters specified by user.
+    /// If this value goes above 2, command line parsing fails.
+    uint8_t drop_time_set_;
+    /// Time to elapse before request is lost. The fisrt value of
+    /// two-element vector refers to DO/SA exchanges,
+    /// second value refers to RA/RR. Default values are { 1, 1 }
+    std::vector<double> drop_time_;
+    /// Maximum number of drops request before aborting test.
+    /// First value of two-element vector specifies maximum
+    /// number of drops for DO/SA exchange, second value
+    /// specifies maximum number of drops for RA/RR.
+    std::vector<int> max_drop_;
+    /// Maximal percentage of lost requests before aborting test.
+    /// First value of two-element vector specifies percentage for
+    /// DO/SA exchanges, second value for RA/RR.
+    std::vector<double> max_pdrop_;
+    /// Local address or interface specified with -l<value> option.
+    std::string localname_;
+    /// Indicates that specified value with -l<value> is
+    /// rather interface (not address)
+    bool is_interface_;
+    /// Number of preload packets. Preload packets are used to
+    /// initiate communication with server before doing performance
+    /// measurements.
+    int preload_;
+    /// Number of exchanges sent before next pause.
+    int aggressivity_;
+    /// Local port number (host endian)
+    int local_port_;
+    /// Randomization seed.
+    uint32_t seed_;
+    /// Indicates that randomization seed was provided.
+    bool seeded_;
+    /// Indicates that we use broadcast address.
+    bool broadcast_;
+    /// Indicates that we do rapid commit option.
+    bool rapid_commit_;
+    /// Indicates that we take server id from first received packet.
+    bool use_first_;
+    /// Packet template file names. These files store template packets
+    /// that are used for initiating echanges. Template packets
+    /// read from files are later tuned with variable data.
+    std::vector<std::string> template_file_;
+    /// Offset of transaction id in template files. First vector
+    /// element points to offset for DISCOVER/SOLICIT messages,
+    /// second element points to trasaction id offset for
+    /// REQUEST messages
+    std::vector<int> xid_offset_;
+    /// Random value offset in templates. Random value offset
+    /// points to last octet of DUID. Up to 4 last octets of
+    /// DUID are randomized to simulate differnt clients.
+    std::vector<int> rnd_offset_;
+    /// Offset of elapsed time option in template packet.
+    int elp_offset_;
+    /// Offset of server id option in template packet.
+    int sid_offset_;
+    /// Offset of requested ip data in template packet
+    int rip_offset_;
+    /// String representing diagnostic selectors specified
+    /// by user with -x<value>.
+    std::string diags_;
+    /// Command to be executed at the beginning/end of the test.
+    /// This command is expected to expose start and stop argument.
+    std::string wrapped_;
+    /// Server name specified as last argument of command line.
+    std::string server_name_;
 };
 
 } // namespace perfdhcp
diff --git a/tests/tools/perfdhcp/main.cc b/tests/tools/perfdhcp/main.cc
index 0c706a2..aa202f1 100644
--- a/tests/tools/perfdhcp/main.cc
+++ b/tests/tools/perfdhcp/main.cc
@@ -26,25 +26,35 @@ using namespace isc::perfdhcp;
 int
 main(int argc, char* argv[]) {
     CommandOptions& command_options = CommandOptions::instance();
+    std::string diags(command_options.getDiags());
+    int ret_code = 0;
     try {
-        command_options.parse(argc, argv);
+        // If parser returns true it means that user specified
+        // 'h' or 'v' command line option. Program shows the
+        // help or version message and exits here.
+        if (command_options.parse(argc, argv)) {
+            return (ret_code);
+        }
     } catch(isc::Exception& e) {
-        std::cout << "Error parsing command line options: "
+        ret_code = 1;
+        std::cerr << "Error parsing command line options: "
                   << e.what() << std::endl;
         command_options.usage();
-        return(1);
+        if (diags.find('e') != std::string::npos) {
+            std::cerr << "Fatal error" << std::endl;
+        }
+        return (ret_code);
     }
     try{
         TestControl& test_control = TestControl::instance();
-        test_control.run();
+        ret_code =  test_control.run();
     } catch (isc::Exception& e) {
-        std::cout << "Error running perfdhcp: " << e.what() << std::endl;
-        std::string diags(command_options.getDiags());
+        ret_code = 1;
+        std::cerr << "Error running perfdhcp: " << e.what() << std::endl;
         if (diags.find('e') != std::string::npos) {
-            std::cout << "Fatal error" << std::endl;
+            std::cerr << "Fatal error" << std::endl;
         }
-        return(1);
     }
-    return(0);
+    return (ret_code);
 }
 
diff --git a/tests/tools/perfdhcp/perfdhcp.c b/tests/tools/perfdhcp/perfdhcp.c
deleted file mode 100644
index 3ab9a2e..0000000
--- a/tests/tools/perfdhcp/perfdhcp.c
+++ /dev/null
@@ -1,3559 +0,0 @@
-/*
- * Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
- * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <config.h>
-
-#ifndef HAVE_GETIFADDRS
-
-/*
- * Solaris 10 does not have the getifaddrs() function available (although it
- * is present on Solaris 11 and later).  For the present we will not implement
- * a replacement (as we do with clock_gettime) as the implementation is
- * relatively complex.  Just output a message saying that the utility is not
- * supported on this operating system.
- */
-
-#include <stdio.h>
-
-int
-main(const int argc, char* const argv[])
-{
-    fprintf(stderr, "perfdhcp is not supported on this version of the operating system\n");
-    return (1);
-}
-
-#else
-
-/* getifaddrs() present, so the code should compile */
-
-#ifdef __linux__
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-
-#include <net/if.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <ifaddrs.h>
-#include <math.h>
-#include <netdb.h>
-#include <signal.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifndef HAVE_PSELECT
-
-#include <assert.h>
-
-/* Platforms such as OpenBSD don't provide a pselect(), so we use our
-   own implementation for this testcase, which wraps around select() and
-   hence doesn't implement the high precision timer. This implementation
-   is fine for our purpose. */
-
-static int
-pselect (int nfds, fd_set *readfds, fd_set *writefds,
-         fd_set *exceptfds, const struct timespec *timeout,
-         const sigset_t *sigmask)
-{
-	struct timeval my_timeout;
-
-	/* Our particular usage of pselect() doesn't use these fields. */
-	assert(writefds == NULL);
-	assert(exceptfds == NULL);
-	assert(sigmask == NULL);
-
-	my_timeout.tv_sec = timeout->tv_sec;
-	my_timeout.tv_usec = timeout->tv_nsec / 1000;
-
-	return (select(nfds, readfds, writefds, exceptfds, &my_timeout));
-}
-
-#endif /* !HAVE_PSELECT */
-
-/* DHCPv4 defines (to be moved/shared) */
-
-#define DHCP_OFF_OPCODE		0
-#define DHCP_OFF_HTYPE		1
-#define DHCP_OFF_HLEN		2
-#define DHCP_OFF_HOPS		3
-#define DHCP_OFF_XID		4
-#define DHCP_OFF_SECS		8
-#define DHCP_OFF_FLAGS		10
-#define DHCP_OFF_CIADDR		12
-#define DHCP_OFF_YIADDR		16
-#define DHCP_OFF_SADDR		20
-#define DHCP_OFF_GIADDR		24
-#define DHCP_OFF_CHADDR		28
-#define DHCP_OFF_SNAME		44
-#define DHCP_OFF_FILE		108
-#define DHCP_OFF_COOKIE		236
-#define DHCP_OFF_OPTIONS	240
-
-#define BOOTP_OP_REQUEST	1
-#define BOOTP_OP_REPLY		2
-#define BOOTP_MIN_LEN		300
-
-#define DHCP_OP_DISCOVER	1
-#define DHCP_OP_OFFER		2
-#define DHCP_OP_REQUEST		3
-#define DHCP_OP_DECLINE		4
-#define DHCP_OP_ACK		5
-#define DHCP_OP_NAK		6
-#define DHCP_OP_RELEASE		7
-#define DHCP_OP_INFORM		8
-
-#define DHCP_HTYPE_ETHER	1
-
-#define DHCP_OPT_PAD		0
-#define DHCP_OPT_SUBNET_MASK	1
-#define DHCP_OPT_TIME_OFFSET	2
-#define DHCP_OPT_ROUTERS	3
-#define DHCP_OPT_DNS_SERVERS	6
-#define DHCP_OPT_HOST_NAME	12
-#define DHCP_OPT_DOMAIN_NAME	15
-#define DHCP_OPT_BROADCAST	28
-#define DHCP_OPT_DHCP_ADDRESS	50
-#define DHCP_OPT_DHCP_LEASE	51
-#define DHCP_OPT_DHCP_MSGTYPE	53
-#define DHCP_OPT_DHCP_SRVID	54
-#define DHCP_OPT_DHCP_PRL	55
-#define DHCP_OPT_END		255
-
-#define DHCP_OPTLEN_SRVID	6
-
-/* DHCPv6 defines  (to be moved/shared) */
-
-#define DHCP6_OFF_MSGTYP	0
-#define DHCP6_OFF_XID		1
-#define DHCP6_OFF_OPTIONS	4
-
-#define DHCP6_OP_SOLICIT	1
-#define DHCP6_OP_ADVERTISE	2
-#define DHCP6_OP_REQUEST	3
-#define DHCP6_OP_REPLY		7
-
-#define DHCP6_OPT_CLIENTID	1
-#define DHCP6_OPT_SERVERID	2
-#define DHCP6_OPT_IA_NA		3
-#define DHCP6_OPT_ORO		6
-#define DHCP6_OPT_ELAPSED_TIME	8
-#define DHCP6_OPT_STATUS_CODE	13
-#define DHCP6_OPT_RAPID_COMMIT	14
-#define DHCP6_OPT_NAME_SERVERS	23
-#define DHCP6_OPT_DOMAIN_SEARCH	24
-
-#define DHCP6_ST_SUCCESS	0
-#define DHCP6_ST_NOADDRSAVAIL	2
-
-#define DHCP6_DUID_LLT		1
-#define DHCP6_DUID_EPOCH	946684800
-
-/* tail queue macros (from FreeBSD 8.2 /sys/sys/queue.h, to be moved/shared) */
-
-#define ISC_TAILQ_HEAD(name, type)					\
-struct name {								\
-	struct type *tqh_first;						\
-	struct type **tqh_last;						\
-}
-
-#define ISC_TAILQ_ENTRY(type)						\
-struct {								\
-	struct type *tqe_next;						\
-	struct type **tqe_prev;						\
-}
-
-#define ISC_TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
-
-#define ISC_TAILQ_FIRST(head)	((head)->tqh_first)
-
-#define ISC_TAILQ_LAST(head, headname)					\
-	(*(((struct headname *)((head)->tqh_last))->tqh_last))
-
-#define ISC_TAILQ_NEXT(elm, field)	((elm)->field.tqe_next)
-
-#define ISC_TAILQ_PREV(elm, headname, field)				\
-	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
-
-#define ISC_TAILQ_INIT(head)	do {					\
-	ISC_TAILQ_FIRST((head)) = NULL;					\
-	(head)->tqh_last = &ISC_TAILQ_FIRST((head));			\
-} while (0)
-
-#define ISC_TAILQ_INSERT_HEAD(head, elm, field)	do {			\
-	ISC_TAILQ_NEXT((elm), field) = ISC_TAILQ_FIRST((head));		\
-	if (!ISC_TAILQ_EMPTY((head)))					\
-		ISC_TAILQ_FIRST((head))->field.tqe_prev =		\
-			&ISC_TAILQ_NEXT((elm), field);			\
-	else								\
-		(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);	\
-	ISC_TAILQ_FIRST((head)) = (elm);				\
-	(elm)->field.tqe_prev = &ISC_TAILQ_FIRST((head));		\
-} while (0)
-
-#define ISC_TAILQ_INSERT_TAIL(head, elm, field)	do {			\
-	ISC_TAILQ_NEXT((elm), field) = NULL;				\
-	(elm)->field.tqe_prev = (head)->tqh_last;			\
-	*(head)->tqh_last = (elm);					\
-	(head)->tqh_last = &ISC_TAILQ_NEXT((elm), field);		\
-} while (0)
-
-#define ISC_TAILQ_REMOVE(head, elm, field)	do {			\
-	if ((ISC_TAILQ_NEXT((elm), field)) != NULL)			\
-		ISC_TAILQ_NEXT((elm), field)->field.tqe_prev =		\
-			(elm)->field.tqe_prev;				\
-	else								\
-		(head)->tqh_last = (elm)->field.tqe_prev;		\
-	*(elm)->field.tqe_prev = ISC_TAILQ_NEXT((elm), field);		\
-} while (0)
-
-#define ISC_TAILQ_FOREACH(var, head, field)				\
-	for ((var) = ISC_TAILQ_FIRST((head));				\
-	     (var);							\
-	     (var) = ISC_TAILQ_NEXT((var), field))
-
-#define ISC_TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
-	for ((var) = ISC_TAILQ_FIRST((head));				\
-	     (var) && ((tvar) = ISC_TAILQ_NEXT((var), field), 1);	\
-	     (var) = (tvar))
-
-/*
- * Data structures
- */
-
-/*
- * exchange:
- *    - per exchange values:
- *	* order (for debugging)
- *	* xid (even/odd for 4 packet exchanges)
- *	* random (for debugging)
- *	* time-stamps
- *	* server ID (for 3rd packet)
- *	* IA_NA (for IPv6 3rd packet)
- *
- * sent/rcvd global chains, "next to be received" on entry cache,
- * and hash table for xid -> data structure fast matching
- * (using the assumption collisions are unlikely, cf birthday problem)
- */
-
-struct exchange {				/* per exchange structure */
-	ISC_TAILQ_ENTRY(exchange) gchain;	/* global chaining */
-	ISC_TAILQ_ENTRY(exchange) hchain;	/* hash table chaining */
-	uint64_t order0, order2;		/* number of this exchange */
-	uint32_t xid;				/* transaction ID */
-	uint32_t rnd;				/* random part */
-	struct timespec ts0, ts1, ts2, ts3;	/* timespecs */
-	uint8_t *sid;				/* server ID */
-	size_t sidlen;				/* server ID length */
-	uint8_t *iana;				/* (IPv6) IA_NA */
-	size_t ianalen;				/* (IPv6) IA_NA length */
-};
-struct exchange *xnext0, *xnext2;		/* next to be received */
-ISC_TAILQ_HEAD(xlist, exchange);		/* exchange list */
-struct xlist xsent0, xsent2, xrcvd0, xrcvd2;	/* sent and received lists */
-uint64_t xscount0, xscount2;			/* sent counters */
-uint64_t xrcount0, xrcount2;			/* received counters */
-caddr_t exchanges0, exchanges2;			/* hash tables */
-uint32_t hashsize0, hashsize2;			/* hash table sizes */
-
-/*
- * statictics counters and accumulators
- */
-
-uint64_t tooshort, orphans, locallimit;		/* error counters */
-uint64_t latesent, compsend, latercvd;		/* rate stats */
-uint64_t multrcvd, shortwait, collected[2];	/* rate stats (cont) */
-double dmin0 = 999999999., dmin2 = 999999999.;	/* minimum delays */
-double dmax0 = 0., dmax2 = 0.;			/* maximum delays */
-double dsum0 = 0., dsum2 = 0.;			/* delay sums */
-double dsumsq0 = 0., dsumsq2 = 0.;		/* square delay sums */
-
-/*
- * command line parameters
- */
-
-int ipversion = 0;			/* IP version */
-int simple;				/* DO/SA in place of DORR/SARR */
-int rate;				/* rate in exchange per second */
-int report;				/* delay between two reports */
-uint32_t range;				/* randomization range */
-uint32_t maxrandom;			/* maximum random value */
-int basecnt;				/* base count */
-char *base[4];				/* bases */
-int gotnumreq;				/* numreq[0] was set */
-int numreq[2];				/* number of exchange */
-int period;				/* test period */
-int gotlosttime;			/* losttime[0] was set */
-double losttime[2] = {1., 1.};		/* time after a request is lost  */
-int gotmaxdrop;				/* max{p}drop[0] was set */
-int maxdrop[2];				/* maximum number of lost requests */
-double maxpdrop[2] = { 0., 0.};		/* maximum percentage */
-char *localname;			/* local address or interface */
-int isinterface;			/* interface vs local address */
-int preload;				/* preload exchanges */
-int aggressivity = 1;			/* back to back exchanges */
-int localport;				/* local port number (host endian) */
-int seeded;				/* is a seed provided */
-unsigned int seed;			/* randomization seed */
-int isbroadcast;			/* use broadcast */
-int rapidcommit;			/* add rapid commit option */
-int usefirst;				/* where to take the server-ID */
-char *templatefile[2];			/* template file name */
-int xidoffset[2] = {-1, -1};		/* template offsets (xid)*/
-int rndoffset[2] = {-1, -1};		/* template offsets (random) */
-int elpoffset = -1;			/* template offset (elapsed time) */
-int sidoffset = -1;			/* template offset (server ID) */
-int ripoffset = -1;			/* template offset (requested IP) */
-char *diags;				/* diagnostic selectors */
-char *wrapped;				/* wrapped command */
-char *servername;			/* server */
-
-/*
- * global variables
- */
-
-struct sockaddr_storage localaddr;	/* local socket address */
-struct sockaddr_storage serveraddr;	/* server socket address */
-
-int sock;				/* socket descriptor */
-int interrupted, fatal;			/* to finish flags */
-
-uint8_t obuf[4096], ibuf[4096];		/* I/O buffers */
-char tbuf[8200];			/* template buffer */
-
-struct timespec boot;			/* the date of boot */
-struct timespec last;			/* the date of last send */
-struct timespec due;			/* the date of next send */
-struct timespec dreport;		/* the date of next reporting */
-struct timespec finished;		/* the date of finish */
-
-uint8_t *gsrvid;			/* global server id */
-size_t gsrvidlen;			/*  and its length */
-uint8_t gsrvidbuf[64];			/*  and its storage */
-
-/* MAC address */
-uint8_t mac_prefix[6] = { 0x00, 0x0c, 0x01, 0x02, 0x03, 0x04 };
-
-/* DUID prefix */
-uint8_t *duid_prefix;
-int duid_length;
-
-/* magic cookie for BOOTP/DHCPv4 */
-uint8_t dhcp_cookie[4] = { 0x63, 0x82, 0x53, 0x63 };
-
-/*
- * templates
- *
- * note: the only hard point is what are the offsets:
- *   - xid_discover4 and xid_request4: first of the 4 octet long
- *     transaction ID (default DHCP_OFF_XID = 4)
- *   - random_discover4 and random_request4: last of the 6 octet long
- *     MAC address (default DHCP_OFF_CHADDR + 6 = 28 + 6)
- *   - elapsed_request4: first of the 2 octet long secs field
- *     (default DHCP_OFF_SECS = 8, 0 means disabled)
- *   - serverid_request4: first of the 6 octet long server ID option
- *     (no default, required)
- *   - reqaddr_request4: first of the 4 octet long requested IP address
- *     option content (i.e., the address itself, btw OFFER yiaddr)
- *     (no default, required)
- *   - xid_solicit6 and xid_request6: first of the 3 octet long
- *     transaction ID (default DHCP6_OFF_XID = 1)
- *   - random_solicit6 and random_request6: last of the DUID in the
- *     client ID option (no default, required when rate is set)
- *   - elapsed_request6: first of the 2 octet long content of
- *     the option elapsed time option (no default, 0 means disabled)
- *   - serverid_request6: position where the variable length server ID
- *     option is inserted (no default, required, set to length means append)
- *   - reqaddr_request6: position where of the variable length requested
- *     IP address option is inserted (no default, required, set to
- *     length means append)
- */
-
-size_t length_discover4;
-uint8_t template_discover4[4096];
-size_t xid_discover4;
-size_t random_discover4;
-size_t length_request4;
-uint8_t template_request4[4096];
-size_t xid_request4;
-size_t elapsed_request4;
-size_t random_request4;
-size_t serverid_request4;
-size_t reqaddr_request4;
-size_t length_solicit6;
-uint8_t template_solicit6[4096];
-size_t xid_solicit6;
-size_t random_solicit6;
-size_t length_request6;
-uint8_t template_request6[4096];
-size_t xid_request6;
-size_t elapsed_request6;
-size_t random_request6;
-size_t serverid_request6;
-size_t reqaddr_request6;
-
-
-// use definition of CLOCK_REALTIME (or lack of thereof) as an indicator
-// if the code is being compiled or Linux (or somewhere else)
-// Perhaps this should be based on OS_LINUX define?
-
-#if !defined (CLOCK_REALTIME)
-#define CLOCK_REALTIME 0
-
-/// @brief clock_gettime implementation for non-Linux systems
-///
-/// This implementation lacks nanosecond resolution. It is intended
-/// to be used on non-Linux systems that does not provide clock_gettime
-/// implementation.
-///
-/// @param clockid ignored (kept for Linux prototype compatibility)
-/// @param tp timespec structure
-///
-/// @return always zero (kept for compatibility reasons)
-int clock_gettime(int clockid, struct timespec *tp) {
-
-    struct timeval tv;
-    gettimeofday(&tv, NULL);
-    tp->tv_sec = tv.tv_sec;
-    tp->tv_nsec = tv.tv_usec*1000;
-
-    return (0);
-}
-
-#endif
-
-/*
- * initialize data structures handling exchanges
- */
-
-void
-inits(void)
-{
-	struct xlist *bucket;
-	caddr_t p;
-	size_t len, i;
-
-	ISC_TAILQ_INIT(&xsent0);
-	ISC_TAILQ_INIT(&xsent2);
-	ISC_TAILQ_INIT(&xrcvd0);
-	ISC_TAILQ_INIT(&xrcvd2);
-
-	/// compute hashsizes
-	hashsize0 = 1024;
-	len = sizeof(*bucket) * hashsize0;
-	exchanges0 = malloc(len);
-	if (exchanges0 == NULL) {
-		perror("malloc(exchanges0)");
-		exit(1);
-	}
-	for (i = 0, p = exchanges0; i < hashsize0; i++, p += sizeof(*bucket)) {
-		bucket = (struct xlist *) p;
-		ISC_TAILQ_INIT(bucket);
-	}
-	if (simple != 0)
-		return;
-	hashsize2 = 1024;
-	len = sizeof(*bucket) * hashsize2;
-	exchanges2 = malloc(len);
-	if (exchanges2 == NULL) {
-		perror("malloc(exchanges2)");
-		exit(1);
-	}
-	for (i = 0, p = exchanges2; i < hashsize2; i++, p += sizeof(*bucket)) {
-		bucket = (struct xlist *) p;
-		ISC_TAILQ_INIT(bucket);
-	}
-}
-
-/*
- * randomize the value of the given field:
- *   - offset of the field
- *   - random seed (used as it when suitable)
- *   - returns the random value which was used
- */
-
-uint32_t
-randomize(size_t offset, uint32_t r)
-{
-	uint32_t v;
-
-	if (range == 0)
-		return 0;
-	if (range == UINT32_MAX)
-		return r;
-	if (maxrandom != 0)
-		while (r >= maxrandom)
-			r = (uint32_t) random();
-	r %= range + 1;
-	v = r;
-	v += obuf[offset];
-	obuf[offset] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 1];
-	obuf[offset - 1] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 2];
-	obuf[offset - 2] = v;
-	if (v < 256)
-		return r;
-	v >>= 8;
-	v += obuf[offset - 3];
-	obuf[offset - 3] = v;
-	return r;
-}
-
-/*
- * receive a reply (4th packet), shared between IPv4 and IPv6:
- *   - transaction ID xid
- *   - receiving time-stamp now
- * called from receive[46]() when the xid is odd
- */
-
-void
-receive_reply(uint32_t xid, struct timespec *now)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	uint32_t hash;
-	int checklost;
-	double delta;
-
-	/* bucket is needed even when the next cache matches */
-	hash = (xid >> 1) & (hashsize2 - 1);
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext2 != NULL) && (xnext2->xid == xid)) {
-		x = xnext2;
-		goto found;
-	}
-	/* usually the lost probability is low for request/reply */
-	checklost = 1;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		checklost = 0;
-		/* check for a timed-out exchange */
-		waited = now->tv_sec - x->ts2.tv_sec;
-		waited += (now->tv_nsec - x->ts2.tv_nsec) / 1e9;
-		if (waited < losttime[1])
-			continue;
-		/* garbage collect timed-out exchange */
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent2, x, gchain);
-		free(x);
-		collected[1] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount2++;
-	x->ts3 = *now;
-	delta = x->ts3.tv_sec - x->ts2.tv_sec;
-	delta += (x->ts3.tv_nsec - x->ts2.tv_nsec) / 1e9;
-	if (delta < dmin2)
-		dmin2 = delta;
-	if (delta > dmax2)
-		dmax2 = delta;
-	dsum2 += delta;
-	dsumsq2 += delta * delta;
-	xnext2 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent2, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd2, x, gchain);
-}
-
-/*
- * get the DHCPv4 socket descriptor
- * (the only complexity is broadcast enabling: there is no easy way to
- *  recognize broadcast addresses, so the command line -B flag)
- */
-
-void
-getsock4(void)
-{
-	int ret;
-
-	/* update local port */
-	if (localport != 0) {
-		uint16_t lp = htons((uint16_t) localport);
-
-		((struct sockaddr_in *) &localaddr)->sin_port = lp;
-	}
-	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-	if (sock < 0) {
-		perror("socket");
-		exit(1);
-	}
-	ret = bind(sock,
-		   (struct sockaddr *) &localaddr,
-		   sizeof(struct sockaddr_in));
-	if (ret < 0) {
-		perror("bind");
-		exit(1);
-	}
-	/* enable broadcast if needed or required */
-	if (isbroadcast != 0) {
-		int on = 1;
-
-		ret = setsockopt(sock,
-				 SOL_SOCKET, SO_BROADCAST,
-				 &on, sizeof(on));
-		if (ret < 0) {
-			perror("setsockopt(SO_BROADCAST)");
-			exit(1);
-		}
-	}
-}
-
-/*
- * build a DHCPv4 DISCOVER from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_discover4(void)
-{
-	uint8_t *p = template_discover4;
-
-	length_discover4 = BOOTP_MIN_LEN;
-	xid_discover4 = DHCP_OFF_XID;
-	random_discover4 = DHCP_OFF_CHADDR + 6;
-	/* opcode */
-	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
-	/* hardware address type */
-	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
-	/* hardware address length */
-	p[DHCP_OFF_HLEN] = 6;
-	/* hops */
-	p[DHCP_OFF_HOPS] = 1;
-	/* gateway address */
-	memcpy(p + DHCP_OFF_GIADDR,
-	       &((struct sockaddr_in *) &localaddr)->sin_addr,
-	       4);
-	/* hardware address */
-	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
-	/* cookie */
-	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
-	/* options */
-	p += DHCP_OFF_OPTIONS;
-	/* inline DHCP message type */
-	*p++ = DHCP_OPT_DHCP_MSGTYPE;
-	*p++ = 1;
-	*p++ = DHCP_OP_DISCOVER;
-	/* inline DHCP parameter request list (default) */
-	*p++ = DHCP_OPT_DHCP_PRL;
-	*p++ = 7;
-	*p++ = DHCP_OPT_SUBNET_MASK;
-	*p++ = DHCP_OPT_BROADCAST;
-	*p++ = DHCP_OPT_TIME_OFFSET;
-	*p++ = DHCP_OPT_ROUTERS;
-	*p++ = DHCP_OPT_DOMAIN_NAME;
-	*p++ = DHCP_OPT_DNS_SERVERS;
-	*p++ = DHCP_OPT_HOST_NAME;
-	/* end */
-	*p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay first packet (usually a DISCOVER) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_discover4(void)
-{
-	uint8_t *p = template_discover4;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[0], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(1);
-	}
-	if (cc < 100) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[0]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[0]);
-		exit(2);
-	}
-	length_discover4 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[0] >= 0)
-		xid_discover4 = (size_t) xidoffset[0];
-	else
-		xid_discover4 = DHCP_OFF_XID;
-	if (xid_discover4 + 4 > length_discover4) {
-		fprintf(stderr,
-			"xid (at %zu) outside the template (length %zu)?\n",
-			xid_discover4, length_discover4);
-		exit(2);
-	}
-	if (rndoffset[0] >= 0)
-		random_discover4 = (size_t) rndoffset[0];
-	else
-		random_discover4 = DHCP_OFF_CHADDR + 6;
-	if (random_discover4 > length_discover4) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_discover4, length_discover4);
-		exit(2);
-	}
-}
-
-/*
- * build a DHCPv4 REQUEST from a relay template
- * (implicit parameters are the local (giaddr) and MAC addresses (chaddr))
- * (assume the link is Ethernet)
- */
-
-void
-build_template_request4(void)
-{
-	uint8_t *p = template_request4;
-
-	length_request4 = BOOTP_MIN_LEN;
-	xid_request4 = DHCP_OFF_XID;
-	elapsed_request4 = DHCP_OFF_SECS;
-	random_request4 = DHCP_OFF_CHADDR + 6;
-	/* opcode */
-	p[DHCP_OFF_OPCODE] = BOOTP_OP_REQUEST;
-	/* hardware address type */
-	p[DHCP_OFF_HTYPE] = DHCP_HTYPE_ETHER;
-	/* hardware address length */
-	p[DHCP_OFF_HLEN] = 6;
-	/* hops */
-	p[DHCP_OFF_HOPS] = 1;
-	/* gateway address */
-	memcpy(p + DHCP_OFF_GIADDR,
-	       &((struct sockaddr_in *) &localaddr)->sin_addr,
-	       4);
-	/* hardware address */
-	memcpy(p + DHCP_OFF_CHADDR, mac_prefix, 6);
-	/* cookie */
-	memcpy(p + DHCP_OFF_COOKIE, dhcp_cookie, 4);
-	/* options */
-	p += DHCP_OFF_OPTIONS;
-	/* inline DHCP message type */
-	*p++ = DHCP_OPT_DHCP_MSGTYPE;
-	*p++ = 1;
-	*p++ = DHCP_OP_REQUEST;
-	/* place for DHCP server id (option) */
-	serverid_request4 = p - template_request4;
-	p += DHCP_OPTLEN_SRVID;
-	/* place for DHCP requested IP address (address) */
-	*p++ = DHCP_OPT_DHCP_ADDRESS;
-	*p++ = 4;
-	reqaddr_request4 = p - template_request4;
-	p += 4;
-	/* inline DHCP parameter request list (default) */
-	*p++ = DHCP_OPT_DHCP_PRL;
-	*p++ = 7;
-	*p++ = DHCP_OPT_SUBNET_MASK;
-	*p++ = DHCP_OPT_BROADCAST;
-	*p++ = DHCP_OPT_TIME_OFFSET;
-	*p++ = DHCP_OPT_ROUTERS;
-	*p++ = DHCP_OPT_DOMAIN_NAME;
-	*p++ = DHCP_OPT_DNS_SERVERS;
-	*p++ = DHCP_OPT_HOST_NAME;
-	/* end */
-	*p = DHCP_OPT_END;
-}
-
-/*
- * get a DHCPv4 client/relay third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request4(void)
-{
-	uint8_t *p = template_request4;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[1], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(1);
-	}
-	if (cc < 100) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[1]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[1]);
-		exit(2);
-	}
-	length_request4 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[1] >= 0)
-		xid_request4 = (size_t) xidoffset[1];
-	else
-		xid_request4 = DHCP_OFF_XID;
-	if (xid_request4 + 4 > length_request4) {
-		fprintf(stderr,
-			"xid (at %zu) outside the template (length %zu)?\n",
-			xid_request4, length_request4);
-		exit(2);
-	}
-	if (rndoffset[1] >= 0)
-		random_request4 = (size_t) rndoffset[1];
-	else
-		random_request4 = DHCP_OFF_CHADDR + 6;
-	if (random_request4 > length_request4) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_request4, length_request4);
-		exit(2);
-	}
-	if (elpoffset >= 0)
-		elapsed_request4 = (size_t) elpoffset;
-	else
-		elapsed_request4 = DHCP_OFF_SECS;
-	if (elapsed_request4 + 2 > length_request4) {
-		fprintf(stderr,
-			"secs (at %zu) outside the template (length %zu)?\n",
-			elapsed_request4, length_request4);
-		exit(2);
-	}
-	serverid_request4 = (size_t) sidoffset;
-	if (serverid_request4 + 6 > length_request4) {
-		fprintf(stderr,
-			"server-id option (at %zu) outside the template "
-			"(length %zu)?\n",
-			serverid_request4, length_request4);
-		exit(2);
-	}
-	reqaddr_request4 = (size_t) ripoffset;
-	if (reqaddr_request4 + 4 > length_request4) {
-		fprintf(stderr,
-			"requested-ip-address option (at %zu) outside "
-			"the template (length %zu)?\n",
-			reqaddr_request4, length_request4);
-		exit(2);
-	}
-}
-
-/*
- * send the DHCPv4 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the OFFER)
- */
-
-void
-send_request4(struct exchange *x0)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL) {
-		locallimit++;
-		perror("send2");
-		return;
-	}
-
-	memcpy(x, x0, sizeof(*x));
-	x->order2 = xscount2++;
-	x->xid |= 1;
-	hash = x->xid >> 1;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
-	hash &= hashsize2 - 1;
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_request4, length_request4);
-	/* xid */
-	memcpy(obuf + xid_request4, &x->xid, 4);
-	/* random */
-	randomize(random_request4, x->rnd);
-	/* secs */
-	if (elapsed_request4 > 0) {
-		int secs;
-
-		secs = x->ts1.tv_sec - x->ts0.tv_sec;
-		if (x->ts1.tv_nsec < x->ts0.tv_nsec)
-			secs += 1;
-		if (secs > 0) {
-			obuf[elapsed_request4] = secs >> 8;
-			obuf[elapsed_request4 + 1] = secs & 0xff;
-		}
-	}
-	/* server ID */
-	memcpy(obuf + serverid_request4, x->sid, x->sidlen);
-	/* requested IP address */
-	memcpy(obuf + reqaddr_request4, ibuf + DHCP_OFF_YIADDR, 4);
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
-	if (ret < 0) {
-		perror("clock_gettime(send2)");
-		fatal = 1;
-		return;
-	}
-	ret = sendto(sock, obuf, length_request4, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in));
-	if (ret >= 0)
-		return;
-	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
-	    (errno == ENOBUFS) || (errno == ENOMEM))
-		locallimit++;
-	perror("send2");
-}
-
-/*
- * send the DHCPv4 DISCOVER first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send4(void)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL)
-		return -ENOMEM;
-
-	memset(x, 0, sizeof(*x));
-	x->order0 = xscount0++;
-	hash = x->rnd = (uint32_t) random();
-	if (simple == 0)
-		x->xid = hash << 1;
-	else
-		x->xid = hash;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
-	hash &= hashsize0 - 1;
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_discover4, length_discover4);
-	/* xid */
-	memcpy(obuf + xid_discover4, &x->xid, 4);
-	/* random */
-	x->rnd = randomize(random_discover4, x->rnd);
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &last);
-	if (ret < 0) {
-		perror("clock_gettime(send)");
-		fatal = 1;
-		return -errno;
-	}
-	x->ts0 = last;
-	errno = 0;
-	ret = sendto(sock, obuf, length_discover4, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in));
-	if (ret == (ssize_t) length_discover4)
-		return 0;
-	return -errno;
-}	
-
-/*
- * scan a DHCPv4 OFFER to get its server-id option
- */
-
-int
-scan_for_srvid4(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP_OFF_OPTIONS;
-
-	for (;;) {
-		if (off + DHCP_OPTLEN_SRVID > cc) {
-			fprintf(stderr, "truncated\n");
-			return -1;
-		}
-		if (ibuf[off] == DHCP_OPT_DHCP_SRVID)
-			break;
-		if (ibuf[off] == DHCP_OPT_END) {
-			fprintf(stderr, "server-id not found\n");
-			return -1;
-		}
-		if (ibuf[off] == DHCP_OPT_PAD) {
-			off++;
-			continue;
-		}
-		off += 2 + ibuf[off + 1];
-	}
-	/* check length */
-	if (ibuf[off + 1] != DHCP_OPTLEN_SRVID - 2) {
-		fprintf(stderr,
-			"bad server-id length (%hhu)\n",
-			ibuf[off + 1]);
-		return -1;
-	}
-	/* cache it in the global variables when required and not yet done */
-	if ((usefirst != 0) && (gsrvid == NULL)) {
-		memcpy(gsrvidbuf, ibuf + off, DHCP_OPTLEN_SRVID);
-		gsrvid = gsrvidbuf;
-		gsrvidlen = DHCP_OPTLEN_SRVID;
-	}
-	x->sid = ibuf + off;
-	x->sidlen = DHCP_OPTLEN_SRVID;
-	return 0;
-}
-
-/*
- * receive a DHCPv4 packet
- */
-
-void
-receive4(void)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	struct timespec now;
-	ssize_t cc;
-	uint32_t xid, hash;
-	int checklost = 0;
-	double delta;
-
-	cc = recv(sock, ibuf, sizeof(ibuf), 0);
-	if (cc < 0) {
-		if ((errno == EAGAIN) ||
-		    (errno == EWOULDBLOCK) ||
-		    (errno == EINTR))
-			return;
-		perror("recv");
-		fatal = 1;
-		return;
-	}
-	/* enforce a reasonable length */
-	if (cc < BOOTP_MIN_LEN) {
-		tooshort++;
-		return;
-	}
-	/* must be a BOOTP REPLY */
-	if (ibuf[DHCP_OFF_OPCODE] != BOOTP_OP_REPLY)
-		return;
-	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-		perror("clock_gettime(receive)");
-		fatal = 1;
-		return;
-	}
-	memcpy(&xid, ibuf + xid_discover4, 4);
-	/* 4-packet exchange even/odd xid */
-	if (simple == 0) {
-		if ((xid & 1) != 0) {
-			receive_reply(xid, &now);
-			return;
-		}
-		hash = (xid >> 1) & (hashsize0 - 1);
-	} else
-		hash = xid & (hashsize0 - 1);
-	/* now it is the second packet, get the bucket which is needed */
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
-		x = xnext0;
-		goto found;
-	}
-	/* if the rate is not illimited, garbage collect up to 3
-	   timed-out exchanges */
-	if (rate != 0)
-		checklost = 3;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		/* check for a timed-out exchange */
-		waited = now.tv_sec - x->ts0.tv_sec;
-		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
-		if (waited < losttime[0]) {
-			checklost = 0;
-			continue;
-		}
-		/* garbage collect timed-out exchange */
-		checklost--;
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-		free(x);
-		collected[0] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount0++;
-	x->ts1 = now;
-	delta = x->ts1.tv_sec - x->ts0.tv_sec;
-	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
-	if (delta < dmin0)
-		dmin0 = delta;
-	if (delta > dmax0)
-		dmax0 = delta;
-	dsum0 += delta;
-	dsumsq0 += delta * delta;
-	xnext0 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
-	/* if the exchange is not finished, go to the second part */
-	if (simple == 0) {
-		int ret = 0;
-
-		/* the server-ID option is needed */
-		if ((usefirst != 0) && (gsrvid != NULL)) {
-			x->sid = gsrvid;
-			x->sidlen = gsrvidlen;
-		} else
-			ret = scan_for_srvid4(x, cc);
-		if (ret >= 0)
-			send_request4(x);
-	}
-}
-
-/*
- * get the DHCPv6 socket descriptor
- */
-
-void
-getsock6(void)
-{
-	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &serveraddr;
-	int ret;
-
-	/* update local port */
-	if (localport != 0) {
-		uint16_t lp = htons((uint16_t) localport);
-
-		((struct sockaddr_in6 *) &localaddr)->sin6_port = lp;
-	}
-	sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-	if (sock < 0) {
-		perror("socket");
-		exit(1);
-	}
-	ret = bind(sock,
-		   (struct sockaddr *) &localaddr,
-		   sizeof(struct sockaddr_in6));
-	if (ret < 0) {
-		perror("Failed to bind v6 socket to local-link address");
-		exit(1);
-	}
-	/* perform the multicast stuff when the destination is multicast */
-	if (IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
-		int hops = 1;
-
-		ret = setsockopt(sock,
-				 IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-				 &hops, sizeof(hops));
-		if (ret < 0) {
-			perror("setsockopt(IPV6_MULTICAST_HOPS)");
-			exit(1);
-		}
-	}
-	if (isinterface && IN6_IS_ADDR_MULTICAST(&s6->sin6_addr)) {
-		unsigned idx = if_nametoindex(localname);
-
-		if (idx == 0) {
-			fprintf(stderr,
-				"if_nametoindex(%s) failed\n",
-				localname);
-			exit(1);
-		}
-		ret = setsockopt(sock,
-				 IPPROTO_IPV6, IPV6_MULTICAST_IF,
-				 &idx, sizeof(idx));
-		if (ret < 0) {
-			perror("setsockopt(IPV6_MULTICAST_IF)");
-			exit(1);
-		}
-	}
-}
-
-/*
- * build a DHCPv6 SOLICIT template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_solicit6(void)
-{
-	uint8_t *p = template_solicit6;
-
-	xid_solicit6 = DHCP6_OFF_XID;
-	/* message type */
-	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_SOLICIT;
-	/* options */
-	p += DHCP6_OFF_OPTIONS;
-	/* elapsed time */
-	p[1] = DHCP6_OPT_ELAPSED_TIME;
-	p[3] = 2;
-	p += 6;
-	/* rapid commit */
-	if (rapidcommit != 0) {
-		p[1] = DHCP6_OPT_RAPID_COMMIT;
-		p += 4;
-	}
-	/* client ID */
-	p[1] = DHCP6_OPT_CLIENTID;
-	p[3] = duid_length;
-	memcpy(p + 4, duid_prefix, duid_length);
-	p += 4 + duid_length;
-	random_solicit6 = p - template_solicit6;
-	/* option request option */
-	p[1] = DHCP6_OPT_ORO;
-	p[3] = 4;
-	p[5] = DHCP6_OPT_NAME_SERVERS;
-	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
-	p += 8;
-	/* IA_NA (IAID = 1, T1 = 3600, T2 = 5400) */
-	p[1] = DHCP6_OPT_IA_NA;
-	p[3] = 12;
-	p[7] = 1;
-	p[10] = 3600 >> 8;
-	p[11] = 3600 & 0xff;
-	p[14] = 5400 >> 8;
-	p[15] = 5400 & 0xff;
-	p += 16;
-	/* set length */
-	length_solicit6 = p - template_solicit6;
-}
-
-/*
- * get a DHCPv6 first packet (usually a SOLICIT) template
- * from the file given in the command line (-T<template-file>)
- * and xid/rnd offsets (-X<xid-offset> and -O<random-offset>)
- */
-
-void
-get_template_solicit6(void)
-{
-	uint8_t *p = template_solicit6;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[0], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[0], strerror(errno));
-		exit(1);
-	}
-	if (cc < 10) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[0]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[0]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[0]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[0]);
-		exit(2);
-	}
-	length_solicit6 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[0] >= 0)
-		xid_solicit6 = (size_t) xidoffset[0];
-	else
-		xid_solicit6 = DHCP6_OFF_XID;
-	if (xid_solicit6 + 3 > length_solicit6) {
-		fprintf(stderr,
-			"xid (at %zu) is outside the template (length %zu)?\n",
-			xid_solicit6, length_solicit6);
-		exit(2);
-	}
-	if (rndoffset[0] >= 0)
-		random_solicit6 = (size_t) rndoffset[0];
-	else
-		random_solicit6 = 0;
-	if (random_solicit6 > length_solicit6) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_solicit6, length_solicit6);
-		exit(2);
-	}
-}
-
-/*
- * build a DHCPv6 REQUEST template
- * (implicit parameter is the DUID, don't assume an Ethernet link)
- */
-
-void
-build_template_request6(void)
-{
-	uint8_t *p = template_request6;
-
-	xid_request6 = DHCP6_OFF_XID;
-	/* message type */
-	p[DHCP6_OFF_MSGTYP] = DHCP6_OP_REQUEST;
-	/* options */
-	p += DHCP6_OFF_OPTIONS;
-	/* elapsed time */
-	p[1] = DHCP6_OPT_ELAPSED_TIME;
-	p[3] = 2;
-	p += 4;
-	elapsed_request6 = p - template_request6;
-	p += 2;
-	/* client ID */
-	p[1] = DHCP6_OPT_CLIENTID;
-	p[3] = duid_length;
-	memcpy(p + 4, duid_prefix, duid_length);
-	p += 4 + duid_length;
-	random_request6 = p - template_request6;
-	/* option request option */
-	p[1] = DHCP6_OPT_ORO;
-	p[3] = 4;
-	p[5] = DHCP6_OPT_NAME_SERVERS;
-	p[7] = DHCP6_OPT_DOMAIN_SEARCH;
-	p += 8;
-	/* server ID and IA_NA */
-	serverid_request6 = p - template_request6;
-	reqaddr_request6 = p - template_request6;
-	/* set length */
-	length_request6 = p - template_request6;
-}
-
-/*
- * get a DHCPv6 third packet (usually a REQUEST) template
- * from the file given in the command line (-T<template-file>)
- * and offsets (-X,-O,-E,-S,-I).
- */
-
-void
-get_template_request6(void)
-{
-	uint8_t *p = template_request6;
-	int fd, cc, i, j;
-
-	fd = open(templatefile[1], O_RDONLY);
-	if (fd < 0) {
-		fprintf(stderr, "open(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(2);
-	}
-	cc = read(fd, tbuf, sizeof(tbuf));
-	(void) close(fd);
-	if (cc < 0) {
-		fprintf(stderr, "read(%s): %s\n",
-			templatefile[1], strerror(errno));
-		exit(1);
-	}
-	if (cc < 10) {
-		fprintf(stderr, "file '%s' too short\n", templatefile[1]);
-		exit(2);
-	}
-	if (cc > 8193) {
-		fprintf(stderr,"file '%s' too large\n", templatefile[1]);
-		exit(2);
-	}
-	j = 0;
-	for (i = 0; i < cc; i++) {
-		if (isspace((int) tbuf[i]))
-			continue;
-		if (!isxdigit((int) tbuf[i])) {
-			fprintf(stderr,
-				"illegal char[%d]='%c' in file '%s'\n",
-				i, (int) tbuf[i], templatefile[1]);
-			exit(2);
-		}
-		tbuf[j] = tbuf[i];
-		j++;
-	}
-	cc = j;
-	if ((cc & 1) != 0) {
-		fprintf(stderr,
-			"odd number of hexadecimal digits in file '%s'\n",
-			templatefile[1]);
-		exit(2);
-	}
-	length_request6 = cc >> 1;
-	for (i = 0; i < cc; i += 2)
-		(void) sscanf(tbuf + i, "%02hhx", &p[i >> 1]);
-	if (xidoffset[1] >= 0)
-		xid_request6 = (size_t) xidoffset[1];
-	else
-		xid_request6 = DHCP6_OFF_XID;
-	if (xid_request6 + 3 > length_request6) {
-		fprintf(stderr,
-			"xid (at %zu) is outside the template (length %zu)?\n",
-			xid_request6, length_request6);
-		exit(2);
-	}
-	if (rndoffset[1] >= 0)
-		random_request6 = (size_t) rndoffset[1];
-	else
-		random_request6 = 0;
-	if (random_request6 > length_request6) {
-		fprintf(stderr,
-			"random (at %zu) outside the template (length %zu)?\n",
-			random_request6, length_request6);
-		exit(2);
-	}
-	if (elpoffset >= 0)
-		elapsed_request6 = (size_t) elpoffset;
-	if (elapsed_request6 + 2 > length_request6) {
-		fprintf(stderr,
-			"secs (at %zu) outside the template (length %zu)?\n",
-			elapsed_request6, length_request6);
-		exit(2);
-	}
-	serverid_request6 = (size_t) sidoffset;
-	if (serverid_request6 > length_request6) {
-		fprintf(stderr,
-			"server-id option (at %zu) outside the template "
-			"(length %zu)?\n",
-			serverid_request6, length_request6);
-		exit(2);
-	}
-	reqaddr_request6 = (size_t) ripoffset;
-	if (reqaddr_request6 > length_request6) {
-		fprintf(stderr,
-			"requested-ip-address option (at %zu) outside "
-			"the template (length %zu)?\n",
-			reqaddr_request6, length_request6);
-		exit(2);
-	}
-}
-
-/*
- * send the DHCPv6 REQUEST third packet
- * (the transaction ID is odd)
- * (TODO: check for errors in the ADVERTISE)
- */
-
-void
-send_request6(struct exchange *x0)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	size_t len;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL) {
-		locallimit++;
-		perror("send2");
-		return;
-	}
-
-	memcpy(x, x0, sizeof(*x));
-	x->order2 = xscount2++;
-	x->xid |= 1;
-	hash = x->xid >> 1;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent2, x, gchain);
-	hash &= hashsize2 - 1;
-	bucket = (struct xlist *) (exchanges2 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	len = length_request6;
-	memcpy(obuf, template_request6, len);
-	/* xid */
-	obuf[xid_request6] = x->xid >> 16;
-	obuf[xid_request6 + 1] = x->xid >> 8;
-	obuf[xid_request6 + 2] = x->xid;
-	/* random */
-	randomize(random_request6, x->rnd);
-	/* elapsed time */
-	if (elapsed_request6 > 0) {
-		int et;
-
-		et = (x->ts1.tv_sec - x->ts0.tv_sec) * 100;
-		et += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 10000000;
-		if (et > 65535) {
-			obuf[elapsed_request6] = 0xff;
-			obuf[elapsed_request6 + 1] = 0xff;
-		} else if (et > 0) {
-			obuf[elapsed_request6] = et >> 8;
-			obuf[elapsed_request6 + 1] = et & 0xff;
-		}
-	}
-	/* server ID */
-	if (serverid_request6 < length_request6)
-		memmove(obuf + serverid_request6 + x->sidlen,
-			obuf + serverid_request6,
-			x->sidlen);
-	memcpy(obuf + serverid_request6, x->sid, x->sidlen);
-	len += x->sidlen;
-	/* IA_NA */
-	if (reqaddr_request6 < serverid_request6) {
-		memmove(obuf + reqaddr_request6 + x->ianalen,
-			obuf + reqaddr_request6,
-			x->ianalen);
-		memcpy(obuf + reqaddr_request6, x->iana, x->ianalen);
-	} else if (reqaddr_request6 < length_request6) {
-		memmove(obuf + reqaddr_request6 + x->sidlen + x->ianalen,
-			obuf + reqaddr_request6 + x->sidlen,
-			x->ianalen);
-		memcpy(obuf + reqaddr_request6+ x->sidlen,
-		       x->iana,
-		       x->ianalen);
-	} else
-		memcpy(obuf + len, x->iana, x->ianalen);
-	len += x->ianalen;
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &x->ts2);
-	if (ret < 0) {
-		perror("clock_gettime(send2)");
-		fatal = 1;
-		return;
-	}
-	ret = sendto(sock, obuf, len, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in6));
-	if (ret >= 0)
-		return;
-	if ((errno == EAGAIN) || (errno == EWOULDBLOCK) ||
-	    (errno == ENOBUFS) || (errno == ENOMEM))
-		locallimit++;
-	perror("send2");
-}
-
-/*
- * send the DHCPv6 SOLICIT first packet
- * (for 4-exchange, the transaction ID xid is even)
- */
-
-int
-send6(void)
-{
-	struct exchange *x;
-	struct xlist *bucket;
-	uint32_t hash;
-	ssize_t ret;
-
-	x = (struct exchange *) malloc(sizeof(*x));
-	if (x == NULL)
-		return -ENOMEM;
-
-	memset(x, 0, sizeof(*x));
-	x->order0 = xscount0++;
-	hash = x->rnd = (uint32_t) random();
-	if (simple == 0)
-		x->xid = (hash << 1) & 0x00ffffff;
-	else
-		x->xid = hash & 0x00ffffff;
-
-	ISC_TAILQ_INSERT_TAIL(&xsent0, x, gchain);
-	hash &= hashsize0 - 1;
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	ISC_TAILQ_INSERT_TAIL(bucket, x, hchain);
-
-	memcpy(obuf, template_solicit6, length_solicit6);
-	/* xid */
-	obuf[xid_solicit6] = x->xid >> 16;
-	obuf[xid_solicit6 + 1] = x->xid >> 8;
-	obuf[xid_solicit6 + 2] = x->xid;
-	/* random */
-	x->rnd = randomize(random_solicit6, x->rnd);
-
-	/* timestamp */
-	ret = clock_gettime(CLOCK_REALTIME, &last);
-	if (ret < 0) {
-		perror("clock_gettime(send)");
-		fatal = 1;
-		return -errno;
-	}
-	x->ts0 = last;
-	errno = 0;
-	ret = sendto(sock, obuf, length_solicit6, 0,
-		     (struct sockaddr *) &serveraddr,
-		     sizeof(struct sockaddr_in6));
-	if (ret == (ssize_t) length_solicit6)
-		return 0;
-	return -errno;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its server-id option
- */
-
-int
-scan_for_srvid6(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP6_OFF_OPTIONS, len;
-
-	for (;;) {
-		if (off + 4 > cc) {
-			fprintf(stderr, "server-id not found\n");
-			return -1;
-		}
-		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_SERVERID))
-			break;
-		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	}
-	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	/* cache it in the global variables when required and not yet done */
-	if ((usefirst != 0) && (gsrvid == NULL)) {
-		if (len <= sizeof(gsrvidbuf)) {
-			memcpy(gsrvidbuf, ibuf + off, len);
-			gsrvid = gsrvidbuf;
-			gsrvidlen = len;
-		} else {
-			gsrvid = (uint8_t *) malloc(len);
-			if (gsrvid == NULL) {
-				perror("malloc(gsrvid");
-				return -1;
-			}
-			memcpy(gsrvid, ibuf + off, len);
-			gsrvidlen = len;
-		}
-	}
-	x->sid = ibuf + off;
-	x->sidlen = len;
-	return 0;
-}
-
-/*
- * scan a DHCPv6 ADVERTISE to get its IA_NA option
- * (TODO: check for errors)
- */
-
-int
-scan_for_ia_na(struct exchange *x, size_t cc)
-{
-	size_t off = DHCP6_OFF_OPTIONS, len;
-
-	for (;;) {
-		if (off + 4 > cc) {
-			fprintf(stderr, "ia-na not found\n");
-			return -1;
-		}
-		if ((ibuf[off] == 0) && (ibuf[off + 1] == DHCP6_OPT_IA_NA))
-			break;
-		off += 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	}
-	len = 4 + (ibuf[off + 2] << 8) + ibuf[off + 3];
-	x->iana = ibuf + off;
-	x->ianalen = len;
-	return 0;
-}
-
-/*
- * receive a DHCPv6 packet
- */
-
-void
-receive6(void)
-{
-	struct exchange *x, *t;
-	struct xlist *bucket;
-	struct timespec now;
-	ssize_t cc;
-	uint32_t xid, hash;
-	int checklost = 0;
-	double delta;
-
-	cc = recv(sock, ibuf, sizeof(ibuf), 0);
-	if (cc < 0) {
-		if ((errno == EAGAIN) ||
-		    (errno == EWOULDBLOCK) ||
-		    (errno == EINTR))
-			return;
-		perror("recv");
-		fatal = 1;
-		return;
-	}
-	/* enforce a reasonable length */
-	if (cc < 22) {
-		tooshort++;
-		return;
-	}
-	if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-		perror("clock_gettime(receive)");
-		fatal = 1;
-		return;
-	}
-	xid = ibuf[xid_solicit6] << 16;
-	xid |= ibuf[xid_solicit6 + 1] << 8;
-	xid |= ibuf[xid_solicit6 + 2];
-	/* 4-packet exchange even/odd xid */
-	if (simple == 0) {
-		if ((xid & 1) != 0) {
-			receive_reply(xid, &now);
-			return;
-		}
-		hash = (xid >> 1) & (hashsize0 - 1);
-	} else
-		hash = xid & (hashsize0 - 1);
-	/* now it is the second packet, get the bucket which is needed */
-	bucket = (struct xlist *) (exchanges0 + hash * sizeof(*bucket));
-	/* try the 'next to be received' cache */
-	if ((xnext0 != NULL) && (xnext0->xid == xid)) {
-		x = xnext0;
-		goto found;
-	}
-	/* if the rate is not illimited, garbage collect up to 3
-	   timed-out exchanges */
-	if (rate != 0)
-		checklost = 3;
-	/* look for the exchange */
-	ISC_TAILQ_FOREACH_SAFE(x, bucket, hchain, t) {
-		double waited;
-
-		if (x->xid == xid)
-			goto found;
-		if (checklost <= 0)
-			continue;
-		/* check for a timed-out exchange */
-		waited = now.tv_sec - x->ts0.tv_sec;
-		waited += (now.tv_nsec - x->ts0.tv_nsec) / 1e9;
-		if (waited < losttime[0]) {
-			checklost = 0;
-			continue;
-		}
-		/* garbage collect timed-out exchange */
-		checklost--;
-		ISC_TAILQ_REMOVE(bucket, x, hchain);
-		ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-		free(x);
-		collected[0] += 1;
-	}
-	/* no match? very late or not for us */
-	orphans++;
-	return;
-
-	/* got it: update stats and move to the received queue */
-    found:
-	xrcount0++;
-	x->ts1 = now;
-	delta = x->ts1.tv_sec - x->ts0.tv_sec;
-	delta += (x->ts1.tv_nsec - x->ts0.tv_nsec) / 1e9;
-	if (delta < dmin0)
-		dmin0 = delta;
-	if (delta > dmax0)
-		dmax0 = delta;
-	dsum0 += delta;
-	dsumsq0 += delta * delta;
-	xnext0 = ISC_TAILQ_NEXT(x, gchain);
-	ISC_TAILQ_REMOVE(bucket, x, hchain);
-	ISC_TAILQ_REMOVE(&xsent0, x, gchain);
-	ISC_TAILQ_INSERT_TAIL(&xrcvd0, x, gchain);
-	/* if the exchange is not finished, go to the second part */
-	if (simple == 0) {
-		int ret = 0;
-
-		/* the server-ID option is needed */
-		if ((usefirst != 0) && (gsrvid != NULL)) {
-			x->sid = gsrvid;
-			x->sidlen = gsrvidlen;
-		} else
-			ret = scan_for_srvid6(x, cc);
-		/* and the IA_NA option too */
-		if (ret >= 0)
-			ret = scan_for_ia_na(x, cc);
-		if (ret >= 0)
-			send_request6(x);
-	}
-}
-
-/*
- * decode a base command line parameter
- * (currently only MAC address and DUID are supported)
- */
-
-void
-decodebase(void)
-{
-	char *b0 = base[basecnt];
-
-	/* MAC address (alias Ethernet address) */
-	if ((strncasecmp(b0, "mac=", 4) == 0) ||
-	    (strncasecmp(b0, "ether=", 6) == 0)) {
-		char *b;
-
-		b = strchr(b0, '=') + 1;
-		if (sscanf(b, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
-			   &mac_prefix[0], &mac_prefix[1],
-			   &mac_prefix[2], &mac_prefix[3],
-			   &mac_prefix[4], &mac_prefix[5]) == 6) {
-			if ((diags != NULL) && (strchr(diags, 'a') != NULL))
-				printf("set MAC to %s\n", b);
-			return;
-		}
-		fprintf(stderr,
-			"expected base in '%*s=xx:xx:xx:xx:xx:xx' format\n",
-			(int) (b - b0 - 1), b0);
-			exit(2);
-	}
-	/* DUID */
-	if (strncasecmp(b0, "duid=", 5) == 0) {
-		char *b;
-		size_t i, l;
-
-		if (duid_prefix != NULL) {
-			fprintf(stderr, "duid was already set?\n");
-			exit(2);
-		}
-		b = strchr(b0, '=') + 1;
-		l = 0;
-		while (*b != '\0') {
-			if (!isxdigit((int) *b)) {
-				fprintf(stderr,
-					"illegal char '%c' in duid\n",
-					(int) *b);
-				exit(2);
-			}
-			b++;
-			l++;
-		}
-		if ((l & 1) != 0) {
-			fprintf(stderr,
-				"odd number of hexadecimal digits in duid\n");
-			exit(2);
-		}
-		l /= 2;
-		if (l > 64) {
-			fprintf(stderr, "duid too large\n");
-			exit(2);
-		}
-		duid_prefix = (uint8_t *) malloc(l);
-		if (duid_prefix == NULL) {
-			perror("malloc(duid)");
-			exit(1);
-		}
-		b = strchr(b0, '=') + 1;
-		for (i = 0; i < l; i++, b += 2)
-			(void) sscanf(b, "%02hhx", &duid_prefix[i]);
-		duid_length = l;
-		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-			b = strchr(b0, '=') + 1;
-			printf("set DUID[%d] to %s\n",
-			       duid_length, b);
-		}
-		return;
-	}
-	/* other */
-	fprintf(stderr, "not yet supported base '%s'\n", b0);
-	exit(2);
-}
-			   
-/*
- * get the server socket address from the command line:
- *  - flags: inherited from main, 0 or AI_NUMERICHOST (for literals)
- */
-
-void
-getserveraddr(const int flags)
-{
-	struct addrinfo hints, *res;
-	char *service;
-	int ret;
-
-	memset(&hints, 0, sizeof(hints));
-	if (ipversion == 4) {
-		hints.ai_family = AF_INET;
-		service = "67";
-	} else {
-		hints.ai_family = AF_INET6;
-		service = "547";
-	}
-	hints.ai_socktype = SOCK_DGRAM;
-
-	hints.ai_flags = AI_NUMERICSERV | flags;
-#if defined(AI_ADDRCONFIG)
-	hints.ai_flags |= AI_ADDRCONFIG;
-#endif
-	hints.ai_protocol = IPPROTO_UDP;
-	
-	ret = getaddrinfo(servername, service, &hints, &res);
-	if (ret != 0) {
-		fprintf(stderr, "bad server=%s: %s\n",
-			servername, gai_strerror(ret));
-		exit(2);
-	}
-	if (res->ai_next != NULL) {
-		fprintf(stderr, "ambiguous server=%s\n", servername);
-		exit(2);
-	}
-	memcpy(&serveraddr, res->ai_addr, res->ai_addrlen);
-	freeaddrinfo(res);
-}
-
-/*
- * get the interface from the command line:
- *   - name of the interface
- *   - socket address to fill
- * (in IPv6, get the first link-local address)
- */
-
-void
-getinterface(const char *name, struct sockaddr_storage *ss)
-{
-	struct ifaddrs *ifaddr, *ifa;
-	int ret;
-	size_t len;
-
-	ret = getifaddrs(&ifaddr);
-	if (ret < 0) {
-		perror("getifaddrs");
-		exit(1);
-	}
-
-	for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
-		if (ifa->ifa_name == NULL)
-			continue;
-		if (strcmp(ifa->ifa_name, name) != 0)
-			continue;
-		if ((ifa->ifa_flags & IFF_UP) == 0)
-			continue;
-		if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
-			continue;
-		if (ifa->ifa_addr == NULL)
-			continue;
-		if ((ipversion == 4) &&
-		    (ifa->ifa_addr->sa_family != AF_INET))
-			continue;
-		if (ipversion == 6) {
-			struct sockaddr_in6 *a6;
-
-			a6 = (struct sockaddr_in6 *) ifa->ifa_addr;
-			if (a6->sin6_family != AF_INET6)
-				continue;
-			if (!IN6_IS_ADDR_LINKLOCAL(&a6->sin6_addr))
-				continue;
-		}
-		if (ipversion == 4)
-			len = sizeof(struct sockaddr_in);
-		else
-			len = sizeof(struct sockaddr_in6);
-		memcpy(ss, ifa->ifa_addr, len);
-		/* fill the server port */
-		if (ipversion == 4)
-			((struct sockaddr_in *) ss)->sin_port = htons(67);
-		else
-			((struct sockaddr_in6 *) ss)->sin6_port = htons(546);
-		return;
-	}
-	fprintf(stderr, "can't find interface %s\n", name);
-	exit(1);
-}
-
-/*
- * get the local socket address from the command line
- * (if it doesn't work, try an interface name)
- */
-
-void
-getlocaladdr(void)
-{
-	struct addrinfo hints, *res;
-	char *service;
-	int ret;
-
-	memset(&hints, 0, sizeof(hints));
-	if (ipversion == 4) {
-		hints.ai_family = AF_INET;
-		service = "67";
-	} else {
-		hints.ai_family = AF_INET6;
-		service = "546";
-	}
-	hints.ai_socktype = SOCK_DGRAM;
-	hints.ai_flags =  AI_NUMERICSERV;
-#if defined(AI_ADDRCONFIG)
-	hints.ai_flags |= AI_ADDRCONFIG;
-#endif
-	hints.ai_protocol = IPPROTO_UDP;
-	
-	ret = getaddrinfo(localname, service, &hints, &res);
-	if ((ret == EAI_NONAME)
-#ifdef EAI_NODATA
-	    || (ret == EAI_NODATA)
-#endif
-	   ) {
-		isinterface = 1;
-		getinterface(localname, &localaddr);
-		return;
-	}
-	if (ret != 0) {
-		fprintf(stderr,
-			"bad -l<local-addr=%s>: %s\n",
-			localname,
-			gai_strerror(ret));
-		exit(2);
-	}
-	/* refuse multiple addresses */
-	if (res->ai_next != NULL) {
-		fprintf(stderr,
-			"ambiguous -l<local-addr=%s>\n",
-			localname);
-		exit(2);
-	}
-	memcpy(&localaddr, res->ai_addr, res->ai_addrlen);
-	freeaddrinfo(res);
-	return;
-}
-
-/*
- * get the local socket address from the server one
- */
-
-void
-getlocal(void)
-{
-	int ret, s;
-	socklen_t l;
-
-	if (ipversion == 4) {
-		l = sizeof(struct sockaddr_in);
-		s = socket(PF_INET, SOCK_DGRAM, 0);
-	} else {
-		l = sizeof(struct sockaddr_in6);
-		s = socket(PF_INET6, SOCK_DGRAM, 0);
-	}
-	if (s < 0) {
-		perror("socket");
-		exit(1);
-	}
-	if ((ipversion == 4) && (isbroadcast != 0)) {
-		int on = 1;
-
-		ret = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
-		if (ret < 0) {
-			perror("setsockopt(SO_BROADCAST)");
-			exit(1);
-		}
-	}
-	ret = connect(s, (struct sockaddr *) &serveraddr, l);
-	if (ret < 0) {
-		perror("connect");
-		exit(1);
-	}
-	l = sizeof(localaddr);
-	ret = getsockname(s, (struct sockaddr *) &localaddr, &l);
-	if (ret < 0) {
-		perror("getsockname");
-		exit(1);
-	}
-	(void) close(s);
-	/* fill the local port */
-	if (ipversion == 4)
-		((struct sockaddr_in *) &localaddr)->sin_port = htons(67);
-	else
-		((struct sockaddr_in6 *) &localaddr)->sin6_port = htons(546);
-}
-
-/*
- * intermediate reporting
- * (note: an in-transit packet can be reported as dropped)
- */
-
-void
-reporting(void)
-{
-	dreport.tv_sec += report;
-
-	if (xscount2 == 0) {
-		printf("sent: %llu, received: %llu (drops: %lld)",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xrcount0,
-		       (long long) (xscount0 - xrcount0));
-		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
-			double avg;
-
-			avg = dsum0 / xrcount0;
-			printf(" average: %.3f ms", avg * 1e3);
-		}
-	} else {
-		printf("sent: %llu/%llu received: %llu/%llu "
-		       "(drops: %lld/%lld)",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xscount2,
-		       (unsigned long long) xrcount0,
-		       (unsigned long long) xrcount2,
-		       (long long) (xscount0 - xrcount0),
-		       (long long) (xscount2 - xrcount2));
-		if (!ISC_TAILQ_EMPTY(&xrcvd0)) {
-			double avg0, avg2;
-
-			avg0 = dsum0 / xrcount0;
-			if (xrcount2 != 0)
-				avg2 = dsum2 / xrcount2;
-			else
-				avg2 = 0.;
-			printf(" average: %.3f/%.3f ms",
-			       avg0 * 1e3, avg2 * 1e3);
-		}
-	}
-	printf("\n");
-}
-
-/*
- * SIGCHLD handler
- */
-
-void
-reapchild(int sig)
-{
-	int status;
-
-	sig = sig;
-	while (wait3(&status, WNOHANG, NULL) > 0)
-		/* continue */;
-}
-
-/*
- * SIGINT handler
- */
-
-void
-interrupt(int sig)
-{
-	sig = sig;
-	interrupted = 1;
-}
-
-/*
- * '-v' handler
- */
-
-void
-version(void)
-{
-	fprintf(stderr, "version 0.01\n");
-}
-
-/*
- * usage (from the wiki)
- */
-
-void
-usage(void)
-{
-	fprintf(stderr, "%s",
-"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
-"    [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
-"    [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
-"    [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
-"    [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
-"    [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
-"    [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
-"\f\n"
-"The [server] argument is the name/address of the DHCP server to\n"
-"contact.  For DHCPv4 operation, exchanges are initiated by\n"
-"transmitting a DHCP DISCOVER to this address.\n"
-"\n"
-"For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
-"SOLICIT to this address.  In the DHCPv6 case, the special name 'all'\n"
-"can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
-"multicast address FF02::1:2), or the special name 'servers' to refer\n"
-"to All_DHCP_Servers (the multicast address FF05::1:3).  The [server]\n"
-"argument is optional only in the case that -l is used to specify an\n"
-"interface, in which case [server] defaults to 'all'.\n"
-"\n"
-"The default is to perform a single 4-way exchange, effectively pinging\n"
-"the server.\n"
-"The -r option is used to set up a performance test, without\n"
-"it exchanges are initiated as fast as possible.\n"
-"\n"
-"Options:\n"
-"-1: Take the server-ID option from the first received message.\n"
-"-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
-"-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
-"-a<aggressivity>: When the target sending rate is not yet reached,\n"
-"    control how many exchanges are initiated before the next pause.\n"
-"-b<base>: The base MAC, DUID, IP, etc, used to simulate different\n"
-"    clients.  This can be specified multiple times, each instance is\n"
-"    in the <type>=<value> form, for instance:\n"
-"    (and default) MAC=00:0c:01:02:03:04.\n"
-"-d<drop-time>: Specify the time after which a request is treated as\n"
-"    having been lost.  The value is given in seconds and may contain a\n"
-"    fractional component.  The default is 1 second.\n"
-"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
-"    elapsed-time option in the (second/request) template.\n"
-"    The value 0 disables it.\n"
-"-h: Print this help.\n"
-"-i: Do only the initial part of an exchange: DO or SA, depending on\n"
-"    whether -6 is given.\n"
-"-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
-"    option / (DHCPv6) IA_NA option in the (second/request) template.\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\n"
-"    normally be routed to the server is used.\n"
-"    For DHCPv6 operation, specify the name of the network interface\n"
-"    via which exchanges are initiated.\n"
-"-L<local-port>: Specify the local port to use\n"
-"    (the value 0 means to use the default).\n"
-"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
-"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
-"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
-"    exchanges per second.  A periodic report is generated showing the\n"
-"    number of exchanges which were not completed, as well as the\n"
-"    average response latency.  The program continues until\n"
-"    interrupted, at which point a final report is generated.\n"
-"-R<range>: Specify how many different clients are used. With 1\n"
-"    (the default), all requests seem to come from the same client.\n"
-"-s<seed>: Specify the seed for randomization, making it repeatable.\n"
-"-S<srvid-offset>: Offset of the server-ID option in the\n"
-"    (second/request) template.\n"
-"-T<template-file>: The name of a file containing the template to use\n"
-"    as a stream of hexadecimal digits.\n"
-"-v: Report the version number of this program.\n"
-"-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
-"    the program.\n"
-"-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
-"    <diagnostic-selector> is a string of single-keywords specifying\n"
-"    the operations for which verbose output is desired.  The selector\n"
-"    keyletters are:\n"
-"   * 'a': print the decoded command line arguments\n"
-"   * 'e': print the exit reason\n"
-"   * 'i': print rate processing details\n"
-"   * 'r': print randomization details\n"
-"   * 's': print first server-id\n"
-"   * 't': when finished, print timers of all successful exchanges\n"
-"   * 'T': when finished, print templates\n"
-"-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
-"\n"
-"DHCPv4 only options:\n"
-"-B: Force broadcast handling.\n"
-"\n"
-"DHCPv6 only options:\n"
-"-c: Add a rapid commit option (exchanges will be SA).\n"
-"\n"
-"The remaining options are used only in conjunction with -r:\n"
-"\n"
-"-D<max-drop>: Abort the test if more than <max-drop> requests have\n"
-"    been dropped.  Use -D0 to abort if even a single request has been\n"
-"    dropped.  If <max-drop> includes the suffix '%', it specifies a\n"
-"    maximum percentage of requests that may be dropped before abort.\n"
-"    In this case, testing of the threshold begins after 10 requests\n"
-"    have been expected to be received.\n"
-"-n<num-request>: Initiate <num-request> transactions.  No report is\n"
-"    generated until all transactions have been initiated/waited-for,\n"
-"    after which a report is generated and the program terminates.\n"
-"-p<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"
-"-t<report>: Delay in seconds between two periodic reports.\n"
-"\n"
-"Errors:\n"
-"- tooshort: received a too short message\n"
-"- orphans: received a message which doesn't match an exchange\n"
-"   (duplicate, late or not related)\n"
-"- locallimit: reached to local system limits when sending a message.\n"
-"\n"
-"Exit status:\n"
-"The exit status is:\n"
-"0 on complete success.\n"
-"1 for a general error.\n"
-"2 if an error is found in the command line arguments.\n"
-"3 if there are no general failures in operation, but one or more\n"
-"  exchanges are not successfully completed.\n");
-}
-
-/*
- * main function / entry point
- */
-
-int
-main(const int argc, char * const argv[])
-{
-	int opt, flags = 0, ret, i;
-	long long r;
-	char *pc;
-	extern char *optarg;
-	extern int optind;
-
-#define OPTIONS	"hv46r:t:R:b:n:p:d:D:l:P:a:L:s:iBc1T:X:O:E:S:I:x:w:"
-
-	/* decode options */
-	while ((opt = getopt(argc, argv, OPTIONS)) != -1)
-	switch (opt) {
-	case 'h':
-		usage();
-		exit(0);
-
-	case 'v':
-		version();
-		exit(0);
-
-	case '4':
-		if (ipversion == 6) {
-			fprintf(stderr, "IP version already set to 6\n");
-			usage();
-			exit(2);
-		}
-		ipversion = 4;
-		break;
-
-	case '6':
-		if (ipversion == 4) {
-			fprintf(stderr, "IP version already set to 4\n");
-			usage();
-			exit(2);
-		}
-		ipversion = 6;
-		break;
-
-	case 'r':
-		rate = atoi(optarg);
-		if (rate <= 0) {
-			fprintf(stderr, "rate must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 't':
-		report = atoi(optarg);
-		if (report <= 0) {
-			fprintf(stderr, "report must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'R':
-		r = atoll(optarg);
-		if (r < 0) {
-			fprintf(stderr,
-				"range must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		range = (uint32_t) r;
-		if ((range != 0) && (range != UINT32_MAX)) {
-			uint32_t s = range + 1;
-			uint64_t b = UINT32_MAX + 1, m;
-
-			m = (b / s) * s;
-			if (m == b)
-				maxrandom = 0;
-			else
-				maxrandom = (uint32_t) m;
-		}
-		break;
-
-	case 'b':
-		if (basecnt > 3) {
-			fprintf(stderr, "too many bases\n");
-			usage();
-			exit(2);
-		}
-		base[basecnt] = optarg;
-		decodebase();
-		basecnt++;
-		break;
-
-	case 'n':
-		numreq[gotnumreq] = atoi(optarg);
-		if (numreq[gotnumreq] <= 0) {
-			fprintf(stderr,
-				"num-request must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		gotnumreq = 1;
-		break;
-
-	case 'p':
-		period = atoi(optarg);
-		if (period <= 0) {
-			fprintf(stderr,
-				"test-period must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'd':
-		losttime[gotlosttime] = atof(optarg);
-		if (losttime[gotlosttime] <= 0.) {
-			fprintf(stderr,
-				"drop-time must be a positive number\n");
-			usage();
-			exit(2);
-		}
-		gotlosttime = 1;
-		break;
-
-	case 'D':
-		pc = strchr(optarg, '%');
-		if (pc != NULL) {
-			*pc = '\0';
-			maxpdrop[gotmaxdrop] = atof(optarg);
-			if ((maxpdrop[gotmaxdrop] <= 0) ||
-			    (maxpdrop[gotmaxdrop] >= 100)) {
-				fprintf(stderr,
-					"invalid drop-time percentage\n");
-				usage();
-				exit(2);
-			}
-			gotmaxdrop = 1;
-			break;
-		}
-		maxdrop[gotmaxdrop] = atoi(optarg);
-		if (maxdrop[gotmaxdrop] <= 0) {
-			fprintf(stderr,
-				"max-drop must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		gotmaxdrop = 1;
-		break;
-
-	case 'l':
-		localname = optarg;
-		break;
-
-	case 'P':
-		preload = atoi(optarg);
-		if (preload < 0) {
-			fprintf(stderr,
-				"preload must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'a':
-		aggressivity = atoi(optarg);
-		if (aggressivity <= 0) {
-			fprintf(stderr,
-				"aggressivity must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'L':
-		localport = atoi(optarg);
-		if (localport < 0) {
-			fprintf(stderr,
-				"local-port must not be a negative integer\n");
-			usage();
-			exit(2);
-		}
-		if (localport > (int) UINT16_MAX) {
-			fprintf(stderr,
-				"local-port must be lower than %d\n",
-				(int) UINT16_MAX);
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 's':
-		seeded = 1;
-		seed = (unsigned int) atol(optarg);
-		break;
-
-	case 'i':
-		simple = 1;
-		break;
-
-	case 'B':
-		isbroadcast = 1;
-		break;
-
-	case 'c':
-		rapidcommit = 1;
-		break;
-
-	case '1':
-		usefirst = 1;
-		break;
-
-	case 'T':
-		if (templatefile[0] != NULL) {
-			if (templatefile[1] != NULL) {
-				fprintf(stderr,
-					"template-files are already set\n");
-				usage();
-				exit(2);
-			}
-			templatefile[1] = optarg;
-		} else
-			templatefile[0] = optarg;
-		break;
-
-	case 'X':
-		if (xidoffset[0] >= 0)
-			i = 1;
-		else
-			i = 0;
-		xidoffset[i] = atoi(optarg);
-		if (xidoffset[i] <= 0) {
-			fprintf(stderr,
-				"xid-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'O':
-		if (rndoffset[0] >= 0)
-			i = 1;
-		else
-			i = 0;
-		rndoffset[i] = atoi(optarg);
-		if (rndoffset[i] < 3) {
-			fprintf(stderr,
-				"random-offset must be greater than 3\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'E':
-		elpoffset = atoi(optarg);
-		if (elpoffset < 0) {
-			fprintf(stderr,
-				"time-offset must not be a "
-				"negative integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'S':
-		sidoffset = atoi(optarg);
-		if (sidoffset <= 0) {
-			fprintf(stderr,
-				"srvid-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'I':
-		ripoffset = atoi(optarg);
-		if (ripoffset <= 0) {
-			fprintf(stderr,
-				"ip-offset must be a positive integer\n");
-			usage();
-			exit(2);
-		}
-		break;
-
-	case 'x':
-		diags = optarg;
-		break;
-
-	case 'w':
-		wrapped = optarg;
-		break;
-
-	default:
-		usage();
-		exit(2);
-	}
-
-	/* adjust some global variables */
-	if (ipversion == 0)
-		ipversion = 4;
-	if (templatefile[1] != NULL) {
-		if (xidoffset[1] < 0)
-			xidoffset[1] = xidoffset[0];
-		if (rndoffset[1] < 0)
-			rndoffset[1] = rndoffset[0];
-	}
-
-	/* when required, print the internal view of the command line */
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-		printf("IPv%d", ipversion);
-		if (simple != 0) {
-			if (ipversion == 4)
-				printf(" DO only");
-			else
-				printf(" SA only");
-		}
-		if (rate != 0)
-			printf(" rate=%d", rate);
-		if (report != 0)
-			printf(" report=%d", report);
-		if (range != 0) {
-			if (strchr(diags, 'r') != NULL)
-				printf(" range=0..%d [0x%x]",
-				       range,
-				       (unsigned int) maxrandom);
-			else
-				printf(" range=0..%d", range);
-		}
-		if (basecnt != 0)
-			for (i = 0; i < basecnt; i++)
-				printf(" base[%d]='%s'", i, base[i]);
-		if (gotnumreq != 0)
-			printf(" num-request=%d,%d", numreq[0], numreq[1]);
-		if (period != 0)
-			printf(" test-period=%d", period);
-		printf(" drop-time=%g,%g", losttime[0], losttime[1]);
-		if ((maxdrop[0] != 0) || (maxdrop[1] != 0))
-			printf(" max-drop=%d,%d", maxdrop[0], maxdrop[1]);
-		if ((maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))
-			printf(" max-drop=%2.2f%%,%2.2f%%",
-			       maxpdrop[0], maxpdrop[1]);
-		if (preload != 0)
-			printf(" preload=%d", preload);
-		printf(" aggressivity=%d", aggressivity);
-		if (localport != 0)
-			printf(" local-port=%d", localport);
-		if (seeded)
-			printf(" seed=%u", seed);
-		if (isbroadcast != 0)
-			printf(" broadcast");
-		if (rapidcommit != 0)
-			printf(" rapid-commit");
-		if (usefirst != 0)
-			printf(" use-first");
-		if ((templatefile[0] != NULL) && (templatefile[1] == NULL))
-			printf(" template-file='%s'", templatefile[0]);
-		else if (templatefile[1] != NULL)
-			printf(" template-file='%s','%s'",
-			       templatefile[0], templatefile[1]);
-		if ((xidoffset[0] >= 0) && (xidoffset[1] < 0))
-			printf(" xid-offset=%d", xidoffset[0]);
-		else if (xidoffset[1] >= 0)
-			printf(" xid-offset=%d,%d",
-			       xidoffset[0], xidoffset[1]);
-		if ((rndoffset[0] >= 0) && (rndoffset[1] < 0))
-			printf(" xid-offset=%d", rndoffset[0]);
-		else if (rndoffset[1] >= 0)
-			printf(" xid-offset=%d,%d",
-			       rndoffset[0], rndoffset[1]);
-		if (elpoffset >= 0)
-			printf(" time-offset=%d", elpoffset);
-		if (sidoffset >= 0)
-			printf(" srvid-offset=%d", sidoffset);
-		if (ripoffset >= 0)
-			printf(" ip-offset=%d", ripoffset);
-		printf(" diagnotic-selectors='%s'", diags);
-		if (wrapped != NULL)
-			printf(" wrapped='%s'", wrapped);
-		printf("\n");
-	}
-
-	/* check DHCPv4 only options */
-	if ((ipversion != 4) && (isbroadcast != 0)) {
-		fprintf(stderr, "-b is not compatible with IPv6 (-6)\n");
-		usage();
-		exit(2);
-	}
-
-	/* check DHCPv6 only options */
-	if ((ipversion != 6) && (rapidcommit != 0)) {
-		fprintf(stderr, "-6 (IPv6) must be set to use -c\n");
-		usage();
-		exit(2);
-	}
-
-	/* check 4-packet (aka not simple) mode options */
-	if ((simple != 0) && (numreq[1] != 0)) {
-		fprintf(stderr,
-			"second -n<num-request> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (losttime[1] != 1.)) {
-		fprintf(stderr,
-			"second -d<drop-time> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) &&
-	    ((maxdrop[1] != 0) || (maxpdrop[1] != 0.))) {
-		fprintf(stderr,
-			"second -D<max-drop> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (usefirst != 0)) {
-		fprintf(stderr,
-			"-1 is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (templatefile[1] != NULL)) {
-		fprintf(stderr,
-			"second -T<template-file> is not "
-			"compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (xidoffset[1] >= 0)) {
-		fprintf(stderr,
-			"second -X<xid-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (rndoffset[1] >= 0)) {
-		fprintf(stderr,
-			"second -O<random-offset is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (elpoffset >= 0)) {
-		fprintf(stderr,
-			"-E<time-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (sidoffset >= 0)) {
-		fprintf(stderr,
-			"-S<srvid-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-	if ((simple != 0) && (ripoffset >= 0)) {
-		fprintf(stderr,
-			"-I<ip-offset> is not compatible with -i\n");
-		usage();
-		exit(2);
-	}
-
-
-	/* check simple mode options */
-	if ((simple == 0) && (rapidcommit != 0)) {
-		fprintf(stderr, "-i must be set to use -c\n");
-		usage();
-		exit(2);
-	}
-
-	/* check rate '-r' options */
-	if ((rate == 0) && (report != 0)) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -t<report>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) && ((numreq[0] != 0) || (numreq[1] != 0))) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -n<num-request>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) && (period != 0)) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -p<test-period>\n");
-		usage();
-		exit(2);
-	}
-	if ((rate == 0) &&
-	    ((maxdrop[0] != 0) || (maxdrop[1] != 0) ||
-	     (maxpdrop[0] != 0.) || (maxpdrop[1] != 0.))) {
-		fprintf(stderr,
-			"-r<rate> must be set to use -D<max-drop>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check (first) template file options */
-	if ((templatefile[0] == NULL) && (xidoffset[0] >= 0)) {
-		fprintf(stderr,
-			"-T<template-file> must be set to "
-			"use -X<xid-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[0] == NULL) && (rndoffset[0] >= 0)) {
-		fprintf(stderr,
-			"-T<template-file> must be set to "
-			"use -O<random-offset>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check (second) template file options */
-	if ((templatefile[1] == NULL) && (elpoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -E<time-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] == NULL) && (sidoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -S<srvid-offset>\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] == NULL) && (ripoffset >= 0)) {
-		fprintf(stderr,
-			"second/request -T<template-file> must be set to "
-			"use -I<ip-offset>\n");
-		usage();
-		exit(2);
-	}
-
-	/* check various template file(s) and other condition(s) options */
-	if ((templatefile[0] != NULL) && (range > 0) && (rndoffset[0] < 0)) {
-		fprintf(stderr,
-			"-O<random-offset> must be set when "
-			"-T<template-file> and -R<range> are used\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] != NULL) && (sidoffset < 0)) {
-		fprintf(stderr,
-			"-S<srvid-offset> must be set when second "
-			"-T<template-file> is used\n");
-		usage();
-		exit(2);
-	}
-	if ((templatefile[1] != NULL) && (ripoffset < 0)) {
-		fprintf(stderr,
-			"-I<ip-offset> must be set when second "
-			"-T<template-file> is used\n");
-		usage();
-		exit(2);
-	}
-
-	/* get the server argument */
-	if (optind < argc - 1) {
-		fprintf(stderr, "extra arguments?\n");
-		usage();
-		exit(2);
-	}
-	if (optind == argc - 1) {
-		servername = argv[optind];
-		/* decode special cases */
-		if ((ipversion == 4) &&
-		    (strcmp(servername, "all") == 0)) {
-			flags = AI_NUMERICHOST;
-			isbroadcast = 1;
-			servername = "255.255.255.255";
-		} else if ((ipversion == 6) &&
-			   (strcmp(servername, "all") == 0)) {
-			flags = AI_NUMERICHOST;
-			servername = "FF02::1:2";
-		} else if ((ipversion == 6) &&
-			   (strcmp(servername, "servers") == 0)) {
-			flags = AI_NUMERICHOST;
-			servername = "FF05::1:3";
-		}
-	}
-
-	/* handle the local '-l' address/interface */
-	if (localname != NULL) {
-		/* given */
-		getlocaladdr();
-		if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-			if (isinterface)
-				printf("interface='%s'\n", localname);
-			else
-				printf("local-addr='%s'\n", localname);
-		}
-		/* get the not given server from it */
-		if (servername == NULL) {
-			if (isinterface && (ipversion == 4)) {
-				flags = AI_NUMERICHOST;
-				isbroadcast = 1;
-				servername = "255.255.255.255";
-			} else if (isinterface && (ipversion == 6)) {
-				flags = AI_NUMERICHOST;
-				servername = "FF02::1:2";
-			} else {
-				fprintf(stderr,
-					"without an interface "
-					"server is required\n");
-				usage();
-				exit(2);
-			}
-		}
-	} else if (servername == NULL) {
-		fprintf(stderr, "without an interface server is required\n");
-		usage();
-		exit(2);
-	}
-	/* get the server socket address */
-	getserveraddr(flags);
-	/* finish local/server socket address stuff and print it */
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL))
-		printf("server='%s'\n", servername);
-	if (localname == NULL)
-		getlocal();
-	if ((diags != NULL) && (strchr(diags, 'a') != NULL)) {
-		char addr[NI_MAXHOST];
-
-		ret = getnameinfo((struct sockaddr *) &localaddr,
-				  sizeof(localaddr),
-				  addr,
-				  NI_MAXHOST,
-				  NULL,
-				  0,
-				  NI_NUMERICHOST);
-		if (ret != 0) {
-			fprintf(stderr,
-				"can't get the local address: %s\n",
-				gai_strerror(ret));
-			exit(1);
-		}
-		printf("local address='%s'\n", addr);
-	}
-
-	/* initialize exchange structures */
-	inits();
-
-	/* get the socket descriptor and template(s) */
-	if (ipversion == 4) {
-		getsock4();
-		if (templatefile[0] == NULL)
-			build_template_discover4();
-		else
-			get_template_discover4();
-		if (simple == 0) {
-			if (templatefile[1] == NULL)
-				build_template_request4();
-			else
-				get_template_request4();
-		}
-	} else {
-		getsock6();
-		if (duid_prefix != NULL) {
-			if (templatefile[0] == NULL)
-				build_template_solicit6();
-			else
-				get_template_solicit6();
-			if (simple == 0) {
-				if (templatefile[1] == NULL)
-					build_template_request6();
-				else
-					get_template_request6();
-			}
-		}
-	}
-	/* sanity check */
-	if ((unsigned) sock > FD_SETSIZE) {
-		fprintf(stderr, "socket descriptor (%d) too large?!\n", sock);
-		exit(1);
-	}
-	/* make the socket descriptor not blocking */
-	flags = fcntl(sock, F_GETFL, 0);
-	if (flags < 0) {
-		perror("fcntl(F_GETFL)");
-		exit(1);
-	}
-	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
-		perror("fcntl(F_SETFL)");
-		exit(1);
-	}
-
-	/* wrapped start */
-	if (wrapped != NULL) {
-		pid_t pid;
-
-		(void) signal(SIGCHLD, reapchild);
-		pid = fork();
-		if (pid < 0) {
-			perror("fork");
-			exit(1);
-		} else if (pid == 0)
-			(void) execlp(wrapped, "start", (char *) NULL);
-	}
-
-	/* boot is done! */
-	if (clock_gettime(CLOCK_REALTIME, &boot) < 0) {
-		perror("clock_gettime(boot)");
-		exit(1);
-	}
-
-	/* compute the next intermediate reporting date */
-	if (report != 0) {
-		dreport.tv_sec = boot.tv_sec + report;
-		dreport.tv_nsec = boot.tv_nsec;
-	}
-
-	/* compute the DUID (the current date is needed) */
-	if ((ipversion == 6) && (duid_prefix == NULL)) {
-		uint32_t curdate;
-
-		duid_length = 14;
-		duid_prefix = (uint8_t *) malloc(duid_length);
-		if (duid_prefix == NULL) {
-			perror("malloc(duid)");
-			exit(1);
-		}
-		duid_prefix[0] = DHCP6_DUID_LLT >> 8;
-		duid_prefix[1] = DHCP6_DUID_LLT;
-		duid_prefix[2] = DHCP_HTYPE_ETHER >> 8;
-		duid_prefix[3] = DHCP_HTYPE_ETHER;
-		curdate = htonl(boot.tv_sec - DHCP6_DUID_EPOCH);
-		memcpy(duid_prefix + 4, &curdate, 4);
-		memcpy(duid_prefix + 8, mac_prefix, 6);
-		/* the DUID is in template(s) */
-		if (templatefile[0] == NULL)
-			build_template_solicit6();
-		else
-			get_template_solicit6();
-		if (simple == 0) {
-			if (templatefile[1] == NULL)
-				build_template_request6();
-			else
-				get_template_request6();
-		}
-	}
-		
-	/* seed the random generator */
-	if (seeded == 0)
-		seed = (unsigned int) (boot.tv_sec + boot.tv_nsec);
-	srandom(seed);
-
-	/* preload the server with at least one packet */
-	compsend = preload + 1;
-	for (i = 0; i <= preload; i++) {
-		if (ipversion == 4)
-			ret = send4();
-		else
-			ret = send6();
-		if (ret < 0) {
-			/* failure at the first packet is fatal */
-			if (i == 0) {
-				fprintf(stderr,
-					"initial send failed: %s\n",
-					strerror(-ret));
-				exit(1);
-			}
-			if ((errno == EAGAIN) ||
-			    (errno == EWOULDBLOCK) ||
-			    (errno == ENOBUFS) ||
-			    (errno == ENOMEM))
-				locallimit++;
-			fprintf(stderr, "preload send: %s\n", strerror(-ret));
-			break;
-		}
-	}
-
-	/* required only before the interrupted flag check */
-	(void) signal(SIGINT, interrupt);
-
-	/* main loop */
-	for (;;) {
-		struct timespec now, ts;
-		fd_set rfds;
-
-		/* immediate loop exit conditions */
-		if (interrupted) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("interrupted\n");
-			break;
-		}
-		if (fatal) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("got a fatal error\n");
-			break;
-		}
-
-		/* get the date and use it */
-		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-			perror("clock_gettime(now)");
-			fatal = 1;
-			continue;
-		}
-		if ((period != 0) &&
-		    ((boot.tv_sec + period < now.tv_sec) ||
-		     ((boot.tv_sec + period == now.tv_sec) &&
-		      (boot.tv_nsec < now.tv_nsec)))) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached test-period\n");
-			break;
-		}
-		if ((report != 0) &&
-		    ((dreport.tv_sec < now.tv_sec) ||
-		     ((dreport.tv_sec == now.tv_sec) &&
-		      (dreport.tv_nsec < now.tv_nsec))))
-			reporting();
-
-		/* compute the delay for the next send */
-		due = last;
-		if (rate == 1)
-			due.tv_sec += 1;
-		else if (rate != 0)
-			due.tv_nsec += 1010000000 / rate;
-		else
-			due.tv_nsec += 1;
-		while (due.tv_nsec >= 1000000000) {
-			due.tv_sec += 1;
-			due.tv_nsec -= 1000000000;
-		}
-		ts = due;
-		ts.tv_sec -= now.tv_sec;
-		ts.tv_nsec -= now.tv_nsec;
-		while (ts.tv_nsec < 0) {
-			ts.tv_sec -= 1;
-			ts.tv_nsec += 1000000000;
-		}
-		/* the send was already due? */
-		if (ts.tv_sec < 0) {
-			ts.tv_sec = ts.tv_nsec = 0;
-			latesent++;
-		}
-
-		/* pselect() */
-		FD_ZERO(&rfds);
-		FD_SET(sock, &rfds);
-		ret = pselect(sock + 1, &rfds, NULL, NULL, &ts, NULL);
-		if (ret < 0) {
-			if (errno == EINTR)
-				continue;
-			perror("pselect");
-			fatal = 1;
-			continue;
-		}
-
-		/* packet(s) to receive */
-		while (ret == 1) {
-			if (ipversion == 4)
-				receive4();
-			else
-				receive6();
-			if (recv(sock, ibuf, sizeof(ibuf), MSG_PEEK) <= 0)
-				ret = 0;
-			else
-				multrcvd++;
-		}
-		if (fatal)
-			continue;
-
-		/* check receive loop exit conditions */
-		if ((numreq[0] != 0) && ((int) xscount0 >= numreq[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached num-request0\n");
-			break;
-		}
-		if ((numreq[1] != 0) && ((int) xscount2 >= numreq[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached num-request2\n");
-			break;
-		}
-		if ((maxdrop[0] != 0) &&
-		    ((int) (xscount0 - xrcount0) > maxdrop[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop%s (absolute)\n",
-				       simple != 0 ? "" : "0");
-			break;
-		}
-		if ((maxdrop[1] != 0) &&
-		    ((int) (xscount2 - xrcount2) > maxdrop[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop2 (absolute)\n");
-			break;
-		}
-		if ((maxpdrop[0] != 0.) &&
-		    (xscount0 > 10) &&
-		    (((100. * (xscount0 - xrcount0)) / xscount0)
-			> maxpdrop[0])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop%s (percent)\n",
-				       simple != 0 ? "" : "0");
-			break;
-		}
-		if ((maxpdrop[1] != 0.) &&
-		    (xscount2 > 10) &&
-		    (((100. * (xscount2 - xrcount2)) / xscount2)
-			> maxpdrop[1])) {
-			if ((diags != NULL) && (strchr(diags, 'e') != NULL))
-				printf("reached max-drop2 (percent)\n");
-			break;
-		}
-
-		/* compute how many packets to send */
-		if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-			perror("clock_gettime(now2)");
-			fatal = 1;
-			continue;
-		}
-		if ((now.tv_sec > due.tv_sec) ||
-		    ((now.tv_sec == due.tv_sec) &&
-		     (now.tv_nsec >= due.tv_nsec))) {
-			double tosend;
-
-			if (rate != 0) {
-				tosend = (now.tv_nsec - due.tv_nsec) / 1e9;
-				tosend += now.tv_sec - due.tv_sec;
-				tosend *= rate;
-				tosend += 1;
-				if (tosend > (double) aggressivity)
-					i = aggressivity;
-				else
-					i = (int) tosend;
-			} else
-				i = aggressivity;
-			compsend += i;
-			/* send packets */
-			for (;;) {
-				if (ipversion == 4)
-					ret = send4();
-				else
-					ret = send6();
-				if (ret < 0) {
-					if ((errno == EAGAIN) ||
-					    (errno == EWOULDBLOCK) ||
-					    (errno == ENOBUFS) ||
-					    (errno == ENOMEM))
-						locallimit++;
-					fprintf(stderr,
-						"send: %s\n", strerror(-ret));
-					break;
-				}
-				i--;
-				if (i == 0)
-					break;
-				/* check for late packets to receive */
-				if (recv(sock, ibuf, sizeof(ibuf),
-					 MSG_PEEK) > 0) {
-					latercvd++;
-					if (ipversion == 4)
-						receive4();
-					else
-						receive6();
-				}
-			}
-		} else
-			/* there was no packet to send */
-			shortwait++;
-	}
-
-	/* after main loop: finished */
-	if (clock_gettime(CLOCK_REALTIME, &finished) < 0)
-		perror("clock_gettime(finished)");
-
-	/* wrapped stop */
-	if (wrapped != NULL) {
-		pid_t pid;
-
-		pid = fork();
-		if (pid == 0)
-			(void) execlp(wrapped, "stop", (char *) NULL);
-	}
-
-	/* main statictics */
-	if (xscount2 == 0)
-		printf("sent: %llu, received: %llu (drops: %lld)\n",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xrcount0,
-		       (long long) (xscount0 - xrcount0));
-	else
-		printf("sent: %llu/%llu, received: %llu/%llu "
-		       "(drops: %lld/%lld)\n",
-		       (unsigned long long) xscount0,
-		       (unsigned long long) xscount2,
-		       (unsigned long long) xrcount0,
-		       (unsigned long long) xrcount2,
-		       (long long) (xscount0 - xrcount0),
-		       (long long) (xscount2 - xrcount2));
-	printf("tooshort: %llu, orphans: %llu, local limits: %llu\n",
-	       (unsigned long long) tooshort,
-	       (unsigned long long) orphans,
-	       (unsigned long long) locallimit);
-
-	/* print the rate */
-	if (finished.tv_sec != 0) {
-		double dall, erate;
-
-		dall = (finished.tv_nsec - boot.tv_nsec) / 1e9;
-		dall += finished.tv_sec - boot.tv_sec;
-		erate = xrcount0 / dall;
-		if (rate != 0)
-			printf("rate: %f (expected %d)\n", erate, rate);
-		else
-			printf("rate: %f\n", erate);
-	}
-
-	/* rate processing instrumentation */
-	if ((diags != NULL) && (strchr(diags, 'i') != NULL)) {
-		printf("latesent: %llu, compsend: %llu, shortwait: %llu\n"
-		       "multrcvd: %llu, latercvd: %llu, collected:%llu/%llu\n",
-		       (unsigned long long) latesent,
-		       (unsigned long long) compsend,
-		       (unsigned long long) shortwait,
-		       (unsigned long long) multrcvd,
-		       (unsigned long long) latercvd,
-		       (unsigned long long) collected[0],
-		       (unsigned long long) collected[1]);
-	}
-
-	/* round-time trip statistics */
-	if (xrcount2 != 0) {
-		double avg0, avg2, stddev0, stddev2;
-		
-		avg0 = dsum0 / xrcount0;
-		avg2 = dsum2 / xrcount2;
-		stddev0 = sqrt(dsumsq0 / xrcount0 - avg0 * avg0);
-		stddev2 = sqrt(dsumsq2 / xrcount2 - avg2 * avg2);
-		printf("RTT0: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       dmin0 * 1e3, avg0 * 1e3, dmax0 * 1e3, stddev0 * 1e3);
-		printf("RTT2: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       dmin2 * 1e3, avg2 * 1e3, dmax2 * 1e3, stddev2 * 1e3);
-	} else if (xrcount0 != 0) {
-		double avg, stddev;
-		
-		avg = dsum0 / xrcount0;
-		stddev = sqrt(dsumsq0 / xrcount0 - avg * avg);
-		printf("RTT%s: min/avg/max/stddev:  %.3f/%.3f/%.3f/%.3f ms\n",
-		       simple != 0 ? "" : "0",
-		       dmin0 * 1e3, avg * 1e3, dmax0 * 1e3, stddev * 1e3);
-	}
-
-	/* (first) server-ID option content */
-	if ((diags != NULL) && (strchr(diags, 's') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
-		struct exchange *x;
-		size_t n;
-
-		printf("server-id: ");
-		x = ISC_TAILQ_FIRST(&xrcvd0);
-		if (ipversion == 4)
-			n = 2;
-		else
-			n = 4;
-		for (; n < x->sidlen; n++)
-			printf("%02hhx", x->sid[n]);
-		printf("\n");
-	}
-
-	/* all time-stamps */
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0)) {
-		struct exchange *x;
-
-		printf("\n\n");
-		ISC_TAILQ_FOREACH(x, &xrcvd0, gchain)
-			printf("%ld.%09ld %ld.%09ld\n",
-			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
-			       (long) x->ts1.tv_sec, x->ts1.tv_nsec);
-
-	}
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd2)) {
-		struct exchange *x;
-
-		printf("--\n");
-		ISC_TAILQ_FOREACH(x, &xrcvd2, gchain)
-			printf("%ld.%09ld %ld.%09ld %ld.%09ld %ld.%09ld\n",
-			       (long) x->ts0.tv_sec, x->ts0.tv_nsec,
-			       (long) x->ts1.tv_sec, x->ts1.tv_nsec,
-			       (long) x->ts2.tv_sec, x->ts2.tv_nsec,
-			       (long) x->ts3.tv_sec, x->ts3.tv_nsec);
-
-	}
-	if ((diags != NULL) && (strchr(diags, 't') != NULL) &&
-	    !ISC_TAILQ_EMPTY(&xrcvd0))
-		printf("\n\n");
-
-	/* template(s) */
-	if ((diags != NULL) && (strchr(diags, 'T') != NULL)) {
-		size_t n;
-
-		if (ipversion == 4) {
-			printf("length = %zu\n", length_discover4);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_discover4);
-			printf("content:\n");
-			for (n = 0; n < length_discover4; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_discover4[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			if ((n & 15) != 15)
-				printf("\n");
-			if (simple != 0)
-				goto doneT;
-			printf("--\n");
-			printf("length = %zu\n", length_request4);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_request4);
-			if (elapsed_request4 > 0)
-				printf("secs offset = %zu\n",
-				       elapsed_request4);
-			printf("server-id offset = %zu\n", serverid_request4);
-			printf("server-id length = %d\n", DHCP_OPTLEN_SRVID);
-			printf("content:\n");
-			printf("requested-ip-address offset = %zu\n",
-			       reqaddr_request4);
-			printf("requested-ip-address length = %d\n", 4);
-			for (n = 0; n < length_request4; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_request4[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			printf("\n");
-		} else {
-			printf("length = %zu\n", length_solicit6);
-			printf("xid offset = %d\n", DHCP6_OFF_XID);
-			printf("xid length = 3\n");
-			printf("random offset = %zu\n", random_solicit6);
-			for (n = 0; n < length_solicit6; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_solicit6[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			if ((n & 15) != 15)
-				printf("\n");
-			if (simple != 0)
-				goto doneT;
-			printf("--\n");
-			printf("length = %zu\n", length_request6);
-			printf("xid offset = %d\n", DHCP_OFF_XID);
-			printf("xid length = 4\n");
-			printf("random offset = %zu\n", random_request6);
-			if (elapsed_request6 > 0)
-				printf("secs offset = %zu\n",
-				       elapsed_request6);
-			printf("server-id offset = %zu\n", serverid_request6);
-			printf("content:\n");
-			printf("requested-ip-address offset = %zu\n",
-			       reqaddr_request6);
-			for (n = 0; n < length_request6; n++) {
-				printf("%s%02hhx",
-				       (n & 15) == 0 ? "" : " ",
-				       template_request6[n]);
-				if ((n & 15) == 15)
-					printf("\n");
-			}
-			printf("\n");
-		}
-	}
-    doneT:
-
-	/* compute the exit code (and exit) */
-	if (fatal)
-		exit(1);
-	else if ((xscount0 == xrcount0) && (xscount2 == xrcount2))
-		exit(0);
-	else
-		exit(3);
-}
-
-#endif  /* HAVE_GETIFADDRS */
diff --git a/tests/tools/perfdhcp/stats_mgr.h b/tests/tools/perfdhcp/stats_mgr.h
index a8dfa8b..0c2b68c 100644
--- a/tests/tools/perfdhcp/stats_mgr.h
+++ b/tests/tools/perfdhcp/stats_mgr.h
@@ -73,14 +73,19 @@ public:
         /// \brief Increment operator.
         const CustomCounter& operator++() {
             ++counter_;
-            return(*this);
+            return (*this);
         }
 
         /// \brief Increment operator.
         const CustomCounter& operator++(int) {
             CustomCounter& this_counter(*this);
             operator++();
-            return(this_counter);
+            return (this_counter);
+        }
+
+        const CustomCounter& operator+=(int val) {
+            counter_ += val;
+            return (*this);
         }
 
         /// \brief Return counter value.
@@ -251,24 +256,30 @@ public:
         /// \brief Constructor
         ///
         /// \param xchg_type exchange type
+        /// \param drop_time maximum time elapsed before packet is
+        /// assumed dropped. Negative value disables it.
         /// \param archive_enabled if true packets archive mode is enabled.
         /// In this mode all packets are stored throughout the test execution.
-        ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
+        ExchangeStats(const ExchangeType xchg_type,
+                      const double drop_time,
+                      const bool archive_enabled)
             : xchg_type_(xchg_type),
-            sent_packets_(),
-            rcvd_packets_(),
-            archived_packets_(),
-            archive_enabled_(archive_enabled),
-            min_delay_(std::numeric_limits<double>::max()),
-            max_delay_(0.),
-            sum_delay_(0.),
-            sum_delay_squared_(0.),
-            orphans_(0),
-            unordered_lookup_size_sum_(0),
-            unordered_lookups_(0),
-            ordered_lookups_(0),
-            sent_packets_num_(0),
-            rcvd_packets_num_(0)
+              sent_packets_(),
+              rcvd_packets_(),
+              archived_packets_(),
+              archive_enabled_(archive_enabled),
+              drop_time_(drop_time),
+              min_delay_(std::numeric_limits<double>::max()),
+              max_delay_(0.),
+              sum_delay_(0.),
+              sum_delay_squared_(0.),
+              orphans_(0),
+              collected_(0),
+              unordered_lookup_size_sum_(0),
+              unordered_lookups_(0),
+              ordered_lookups_(0),
+              sent_packets_num_(0),
+              rcvd_packets_num_(0)
         {
             next_sent_ = sent_packets_.begin();
         }
@@ -370,6 +381,8 @@ public:
         /// not found
         boost::shared_ptr<T>
         matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
+            using namespace boost::posix_time;
+
             if (!rcvd_packet) {
                 isc_throw(BadValue, "Received packet is null");
             }
@@ -432,6 +445,20 @@ public:
                             sent_packets_.template project<0>(it);
                         break;
                     }
+                    ptime now = microsec_clock::universal_time();
+                    ptime packet_time = (*it)->getTimestamp();
+                    time_period packet_period(packet_time, now);
+                    if (!packet_period.is_null()) {
+                        double period_fractional =
+                            packet_period.length().total_seconds() +
+                            (static_cast<double>(packet_period.length().fractional_seconds())
+                             / packet_period.length().ticks_per_second());
+                        if (drop_time_ > 0 &&
+                            (period_fractional > drop_time_)) {
+                            eraseSent(sent_packets_.template project<0>(it));
+                            ++collected_;
+                        }
+                    }
                 }
             }
 
@@ -510,6 +537,17 @@ public:
         /// \return number of orphant received packets.
         uint64_t getOrphans() const { return(orphans_); }
 
+        /// \brief Return number of garbage collected packets.
+        ///
+        /// Method returns number of garbage collected timed out
+        /// packets. Packet is assumed timed out when duration
+        /// between sending it to server and receiving server's
+        /// response is greater than value specified with -d<value>
+        /// command line argument.
+        ///
+        /// \return number of garbage collected packets.
+        uint64_t getCollectedNum() const { return(collected_); }
+
         /// \brief Return average unordered lookup set size.
         ///
         /// Method returns average unordered lookup set size.
@@ -603,9 +641,10 @@ public:
                      << "avg delay: " << getAvgDelay() * 1e3 << " ms" << endl
                      << "max delay: " << getMaxDelay() * 1e3 << " ms" << endl
                      << "std deviation: " << getStdDevDelay() * 1e3 << " ms"
-                     << endl;
+                     << endl
+                     << "collected packets: " << getCollectedNum() << endl;
             } catch (const Exception& e) {
-                cout << "Unavailable! No packets received." << endl;
+                cout << "Delay summary unavailable! No packets received." << endl;
             }
         }
 
@@ -644,7 +683,7 @@ public:
                     idx.equal_range(hashTransid(rcvd_packet));
                 for (PktListTransidHashIterator it_archived = p.first;
                      it_archived != p.second;
-                     ++it) {
+                     ++it_archived) {
                     if ((*it_archived)->getTransid() ==
                         rcvd_packet->getTransid()) {
                         boost::shared_ptr<T> sent_packet = *it_archived;
@@ -733,6 +772,10 @@ public:
         /// to keep all packets archived throughout the test.
         bool archive_enabled_;
 
+        /// Maxmimum time elapsed between sending and receiving packet
+        /// before packet is assumed dropped.
+        double drop_time_;
+
         double min_delay_;             ///< Minimum delay between sent
                                        ///< and received packets.
         double max_delay_;             ///< Maximum delay between sent
@@ -744,6 +787,8 @@ public:
 
         uint64_t orphans_;   ///< Number of orphant received packets.
 
+        uint64_t collected_; ///< Number of garbage collected packets.
+
         /// Sum of unordered lookup sets. Needed to calculate mean size of
         /// lookup set. It is desired that number of unordered lookups is
         /// minimal for performance reasons. Tracking number of lookups and
@@ -786,7 +831,6 @@ public:
     /// archive mode is enabled.
     StatsMgr(const bool archive_enabled = false) :
         exchanges_(),
-        custom_counters_(),
         archive_enabled_(archive_enabled),
         boot_time_(boost::posix_time::microsec_clock::universal_time()) {
     }
@@ -798,13 +842,18 @@ public:
     /// type.
     ///
     /// \param xchg_type exchange type.
+    /// \param drop_time maximum time elapsed before packet is
+    /// assumed dropped. Negative value disables it.
     /// \throw isc::BadValue if exchange of specified type exists.
-    void addExchangeStats(const ExchangeType xchg_type) {
+    void addExchangeStats(const ExchangeType xchg_type,
+                          const double drop_time = -1) {
         if (exchanges_.find(xchg_type) != exchanges_.end()) {
             isc_throw(BadValue, "Exchange of specified type already added.");
         }
         exchanges_[xchg_type] =
-            ExchangeStatsPtr(new ExchangeStats(xchg_type, archive_enabled_));
+            ExchangeStatsPtr(new ExchangeStats(xchg_type,
+                                               drop_time,
+                                               archive_enabled_));
     }
 
     /// \brief Add named custom uint64 counter.
@@ -824,6 +873,20 @@ public:
             CustomCounterPtr(new CustomCounter(long_name));
     }
 
+    /// \brief Check if any packet drops occured.
+    ///
+    // \return true, if packet drops occured.
+    bool droppedPackets() const {
+        for (ExchangesMapIterator it = exchanges_.begin();
+             it != exchanges_.end();
+             ++it) {
+            if (it->second->getDroppedPacketsNum() > 0) {
+                return (true);
+            }
+        }
+        return (false);
+    }
+
     /// \brief Return specified counter.
     ///
     /// Method returns specified counter.
@@ -844,11 +907,14 @@ public:
     ///
     /// Increement counter value by one.
     ///
-    /// \param counter_key key poitinh to the counter in the counters map.
+    /// \param counter_key key poiting to the counter in the counters map.
+    /// \param value value to increment counter by.
     /// \return pointer to specified counter after incrementation.
-    const CustomCounter& incrementCounter(const std::string& counter_key) {
+    const CustomCounter& incrementCounter(const std::string& counter_key,
+                                          const uint64_t value = 1) {
         CustomCounterPtr counter = getCounter(counter_key);
-        return(++(*counter));
+        *counter += value;
+        return (*counter);
     }
 
     /// \brief Adds new packet to the sent packets list.
@@ -1041,6 +1107,22 @@ public:
         return(xchg_stats->getDroppedPacketsNum());
     }
 
+    /// \brief Return number of garbage collected packets.
+    ///
+    /// Method returns number of garbage collected timed out
+    /// packets. Packet is assumed timed out when duration
+    /// between sending it to server and receiving server's
+    /// response is greater than value specified with -d<value>
+    /// command line argument.
+    ///
+    /// \throw isc::BadValue if invalid exchange type specified.
+    /// \return number of garbage collected packets.
+    uint64_t getCollectedNum(const ExchangeType xchg_type) const {
+        ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+        return(xchg_stats->getCollectedNum());
+    }
+
+
     /// \brief Get time period since the start of test.
     ///
     /// Calculate dna return period since the test start. This
@@ -1128,7 +1210,7 @@ public:
             stream_rcvd << sep << it->second->getRcvdPacketsNum();
             stream_drops << sep << it->second->getDroppedPacketsNum();
         }
-        std::cout << "sent: " << stream_sent.str() 
+        std::cout << "sent: " << stream_sent.str()
                   << "; received: " << stream_rcvd.str()
                   << "; drops: " << stream_drops.str()
                   << std::endl;
diff --git a/tests/tools/perfdhcp/templates/Makefile.am b/tests/tools/perfdhcp/templates/Makefile.am
index 1eee19c..33930a7 100644
--- a/tests/tools/perfdhcp/templates/Makefile.am
+++ b/tests/tools/perfdhcp/templates/Makefile.am
@@ -1,8 +1,8 @@
 SUBDIRS = .
 
-# The test1.hex and test2.hex are created by the TestControl.PacketTemplates
+# The test[1-5].hex are created by the TestControl.PacketTemplates
 # unit tests and have to be removed.
-CLEANFILES = test1.hex test2.hex
+CLEANFILES = test1.hex test2.hex test3.hex test4.hex test5.hex
 
 perfdhcpdir = $(pkgdatadir)
 perfdhcp_DATA = discover-example.hex request4-example.hex
diff --git a/tests/tools/perfdhcp/test_control.cc b/tests/tools/perfdhcp/test_control.cc
index c154cf9..b7c3cd6 100644
--- a/tests/tools/perfdhcp/test_control.cc
+++ b/tests/tools/perfdhcp/test_control.cc
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <unistd.h>
 #include <signal.h>
+#include <sys/wait.h>
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 
@@ -378,6 +379,16 @@ TestControl::generateDuid(uint8_t& randomized) const {
     return (duid);
 }
 
+int
+TestControl::getElapsedTimeOffset() const {
+    int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
+    if (CommandOptions::instance().getElapsedTimeOffset() > 0) {
+        elp_offset = CommandOptions::instance().getElapsedTimeOffset();
+    }
+    return (elp_offset);
+}
+
 template<class T>
 uint32_t
 TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
@@ -438,6 +449,26 @@ TestControl::getNextExchangesNum() const {
     return (0);
 }
 
+int
+TestControl::getRandomOffset(const int arg_idx) const {
+    int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
+    if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
+        rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
+    }
+    return (rand_offset);
+}
+
+int
+TestControl::getRequestedIpOffset() const {
+    int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
+    if (CommandOptions::instance().getRequestedIpOffset() > 0) {
+        rip_offset = CommandOptions::instance().getRequestedIpOffset();
+    }
+    return (rip_offset);
+}
+
 uint64_t
 TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
     uint8_t ip_version = CommandOptions::instance().getIpVersion();
@@ -458,6 +489,16 @@ TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
             getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
 }
 
+int
+TestControl::getServerIdOffset() const {
+    int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
+    if (CommandOptions::instance().getServerIdOffset() > 0) {
+        srvid_offset = CommandOptions::instance().getServerIdOffset();
+    }
+    return (srvid_offset);
+}
+
 TestControl::TemplateBuffer
 TestControl::getTemplateBuffer(const size_t idx) const {
     if (template_buffers_.size() > idx) {
@@ -466,6 +507,24 @@ TestControl::getTemplateBuffer(const size_t idx) const {
     isc_throw(OutOfRange, "invalid buffer index");
 }
 
+int
+TestControl::getTransactionIdOffset(const int arg_idx) const {
+    int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
+        DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
+    if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
+        xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
+    }
+    return (xid_offset);
+}
+
+void
+TestControl::handleChild(int) {
+    int status = 0;
+    while (wait3(&status, WNOHANG, NULL) > 0) {
+        // continue
+    }
+}
+
 void
 TestControl::handleInterrupt(int) {
     interrupted_ = true;
@@ -473,6 +532,8 @@ TestControl::handleInterrupt(int) {
 
 void
 TestControl::initPacketTemplates() {
+    template_packets_v4_.clear();
+    template_packets_v6_.clear();
     template_buffers_.clear();
     CommandOptions& options = CommandOptions::instance();
     std::vector<std::string> template_files = options.getTemplateFiles();
@@ -485,20 +546,28 @@ TestControl::initPacketTemplates() {
 void
 TestControl::initializeStatsMgr() {
     CommandOptions& options = CommandOptions::instance();
+    // Check if packet archive mode is required. If user
+    // requested diagnostics option -x t we have to enable
+    // it so as StatsMgr preserves all packets.
+    const bool archive_mode = testDiags('t') ? true : false;
     if (options.getIpVersion() == 4) {
         stats_mgr4_.reset();
-        stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
-        stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
+        stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
+        stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
+                                      options.getDropTime()[0]);
         if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
-            stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
+            stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
+                                          options.getDropTime()[1]);
         }
 
     } else if (options.getIpVersion() == 6) {
         stats_mgr6_.reset();
-        stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
-        stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
+        stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
+        stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
+                                      options.getDropTime()[0]);
         if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
-            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
+            stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
+                                          options.getDropTime()[1]);
         }
     }
     if (testDiags('i')) {
@@ -506,12 +575,12 @@ TestControl::initializeStatsMgr() {
             stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
             stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
             stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
-            //            stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
+            stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
         } else if (options.getIpVersion() == 6) {
             stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
             stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
             stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
-            //            stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
+            stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
         }
     }
 }
@@ -605,6 +674,47 @@ TestControl::openSocket() const {
 }
 
 void
+TestControl::sendPackets(const TestControlSocket& socket,
+                         const uint64_t packets_num,
+                         const bool preload /* = false */) {
+    CommandOptions& options = CommandOptions::instance();
+    for (uint64_t i = packets_num; i > 0; --i) {
+        if (options.getIpVersion() == 4) {
+            // No template packets means that no -T option was specified.
+            // We have to build packets ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendDiscover4(socket, preload);
+            } else {
+                // @todo add defines for packet type index that can be
+                // used to access template_buffers_.
+                sendDiscover4(socket, template_buffers_[0], preload);
+            }
+        } else {
+            // No template packets means that no -T option was specified.
+            // We have to build packets ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendSolicit6(socket, preload);
+            } else {
+                // @todo add defines for packet type index that can be
+                // used to access template_buffers_.
+                sendSolicit6(socket, template_buffers_[0], preload);
+            }
+        }
+        // If we preload server we don't want to receive any packets.
+        if (!preload) {
+            uint64_t latercvd = receivePackets(socket);
+            if (testDiags('i')) {
+                if (options.getIpVersion() == 4) {
+                    stats_mgr4_->incrementCounter("latercvd", latercvd);
+                } else if (options.getIpVersion() == 6) {
+                    stats_mgr6_->incrementCounter("latercvd", latercvd);
+                }
+            }
+        }
+    }
+}
+
+void
 TestControl::printDiagnostics() const {
     CommandOptions& options = CommandOptions::instance();
     if (testDiags('a')) {
@@ -620,6 +730,75 @@ TestControl::printDiagnostics() const {
 }
 
 void
+TestControl::printTemplate(const uint8_t packet_type) const {
+    std::string hex_buf;
+    int arg_idx = 0;
+    if (CommandOptions::instance().getIpVersion() == 4) {
+        if (packet_type == DHCPREQUEST) {
+            arg_idx = 1;
+        }
+        std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
+            template_packets_v4_.find(packet_type);
+        if ((pkt_it != template_packets_v4_.end()) &&
+            pkt_it->second) {
+            const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+            const char* out_buf_data =
+                static_cast<const char*>(out_buf.getData());
+            std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+            hex_buf = vector2Hex(buf);
+        }
+    } else if (CommandOptions::instance().getIpVersion() == 6) {
+        if (packet_type == DHCPV6_REQUEST) {
+            arg_idx = 1;
+        }
+        std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
+            template_packets_v6_.find(packet_type);
+        if (pkt_it != template_packets_v6_.end() &&
+            pkt_it->second) {
+            const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
+            const char* out_buf_data =
+                static_cast<const char*>(out_buf.getData());
+            std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
+            hex_buf = vector2Hex(buf);
+        }
+    }
+    std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
+    std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
+    if (arg_idx > 0) {
+        std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
+        std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
+        std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
+    }
+
+    std::cout << "contents: " << std::endl;
+    int line_len = 32;
+    int i = 0;
+    while (line_len == 32) {
+        if (hex_buf.length() - i < 32) {
+            line_len = hex_buf.length() - i;
+        };
+        if (line_len > 0) {
+            std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
+                      << "   " << hex_buf.substr(i, line_len) << std::endl;
+        }
+        i += 32;
+    }
+    std::cout << std::endl;
+}
+
+void
+TestControl::printTemplates() const {
+    CommandOptions& options = CommandOptions::instance();
+    if (options.getIpVersion() == 4) {
+        printTemplate(DHCPDISCOVER);
+        printTemplate(DHCPREQUEST);
+    } else if (options.getIpVersion() == 6) {
+        printTemplate(DHCPV6_SOLICIT);
+        printTemplate(DHCPV6_REQUEST);
+    }
+}
+
+void
 TestControl::printRate() const {
     double rate = 0;
     CommandOptions& options = CommandOptions::instance();
@@ -634,8 +813,8 @@ TestControl::printRate() const {
     }
     std::cout << "***Rate statistics***" << std::endl;
     if (options.getRate() > 0) {
-        std::cout << "Rate: " << rate << ", expected rate: "
-                  << options.getRate() << std::endl << std::endl;
+        std::cout << "Rate: " << rate << " exchanges/second, expected rate: "
+                  << options.getRate() << " exchanges/second" <<  std::endl << std::endl;
     } else {
         std::cout << "Rate: " << rate << std::endl << std::endl;
     }
@@ -705,21 +884,38 @@ TestControl::readPacketTemplate(const std::string& file_name) {
     if (!temp_file.is_open()) {
         isc_throw(BadValue, "unable to open template file " << file_name);
     }
-    std::ifstream::pos_type temp_size = temp_file.tellg();
-    if (temp_size % 2 != 0) {
+    // Read template file contents.
+    std::streampos temp_size = temp_file.tellg();
+    if (temp_size == std::streampos(0)) {
         temp_file.close();
-        isc_throw(BadValue, "odd number of digits in template file");
+        isc_throw(OutOfRange, "the template file " << file_name << " is empty");
     }
     temp_file.seekg(0, ios::beg);
-    std::vector<char> hex_digits(temp_size);
-    std::vector<uint8_t> binary_stream;
-    temp_file.read(&hex_digits[0], temp_size);
+    std::vector<char> file_contents(temp_size);
+    temp_file.read(&file_contents[0], temp_size);
     temp_file.close();
-    for (int i = 0; i < hex_digits.size(); i += 2) {
-        if (!isxdigit(hex_digits[i]) || !isxdigit(hex_digits[i+1])) {
-            isc_throw(BadValue, "the '" << hex_digits[i] << hex_digits[i+1]
-                      << "' is not hexadecimal digit");
+    // Spaces are allowed so we have to strip the contents
+    // from them. In the same time we want to make sure that
+    // apart from spaces the file contains hexadecimal digits
+    // only.
+    std::vector<char> hex_digits;
+    for (int i = 0; i < file_contents.size(); ++i) {
+        if (isxdigit(file_contents[i])) {
+            hex_digits.push_back(file_contents[i]);
+        } else if (!isxdigit(file_contents[i]) &&
+                   !isspace(file_contents[i])) {
+            isc_throw(BadValue, "the '" << file_contents[i] << "' is not a"
+                      " heaxadecimal digit");
         }
+    }
+    // Expect even number of digits.
+    if (hex_digits.size() % 2 != 0) {
+        isc_throw(OutOfRange, "odd number of digits in template file");
+    } else if (hex_digits.size() == 0) {
+        isc_throw(OutOfRange, "template file " << file_name << " is empty");
+    }
+    std::vector<uint8_t> binary_stream;
+    for (int i = 0; i < hex_digits.size(); i += 2) {
         stringstream s;
         s << "0x" << hex_digits[i] << hex_digits[i+1];
         int b;
@@ -777,7 +973,7 @@ TestControl::processReceivedPacket6(const TestControlSocket& socket,
     }
 }
 
-void
+uint64_t
 TestControl::receivePackets(const TestControlSocket& socket) {
     int timeout = 0;
     bool receiving = true;
@@ -810,6 +1006,7 @@ TestControl::receivePackets(const TestControlSocket& socket) {
             }
         }
     }
+    return (received);
 }
 
 void
@@ -896,7 +1093,7 @@ TestControl::reset() {
     interrupted_ = false;
 }
 
-void
+int
 TestControl::run() {
     // Reset singleton state before test starts.
     reset();
@@ -910,14 +1107,14 @@ TestControl::run() {
         isc_throw(InvalidOperation,
                   "command options must be parsed before running a test");
     } else if (options.getIpVersion() == 4) {
-        setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator()));
+        setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator()));
     } else {
-        setTransidGenerator(NumberGeneratorPtr(new SequencialGenerator(0x00FFFFFF)));
+        setTransidGenerator(NumberGeneratorPtr(new SequentialGenerator(0x00FFFFFF)));
     }
 
     uint32_t clients_num = options.getClientsNum() == 0 ?
         1 : options.getClientsNum();
-    setMacAddrGenerator(NumberGeneratorPtr(new SequencialGenerator(clients_num)));
+    setMacAddrGenerator(NumberGeneratorPtr(new SequentialGenerator(clients_num)));
 
     // Diagnostics are command line options mainly.
     printDiagnostics();
@@ -941,48 +1138,20 @@ TestControl::run() {
     }
     // If user interrupts the program we will exit gracefully.
     signal(SIGINT, TestControl::handleInterrupt);
-    // Preload server with number of packets.
-    const bool do_preload = true;
-    for (int i = 0; i < options.getPreload(); ++i) {
-        if (options.getIpVersion() == 4) {
-            // No template buffer means no -T option specified.
-            // We will build packet ourselves.
-            if (template_buffers_.size() == 0) {
-                sendDiscover4(socket, do_preload);
-            } else {
-                // Pick template #0 if Discover is being sent.
-                // For Request it would be #1.
-                // @todo add defines for packet type index that can be
-                // used to access template_buffers_.
-                sendDiscover4(socket, template_buffers_[0],
-                              do_preload);
-            }
-        } else if (options.getIpVersion() == 6) {
-            // No template buffer means no -T option specified.
-            // We will build packet ourselfs.
-            if (template_buffers_.size() == 0) {
-                sendSolicit6(socket, do_preload);
-            } else {
-                // Pick template #0 if Solicit is being sent.
-                // For Request it would be #1.
-                // @todo add defines for packet type index that can be
-                // used to access template_buffers_.
-                sendSolicit6(socket, template_buffers_[0],
-                             do_preload);
-            }
-        }
+
+    // Preload server with the number of packets.
+    sendPackets(socket, options.getPreload(), true);
+
+    // Fork and run command specified with -w<wrapped-command>
+    if (!options.getWrapped().empty()) {
+        runWrapped();
     }
+
     // Initialize Statistics Manager. Release previous if any.
     initializeStatsMgr();
     for (;;) {
         // Calculate send due based on when last exchange was initiated.
         updateSendDue();
-        // If test period finished, maximum number of packet drops
-        // has been reached or test has been interrupted we have to
-        // finish the test.
-        if (checkExitConditions()) {
-            break;
-        }
         // Calculate number of packets to be sent to stay
         // catch up with rate.
         uint64_t packets_due = getNextExchangesNum();
@@ -997,30 +1166,17 @@ TestControl::run() {
         // @todo: set non-zero timeout for packets once we implement
         // microseconds timeout in IfaceMgr.
         receivePackets(socket);
-        // Send packets.
-        for (uint64_t i = packets_due; i > 0; --i) {
-            if (options.getIpVersion() == 4) {
-                // No template packets means that no -T option was specified.
-                // We have to build packets ourselfs.
-                if (template_buffers_.size() == 0) {
-                    sendDiscover4(socket);
-                } else {
-                    // @todo add defines for packet type index that can be
-                    // used to access template_buffers_.
-                    sendDiscover4(socket, template_buffers_[0]);
-                }
-            } else {
-                // No template packets means that no -T option was specified.
-                // We have to build packets ourselfs.
-                if (template_buffers_.size() == 0) {
-                    sendSolicit6(socket);
-                } else {
-                    // @todo add defines for packet type index that can be
-                    // used to access template_buffers_.
-                    sendSolicit6(socket, template_buffers_[0]);
-                }
-            }
+
+        // If test period finished, maximum number of packet drops
+        // has been reached or test has been interrupted we have to
+        // finish the test.
+        if (checkExitConditions()) {
+            break;
         }
+
+        // Initiate new DHCP packet exchanges.
+        sendPackets(socket, packets_due);
+
         // Report delay means that user requested printing number
         // of sent/received/dropped packets repeatedly.
         if (options.getReportDelay() > 0) {
@@ -1028,14 +1184,79 @@ TestControl::run() {
         }
     }
     printStats();
+
+    if (!options.getWrapped().empty()) {
+        // true means that we execute wrapped command with 'stop' argument.
+        runWrapped(true);
+    }
+
+    // Print packet timestamps
+    if (testDiags('t')) {
+        if (options.getIpVersion() == 4) {
+            stats_mgr4_->printTimestamps();
+        } else if (options.getIpVersion() == 6) {
+            stats_mgr6_->printTimestamps();
+        }
+    }
+
     // Print server id.
     if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
         std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
     }
+
     // Diagnostics flag 'e' means show exit reason.
     if (testDiags('e')) {
         std::cout << "Interrupted" << std::endl;
     }
+    // Print packet templates. Even if -T options have not been specified the
+    // dynamically build packet will be printed if at least one has been sent.
+    if (testDiags('T')) {
+        printTemplates();
+    }
+
+    int ret_code = 0;
+    // Check if any packet drops occured.
+    if (options.getIpVersion() == 4) {
+        ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
+    } else if (options.getIpVersion() == 6)  {
+        ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
+    }
+    return (ret_code);
+}
+
+void
+TestControl::runWrapped(bool do_stop /*= false */) const {
+    CommandOptions& options = CommandOptions::instance();
+    if (!options.getWrapped().empty()) {
+        pid_t pid = 0;
+        signal(SIGCHLD, handleChild);
+        pid = fork();
+        if (pid < 0) {
+            isc_throw(Unexpected, "unable to fork");
+        } else if (pid == 0) {
+            execlp(options.getWrapped().c_str(),
+                   do_stop ? "stop" : "start",
+                   NULL);
+        }
+    }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt4Ptr& pkt) {
+    if (testDiags('T')) {
+        if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
+            template_packets_v4_[pkt->getType()] = pkt;
+        }
+    }
+}
+
+void
+TestControl::saveFirstPacket(const Pkt6Ptr& pkt) {
+    if (testDiags('T')) {
+        if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
+            template_packets_v6_[pkt->getType()] = pkt;
+        }
+    }
 }
 
 void
@@ -1075,6 +1296,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
         }
         stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
     }
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1084,7 +1306,6 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // last_sent_ has to be updated for each function that initiates
     // new transaction. The packet exchange synchronization relies on this.
     last_sent_ = microsec_clock::universal_time();
-    CommandOptions& options = CommandOptions::instance();
     // Get the first argument if mulitple the same arguments specified
     // in the command line. First one refers to DISCOVER packets.
     const uint8_t arg_idx = 0;
@@ -1094,18 +1315,11 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV4_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
-    // Calculate randomization offset.
-    size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
+    // Get randomization offset.
     // We need to go back by HW_ETHER_LEN (MAC address length)
     // because this offset points to last octet of MAC address.
-    rand_offset -= HW_ETHER_LEN + 1;
+    size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
     // Create temporary buffer with template contents. We will
     // modify this temporary buffer but we don't want to modify
     // the original template.
@@ -1136,6 +1350,7 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
         stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
                                     boost::static_pointer_cast<Pkt4>(pkt4));
     }
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1149,6 +1364,8 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
                                              buf_msg_type);
     pkt4->addOption(opt_msg_type);
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
@@ -1199,6 +1416,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
                   "hasn't been initialized");
     }
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1206,25 +1424,17 @@ TestControl::sendRequest4(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const dhcp::Pkt4Ptr& discover_pkt4,
                           const dhcp::Pkt4Ptr& offer_pkt4) {
-    CommandOptions& options = CommandOptions::instance();
     // Get the second argument if multiple the same arguments specified
     // in the command line. Second one refers to REQUEST packets.
     const uint8_t arg_idx = 1;
     // Generate new transaction id.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV4_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     // Get the offset of MAC's last octet.
-    size_t rand_offset = DHCPV4_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
     // We need to go back by HW_ETHER_LEN (MAC address length)
     // because this offset points to last octet of MAC address.
-    rand_offset -= HW_ETHER_LEN + 1;
+    size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
     // Create temporaru buffer from the template.
     std::vector<uint8_t> in_buf(template_buf.begin(),
                                 template_buf.end());
@@ -1244,19 +1454,15 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
 
     // Set elapsed time.
-    size_t elp_offset = 0;
-    if (options.getElapsedTimeOffset() > 0) {
-        elp_offset = options.getElapsedTimeOffset();
-    }
+    size_t elp_offset = getElapsedTimeOffset();
     uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
     pkt4->writeValueAt<uint16_t>(elp_offset,
                                  static_cast<uint16_t>(elapsed_time / 1000));
 
     // Get the actual server id offset.
-    size_t sid_offset = DHCPV4_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        sid_offset = options.getServerIdOffset();
-    }
+    size_t sid_offset = getServerIdOffset();
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
@@ -1293,10 +1499,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     }
 
     // Get the actual offset of requested ip.
-    size_t rip_offset = DHCPV4_REQUESTED_IP_OFFSET;
-    if (options.getRequestedIpOffset() > 0) {
-        rip_offset = options.getRequestedIpOffset();
-    }
+    size_t rip_offset = getRequestedIpOffset();
     // Place requested IP option at specified position (rip_offset).
     boost::shared_ptr<LocalizedOption>
         opt_requested_ip(new LocalizedOption(Option::V4,
@@ -1318,6 +1521,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     // Update packet stats.
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
                                 boost::static_pointer_cast<Pkt4>(pkt4));
+    saveFirstPacket(pkt4);
 }
 
 void
@@ -1337,7 +1541,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     pkt6->addOption(opt_clientid);
 
     // Use first flags indicates that we want to use the server
-    // id captured in fisrt packet.
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
@@ -1370,40 +1574,33 @@ TestControl::sendRequest6(const TestControlSocket& socket,
                   "hasn't been initialized");
     }
     stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+    saveFirstPacket(pkt6);
 }
 
 void
 TestControl::sendRequest6(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const Pkt6Ptr& advertise_pkt6) {
-    CommandOptions& options = CommandOptions::instance();
     // Get the second argument if multiple the same arguments specified
     // in the command line. Second one refers to REQUEST packets.
     const uint8_t arg_idx = 1;
     // Generate transaction id.
     const uint32_t transid = generateTransid();
     // Get transaction id offset.
-    size_t transid_offset = DHCPV6_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
                                   transid_offset, transid));
     // Set elapsed time.
-    size_t elp_offset = DHCPV6_ELAPSED_TIME_OFFSET;
-    if (options.getElapsedTimeOffset() > 0) {
-        elp_offset = options.getElapsedTimeOffset();
-    }
+    size_t elp_offset = getElapsedTimeOffset();
     boost::shared_ptr<LocalizedOption>
         opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
                                              OptionBuffer(), elp_offset));
     pkt6->addOption(opt_elapsed_time);
 
     // Get the actual server id offset.
-    size_t sid_offset = DHCPV6_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        sid_offset = options.getServerIdOffset();
-    }
+    size_t sid_offset = getServerIdOffset();
+    // Use first flags indicates that we want to use the server
+    // id captured in first packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         boost::shared_ptr<LocalizedOption>
@@ -1439,10 +1636,7 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
                   "packet");
     }
-    size_t addr_offset = DHCPV6_IA_NA_OFFSET;
-    if (options.getRequestedIpOffset() > 0) {
-        addr_offset = options.getRequestedIpOffset();
-    }
+    size_t addr_offset = getRequestedIpOffset();
     boost::shared_ptr<LocalizedOption>
         opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
     if (!opt_ia_na->valid()) {
@@ -1455,20 +1649,14 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
                   "packet");
     }
-    size_t srvid_offset = DHCPV6_SERVERID_OFFSET;
-    if (options.getServerIdOffset() > 0) {
-        srvid_offset = options.getServerIdOffset();
-    }
+    size_t srvid_offset = getServerIdOffset();
     boost::shared_ptr<LocalizedOption>
         opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
                                          opt_serverid_advertise->getData(),
                                          srvid_offset));
     pkt6->addOption(opt_serverid);
     // Get randomization offset.
-    size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t rand_offset = getRandomOffset(arg_idx);
     OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
     if (!opt_clientid_advertise) {
         isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
@@ -1493,6 +1681,15 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     // Update packet stats.
     stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
 
+    // When 'T' diagnostics flag is specified it means that user requested
+    // printing packet contents. It will be just one (first) packet which
+    // contents will be printed. Here we check if this packet has been already
+    // collected. If it hasn't we save this packet so as we can print its
+    // contents when test is finished.
+    if (testDiags('T') &&
+        (template_packets_v6_.find(DHCPV6_REQUEST) == template_packets_v6_.end())) {
+        template_packets_v6_[DHCPV6_REQUEST] = pkt6;
+    }
 }
 
 void
@@ -1526,6 +1723,8 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
         }
         stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
     }
+
+    saveFirstPacket(pkt6);
 }
 
 void
@@ -1533,13 +1732,9 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
                           const std::vector<uint8_t>& template_buf,
                           const bool preload /*= false*/) {
     last_sent_ = microsec_clock::universal_time();
-    CommandOptions& options = CommandOptions::instance();
     const int arg_idx = 0;
     // Get transaction id offset.
-    size_t transid_offset = DHCPV6_TRANSID_OFFSET;
-    if (options.getTransactionIdOffset().size() > arg_idx) {
-        transid_offset = options.getTransactionIdOffset()[arg_idx];
-    }
+    size_t transid_offset = getTransactionIdOffset(arg_idx);
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     // Create packet.
@@ -1548,10 +1743,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
     if (!pkt6) {
         isc_throw(Unexpected, "failed to create SOLICIT packet");
     }
-    size_t rand_offset = DHCPV6_RANDOMIZATION_OFFSET;
-    if (options.getRandomOffset().size() > arg_idx) {
-        rand_offset = options.getRandomOffset()[arg_idx];
-    }
+    size_t rand_offset = getRandomOffset(arg_idx);
     // randomized will pick number of bytes randomized so we can
     // just use part of the generated duid and substitude a few bytes
     /// in template.
@@ -1577,6 +1769,7 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
         // Update packet stats.
         stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
     }
+    saveFirstPacket(pkt6);
 }
 
 
diff --git a/tests/tools/perfdhcp/test_control.h b/tests/tools/perfdhcp/test_control.h
index f89a7be..26c0c0b 100644
--- a/tests/tools/perfdhcp/test_control.h
+++ b/tests/tools/perfdhcp/test_control.h
@@ -33,15 +33,79 @@
 namespace isc {
 namespace perfdhcp {
 
+/// Default transaction id offset in the packet template.
+static const size_t DHCPV4_TRANSID_OFFSET = 4;
+/// Default offset of MAC's last octet in the packet template..
+static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
+/// Default elapsed time offset in the packet template.
+static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
+/// Default server id offset in the packet template.
+static const size_t DHCPV4_SERVERID_OFFSET = 54;
+/// Default requested ip offset in the packet template.
+static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
+/// Default DHCPV6 transaction id offset in t the packet template.
+static const size_t DHCPV6_TRANSID_OFFSET = 1;
+/// Default DHCPV6 randomization offset (last octet of DUID)
+/// in the packet template.
+static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
+/// Default DHCPV6 elapsed time offset in the packet template.
+static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
+/// Default DHCPV6 server id offset in the packet template.
+static const size_t DHCPV6_SERVERID_OFFSET = 22;
+/// Default DHCPV6 IA_NA offset in the packet template.
+static const size_t DHCPV6_IA_NA_OFFSET = 40;
+
 /// \brief Test Control class.
 ///
-/// This class is responsible for executing DHCP performance
-/// test end to end.
+/// This singleton class is used to run the performance test with
+/// with \ref TestControl::run function. This function can be executed
+/// multiple times if desired because it resets TestControl's internal
+/// state every time it is executed. Prior to running \ref TestControl::run,
+/// one must make sure to parse command line options by calling
+/// \ref CommandOptions::parse. Failing to do this will result in an exception.
+///
+/// The following major stages of the test are performed by this class:
+/// - set default transaction id and MAC address generators - the generator
+/// is an object of \ref TestControl::NumberGenerator type and it provides
+/// the custom randomization algorithms,
+/// - print command line arguments,
+/// - register option factory functions which are used to generate DHCP options
+/// being sent to a server,
+/// - create the socket for communication with a server,
+/// - read packet templates if user specified template files with '-T' command
+/// line option,
+/// - set the interrupt handler (invoked when ^C is pressed) which makes
+/// perfdhcp stop gracefully and print the test results before exiting,
+/// - executes an external command (if specified '-w' option), e.g. if user
+/// specified -w ./foo in the command line then program will execute
+/// "./foo start" at the beginning of the test and "./foo stop" when the test
+/// ends,
+/// - initialize the Statistics Manager,
+/// - executes the main loop:
+///   - calculate how many packets must be send to satisfy desired rate,
+///   - receive incoming packets from the server,
+///   - check the exit conditions - terminate the program if the exit criteria
+///   are fulfiled, e.g. reached maximum number of packet drops,
+///   - send the number of packets appropriate to satisfy the desired rate,
+///   - optionally print intermediate reports,
+/// - print statistics, e.g. achieved rate,
+/// - optionally print some diagnostics.
+///
+/// With the '-w' command line option user may specify the external application
+/// or script to be executed. This is executed twice, first when the test starts
+/// and second time when the test ends. This external script or application must
+/// accept 'start' and 'stop' arguments. The first time it is called, it is
+/// called with the argument 'start' and the second time with the argument
+/// 'stop'.
+///   
+/// The application is executed by calling fork() to fork the current perfdhcp
+/// process and then call execlp() to replace the current process image with
+/// the new one.
 ///
 /// Option factory functions are registered using
 /// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
 /// provide a way to create options of the same type in the same way.
-///  When new option instance is needed the corresponding factory
+///  When a new option instance is needed, the corresponding factory
 /// function is called to create it. This is done by calling
 /// \ref dhcp::Option::factory with DHCP message type specified as one of
 ///  parameters. Some of the parameters passed to factory function
@@ -55,27 +119,6 @@ namespace perfdhcp {
 class TestControl : public boost::noncopyable {
 public:
 
-    /// Default transaction id offset.
-    static const size_t DHCPV4_TRANSID_OFFSET = 4;
-    /// Default offset of MAC's last octet.
-    static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
-    /// Default elapsed time offset.
-    static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
-    /// Default server id offset.
-    static const size_t DHCPV4_SERVERID_OFFSET = 54;
-    /// Default requested ip offset.
-    static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
-    /// Default DHCPV6 transaction id offset.
-    static const size_t DHCPV6_TRANSID_OFFSET = 1;
-    /// Default DHCPV6 randomization offset (last octet of DUID)
-    static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
-    /// Default DHCPV6 elapsed time offset.
-    static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
-    /// Default DHCPV6 server id offset.
-    static const size_t DHCPV6_SERVERID_OFFSET = 22;
-    /// Default DHCPV6 IA_NA offset.
-    static const size_t DHCPV6_IA_NA_OFFSET = 40;
-
     /// Statistics Manager for DHCPv4.
     typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
     /// Pointer to Statistics Manager for DHCPv4;
@@ -138,7 +181,7 @@ public:
     /// This is default numbers generator class. The member function is
     /// used to generate uint32_t values. Other generator classes should
     /// derive from this one to implement generation algorithms
-    /// (e.g. sequencial or based on random function).
+    /// (e.g. sequential or based on random function).
     class NumberGenerator {
     public:
 
@@ -154,14 +197,14 @@ public:
     /// The default generator pointer.
     typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
 
-    /// \brief Sequencial numbers generatorc class.
-    class SequencialGenerator : public NumberGenerator {
+    /// \brief Sequential numbers generatorc class.
+    class SequentialGenerator : public NumberGenerator {
     public:
         /// \brief Constructor.
         ///
         /// \param range maximum number generated. If 0 is given then
-        /// range defaults to maximym uint32_t value.
-        SequencialGenerator(uint32_t range = 0xFFFFFFFF) :
+        /// range defaults to maximum uint32_t value.
+        SequentialGenerator(uint32_t range = 0xFFFFFFFF) :
             NumberGenerator(),
             num_(0),
             range_(range) {
@@ -170,7 +213,7 @@ public:
             }
         }
 
-        /// \brief Generate number sequencialy.
+        /// \brief Generate number sequentialy.
         ///
         /// \return generated number.
         virtual uint32_t generate() {
@@ -180,7 +223,7 @@ public:
         }
     private:
         uint32_t num_;   ///< Current number.
-        uint32_t range_; ///< Maximum number generated.
+        uint32_t range_; ///< Number of unique numbers generated.
     };
 
     /// \brief Length of the Ethernet HW address (MAC) in bytes.
@@ -203,7 +246,9 @@ public:
     ///
     /// \throw isc::InvalidOperation if command line options are not parsed.
     /// \throw isc::Unexpected if internal Test Controler error occured.
-    void run();
+    /// \return error_code, 3 if number of received packets is not equal
+    /// to number of sent packets, 0 if everything is ok.
+    int run();
 
     /// \brief Set new transaction id generator.
     ///
@@ -412,7 +457,10 @@ protected:
     /// their content and stores it in class internal buffers. Template
     /// file names are specified from the command line with -T option.
     ///
-    /// \throw isc::BadValue if any of the template files does not exist
+    /// \throw isc::BadValue if any of the template files does not exist,
+    /// contains characters other than hexadecimal digits or spaces.
+    /// \throw OutOfRange if any of the template files is empty or has
+    /// odd number of hexadecimal digits.
     void initPacketTemplates();
 
     /// \brief Initializes Statistics Manager.
@@ -496,8 +544,8 @@ protected:
     /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
     ///
     /// Method receives DHCPv4 or DHCPv6 packets from the server.
-    /// This function will call \ref receivePacket4 or
-    /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
+    /// This function will call \ref processReceivedPacket4 or
+    /// \ref processReceivedPacket6 depending if DHCPv4 or DHCPv6 packet
     /// has arrived.
     ///
     /// \warning this method does not check if provided socket is
@@ -506,7 +554,8 @@ protected:
     /// \param socket socket to be used.
     /// \throw isc::BadValue if unknown message type received.
     /// \throw isc::Unexpected if unexpected error occured.
-    void receivePackets(const TestControlSocket& socket);
+    /// \return number of received packets.
+    uint64_t receivePackets(const TestControlSocket& socket);
 
     /// \brief Register option factory functions for DHCPv4
     ///
@@ -539,6 +588,36 @@ protected:
     /// called before new test is started.
     void reset();
 
+    /// \brief Save the first DHCPv4 sent packet of the specified type.
+    ///
+    /// This method saves first packet of the specified being sent
+    /// to the server if user requested diagnostics flag 'T'. In
+    /// such case program has to print contents of selected packets
+    /// being sent to the server. It collects first packets of each
+    /// type and keeps them around until test finishes. Then they
+    /// are printed to the user. If packet of specified type has
+    /// been already stored this function perfroms no operation.
+    /// This function does not perform sanity check if packet
+    /// pointer is valid. Make sure it is before calling it.
+    ///
+    /// \param pkt packet to be stored.
+    inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt);
+
+    /// \brief Save the first DHCPv6 sent packet of the specified type.
+    ///
+    /// This method saves first packet of the specified being sent
+    /// to the server if user requested diagnostics flag 'T'. In
+    /// such case program has to print contents of selected packets
+    /// being sent to the server. It collects first packets of each
+    /// type and keeps them around until test finishes. Then they
+    /// are printed to the user. If packet of specified type has
+    /// been already stored this function perfroms no operation.
+    /// This function does not perform sainty check if packet
+    /// pointer is valid. Make sure it is before calling it.
+    ///
+    /// \param pkt packet to be stored.
+    inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt);
+
     /// \brief Send DHCPv4 DISCOVER message.
     ///
     /// Method creates and sends DHCPv4 DISCOVER message to the server
@@ -576,6 +655,31 @@ protected:
                        const std::vector<uint8_t>& template_buf,
                        const bool preload = false);
 
+    /// \brief Send number of packets to initiate new exchanges.
+    ///
+    /// Method initiates the new DHCP exchanges by sending number
+    /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload
+    /// mode was requested sent packets will not be counted in
+    /// the statistics. The responses from the server will be
+    /// received and counted as orphans because corresponding sent
+    /// packets are not included in StatsMgr for match.
+    /// When preload mode is disabled and diagnostics flag 'i' is
+    /// specified then function will be trying to receive late packets
+    /// before new packets are sent to the server. Statistics of
+    /// late received packets is updated accordingly.
+    ///
+    /// \todo do not count responses in preload mode as orphans.
+    ///
+    /// \param socket socket to be used to send packets.
+    /// \param packets_num number of packets to be sent.
+    /// \param preload preload mode, packets not included in statistics.
+    /// \throw isc::Unexpected if thrown by packet sending method.
+    /// \throw isc::InvalidOperation if thrown by packet sending method.
+    /// \throw isc::OutOfRange if thrown by packet sending method.
+    void sendPackets(const TestControlSocket &socket,
+                     const uint64_t packets_num,
+                     const bool preload = false);
+
     /// \brief Send DHCPv4 REQUEST message.
     ///
     /// Method creates and sends DHCPv4 REQUEST message to the server.
@@ -732,6 +836,34 @@ private:
     template<class T>
     uint32_t getElapsedTime(const T& pkt1, const T& pkt2);
 
+    /// \brief Return elapsed time offset in a packet.
+    ///
+    /// \return elapsed time offset in packet.
+    int getElapsedTimeOffset() const;
+
+    /// \brief Return randomization offset in a packet.
+    ///
+    /// \return randomization offset in packet.
+    int getRandomOffset(const int arg_idx) const;
+
+    /// \brief Return requested ip offset in a packet.
+    ///
+    /// \return randomization offset in a packet.
+    int getRequestedIpOffset() const;
+
+    /// \brief Return server id offset in a packet.
+    ///
+    /// \return server id offset in packet.
+    int getServerIdOffset() const;
+
+    /// \brief Return transaction id offset in a packet.
+    ///
+    /// \param arg_idx command line argument index to be used.
+    /// If multiple -X parameters specifed it points to the
+    /// one to be used.
+    /// \return transaction id offset in packet.
+    int getTransactionIdOffset(const int arg_idx) const;
+
     /// \brief Get number of received packets.
     ///
     /// Get the number of received packets from the Statistics Manager.
@@ -750,6 +882,14 @@ private:
     /// \return number of sent packets.
     uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
 
+    /// \brief Handle child signal.
+    ///
+    /// Function handles child signal by waiting for
+    /// the process to complete.
+    ///
+    /// \param sig signal (ignored)
+    static void handleChild(int sig);
+
     /// \brief Handle interrupt signal.
     ///
     /// Function sets flag indicating that program has been
@@ -763,13 +903,35 @@ private:
     /// Method prints main diagnostics data.
     void printDiagnostics() const;
 
+    /// \brief Print template information
+    ///
+    /// \param packet_type packet type.
+    void printTemplate(const uint8_t packet_type) const;
+
+    /// \brief Print templates information.
+    ///
+    /// Method prints information about data offsets
+    /// in packet templates and their contents.
+    void printTemplates() const;
+
     /// \brief Read DHCP message template from file.
     ///
     /// Method reads DHCP message template from file and
     /// converts it to binary format. Read data is appended
     /// to template_buffers_ vector.
+    ///
+    /// \param file_name name of the packet template file.
+    /// \throw isc::OutOfRange if file is empty or has odd number
+    /// of hexadecimal digits.
+    /// \throw isc::BadValue if file contains characters other than
+    /// spaces or hexadecimal digits.
     void readPacketTemplate(const std::string& file_name);
 
+    /// \brief Run wrapped command.
+    ///
+    /// \param do_stop execute wrapped command with "stop" argument.
+    void runWrapped(bool do_stop = false) const;
+
     /// \brief Convert vector in hexadecimal string.
     ///
     /// \todo Consider moving this function to src/lib/util.
@@ -798,6 +960,11 @@ private:
     /// Packet template buffers.
     TemplateBufferCollection template_buffers_;
 
+    /// First packets send. They are used at the end of the test
+    /// to print packet templates when diagnostics flag T is specifed.
+    std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_;
+    std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;
+
     static bool interrupted_;  ///< Is program interrupted.
 };
 
diff --git a/tests/tools/perfdhcp/tests/command_options_helper.h b/tests/tools/perfdhcp/tests/command_options_helper.h
index 2fc70ea..f9f6d36 100644
--- a/tests/tools/perfdhcp/tests/command_options_helper.h
+++ b/tests/tools/perfdhcp/tests/command_options_helper.h
@@ -87,13 +87,14 @@ public:
     /// parsing.
     ///
     /// \param cmdline command line provided as single string.
-    static void process(const std::string& cmdline) {
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    static bool process(const std::string& cmdline) {
         CommandOptions& opt = CommandOptions::instance();
         int argc = 0;
         char** argv = tokenizeString(cmdline, argc);
         ArgvPtr args(argv, argc);
         opt.reset();
-        opt.parse(args.getArgc(), args.getArgv());
+        return (opt.parse(args.getArgc(), args.getArgv()));
     }
 
 private:
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index 801b02d..360178f 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -45,10 +45,11 @@ protected:
     /// parses arguments using CommandOptions class to set
     /// its data members and de-allocates array of C-strings.
     ///
-    /// \param cmdline Command line to parse
-    /// \throws std::bad allocation if tokenization failed
-    void process(const std::string& cmdline) {
-        CommandOptionsHelper::process(cmdline);
+    /// \param cmdline Command line to parse.
+    /// \throws std::bad allocation if tokenization failed.
+    /// \return true if program has been run in help or version mode ('h' or 'v' flag).
+    bool process(const std::string& cmdline) {
+        return (CommandOptionsHelper::process(cmdline));
     }
 
     /// \brief Check default initialized values
@@ -56,7 +57,7 @@ protected:
     /// Check if initialized values are correct
     void checkDefaults() {
         CommandOptions& opt = CommandOptions::instance();
-        process("perfdhcp 192.168.0.1");
+        EXPECT_NO_THROW(process("perfdhcp 192.168.0.1"));
         EXPECT_EQ(4, opt.getIpVersion());
         EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
         EXPECT_EQ(0, opt.getRate());
@@ -137,18 +138,31 @@ protected:
 };
 
 TEST_F(CommandOptionsTest, Defaults) {
-    process("perfdhcp all");
+    EXPECT_NO_THROW(process("perfdhcp all"));
     checkDefaults();
 }
 
+TEST_F(CommandOptionsTest, HelpVersion) {
+    // The parser is supposed to return true if 'h' or 'v' options
+    // are specified.
+    EXPECT_TRUE(process("perfdhcp -h"));
+    EXPECT_TRUE(process("perfdhcp -v"));
+    EXPECT_TRUE(process("perfdhcp -h -v"));
+    EXPECT_TRUE(process("perfdhcp -6 -l ethx -h all"));
+    EXPECT_TRUE(process("perfdhcp -l ethx -v all"));
+    // No 'h' or 'v' option specified. The false value
+    // should be returned.
+    EXPECT_FALSE(process("perfdhcp -l ethx all"));
+}
+
 TEST_F(CommandOptionsTest, UseFirst) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -1 -B -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all"));
     EXPECT_TRUE(opt.isUseFirst());
 }
 TEST_F(CommandOptionsTest, IpVersion) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -6 -l ethx -c -i all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all"));
     EXPECT_EQ(6, opt.getIpVersion());
     EXPECT_EQ("ethx", opt.getLocalName());
     EXPECT_TRUE(opt.isRapidCommit());
@@ -169,7 +183,7 @@ TEST_F(CommandOptionsTest, IpVersion) {
 
 TEST_F(CommandOptionsTest, Rate) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -4 -r 10 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -4 -r 10 -l ethx all"));
     EXPECT_EQ(10, opt.getRate());
 
     // Negative test cases
@@ -189,7 +203,7 @@ TEST_F(CommandOptionsTest, Rate) {
 
 TEST_F(CommandOptionsTest, ReportDelay) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -r 100 -t 17 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -r 100 -t 17 -l ethx all"));
     EXPECT_EQ(17, opt.getReportDelay());
 
     // Negative test cases
@@ -204,7 +218,7 @@ TEST_F(CommandOptionsTest, ReportDelay) {
 
 TEST_F(CommandOptionsTest, ClientsNum) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -R 200 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -R 200 -l ethx all"));
     EXPECT_EQ(200, opt.getClientsNum());
     process("perfdhcp -R 0 -l ethx all");
     EXPECT_EQ(0, opt.getClientsNum());
@@ -282,12 +296,12 @@ TEST_F(CommandOptionsTest, Base) {
 
 TEST_F(CommandOptionsTest, DropTime) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -d 12 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 12 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
 
-    process("perfdhcp -l ethx -d 2 -d 4.7 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -d 2 -d 4.7 all"));
     ASSERT_EQ(2, opt.getDropTime().size());
     EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
     EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
@@ -302,7 +316,7 @@ TEST_F(CommandOptionsTest, DropTime) {
 
 TEST_F(CommandOptionsTest, TimeOffset) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all"));
     EXPECT_EQ(4, opt.getElapsedTimeOffset());
 
     // Negative test cases
@@ -339,8 +353,8 @@ TEST_F(CommandOptionsTest, ExchangeMode) {
 
 TEST_F(CommandOptionsTest, Offsets) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
-            "-X3 -T file1.x -T file2.x all");
+    EXPECT_NO_THROW(process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
+                            "-X3 -T file1.x -T file2.x all"));
     EXPECT_EQ(2, opt.getRequestedIpOffset());
     EXPECT_EQ(5, opt.getElapsedTimeOffset());
     EXPECT_EQ(3, opt.getServerIdOffset());
@@ -363,7 +377,7 @@ TEST_F(CommandOptionsTest, Offsets) {
 
 TEST_F(CommandOptionsTest, LocalPort) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -L 2000 all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -L 2000 all"));
     EXPECT_EQ(2000, opt.getLocalPort());
 
     // Negative test cases
@@ -378,7 +392,7 @@ TEST_F(CommandOptionsTest, LocalPort) {
 
 TEST_F(CommandOptionsTest, Preload) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -1 -P 3 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -1 -P 3 -l ethx all"));
     EXPECT_EQ(3, opt.getPreload());
 
     // Negative test cases
@@ -391,11 +405,11 @@ TEST_F(CommandOptionsTest, Preload) {
 
 TEST_F(CommandOptionsTest, Seed) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -6 -P 2 -s 23 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 23 -l ethx all"));
     EXPECT_EQ(23, opt.getSeed());
     EXPECT_TRUE(opt.isSeeded());
 
-    process("perfdhcp -6 -P 2 -s 0 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -6 -P 2 -s 0 -l ethx all"));
     EXPECT_EQ(0, opt.getSeed());
     EXPECT_FALSE(opt.isSeeded());
 
@@ -409,11 +423,11 @@ TEST_F(CommandOptionsTest, Seed) {
 
 TEST_F(CommandOptionsTest, TemplateFiles) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -T file1.x -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -T file1.x -l ethx all"));
     ASSERT_EQ(1, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
 
-    process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all"));
     ASSERT_EQ(2, opt.getTemplateFiles().size());
     EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
     EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
@@ -430,7 +444,7 @@ TEST_F(CommandOptionsTest, TemplateFiles) {
 
 TEST_F(CommandOptionsTest, Wrapped) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -B -w start -i -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -B -w start -i -l ethx all"));
     EXPECT_EQ("start", opt.getWrapped());
 
     // Negative test cases
@@ -441,7 +455,7 @@ TEST_F(CommandOptionsTest, Wrapped) {
 
 TEST_F(CommandOptionsTest, Diagnostics) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -l ethx -i -x asTe all");
+    EXPECT_NO_THROW(process("perfdhcp -l ethx -i -x asTe all"));
     EXPECT_EQ("asTe", opt.getDiags());
 
     // Negative test cases
@@ -467,18 +481,18 @@ TEST_F(CommandOptionsTest, Aggressivity) {
 
 TEST_F(CommandOptionsTest, MaxDrop) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -D 25 -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -r 10 all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
-    process("perfdhcp -D 25 -l ethx -D 15 -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 25 -l ethx -D 15 -r 10 all"));
     EXPECT_EQ(25, opt.getMaxDrop()[0]);
     EXPECT_EQ(15, opt.getMaxDrop()[1]);
 
-    process("perfdhcp -D 15% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 15% -l ethx -r 10 all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
-    process("perfdhcp -D 15% -D25% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 15% -D25% -l ethx -r 10 all"));
     EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
-    process("perfdhcp -D 1% -D 99% -l ethx -r 10 all");
+    EXPECT_NO_THROW(process("perfdhcp -D 1% -D 99% -l ethx -r 10 all"));
     EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
     EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
 
@@ -498,9 +512,9 @@ TEST_F(CommandOptionsTest, MaxDrop) {
 
 TEST_F(CommandOptionsTest, NumRequest) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -n 1000 -r 10 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -n 1000 -r 10 -l ethx all"));
     EXPECT_EQ(1000, opt.getNumRequests()[0]);
-    process("perfdhcp -n 5 -r 10 -n 500 -l ethx all");
+    EXPECT_NO_THROW(process("perfdhcp -n 5 -r 10 -n 500 -l ethx all"));
     EXPECT_EQ(5, opt.getNumRequests()[0]);
     EXPECT_EQ(500, opt.getNumRequests()[1]);
 
@@ -517,7 +531,7 @@ TEST_F(CommandOptionsTest, NumRequest) {
 
 TEST_F(CommandOptionsTest, Period) {
     CommandOptions& opt = CommandOptions::instance();
-    process("perfdhcp -p 120 -l ethx -r 100 all");
+    EXPECT_NO_THROW(process("perfdhcp -p 120 -l ethx -r 100 all"));
     EXPECT_EQ(120, opt.getPeriod());
 
     // Negative test cases
@@ -567,19 +581,19 @@ TEST_F(CommandOptionsTest, Server) {
     // set to broadcast address because 'all' was specified.
     EXPECT_TRUE(opt.isBroadcast());
     // The broadcast address is 255.255.255.255.
-    EXPECT_EQ("255.255.255.255", opt.getServerName());
+    EXPECT_EQ(DHCP_IPV4_BROADCAST_ADDRESS, opt.getServerName());
 
     // When all is specified for DHCPv6 mode we expect
     // FF02::1:2 as a server name which means All DHCP
     // servers and relay agents in local network segment
     ASSERT_NO_THROW(process("perfdhcp -6 all"));
-    EXPECT_EQ("FF02::1:2", opt.getServerName());
+    EXPECT_EQ(ALL_DHCP_RELAY_AGENTS_AND_SERVERS, opt.getServerName());
 
     // When server='servers' in DHCPv6 mode we expect
     // FF05::1:3 as server name which means All DHCP
     // servers in local network.
     ASSERT_NO_THROW(process("perfdhcp -6 servers"));
-    EXPECT_EQ("FF05::1:3", opt.getServerName());
+    EXPECT_EQ(ALL_DHCP_SERVERS, opt.getServerName());
 
     // If server name is neither 'all' nor 'servers'
     // the given argument value is expected to be
diff --git a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
index d6b3aef..8a83cac 100644
--- a/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
+++ b/tests/tools/perfdhcp/tests/stats_mgr_unittest.cc
@@ -166,6 +166,7 @@ TEST_F(StatsMgrTest, Constructor) {
     EXPECT_EQ(0, stats_mgr->getUnorderedLookups(StatsMgr4::XCHG_DO));
     EXPECT_EQ(0, stats_mgr->getSentPacketsNum(StatsMgr4::XCHG_DO));
     EXPECT_EQ(0, stats_mgr->getRcvdPacketsNum(StatsMgr4::XCHG_DO));
+    EXPECT_EQ(0, stats_mgr->getCollectedNum(StatsMgr4::XCHG_DO));
 
     EXPECT_THROW(stats_mgr->getAvgDelay(StatsMgr4::XCHG_DO), InvalidOperation);
     EXPECT_THROW(stats_mgr->getStdDevDelay(StatsMgr4::XCHG_DO),
@@ -342,7 +343,7 @@ TEST_F(StatsMgrTest, Orphans) {
 TEST_F(StatsMgrTest, Delays) {
 
     boost::shared_ptr<StatsMgr4> stats_mgr(new StatsMgr4());
-    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO);
+    stats_mgr->addExchangeStats(StatsMgr4::XCHG_DO, 5);
 
     // Send DISCOVER, wait 2s and receive OFFER. This will affect
     // counters in Stats Manager.
diff --git a/tests/tools/perfdhcp/tests/test_control_unittest.cc b/tests/tools/perfdhcp/tests/test_control_unittest.cc
index a4cde00..66f85fe 100644
--- a/tests/tools/perfdhcp/tests/test_control_unittest.cc
+++ b/tests/tools/perfdhcp/tests/test_control_unittest.cc
@@ -92,7 +92,7 @@ public:
     NakedTestControl() : TestControl() {
         uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
             1 : CommandOptions::instance().getClientsNum();
-        setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequencialGenerator(clients_num)));
+        setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequentialGenerator(clients_num)));
     };
 
 };
@@ -117,12 +117,20 @@ public:
     /// \brief Create packet template file from binary data.
     ///
     /// Function creates file containing data from the provided buffer
-    /// in hexadecimal format.
+    /// in hexadecimal format. The size parameter specifies the maximum
+    /// size of the file. If total number of hexadecimal digits resulting
+    /// from buffer size is greater than maximum file size the file is
+    /// truncated.
+    ///
     /// \param filename template file to be created.
     /// \param buffer with binary datato be stored in file.
+    /// \param size target size of the file.
+    /// \param invalid_chars inject invalid chars to the template file.
     /// \return true if file creation successful.
     bool createTemplateFile(const std::string& filename,
-                            const std::vector<uint8_t>& buf) const {
+                            const std::vector<uint8_t>& buf,
+                            const size_t size,
+                            const bool invalid_chars = false) const {
         std::ofstream temp_file;
         temp_file.open(filename.c_str(), ios::out | ios::trunc);
         if (!temp_file.is_open()) {
@@ -131,7 +139,24 @@ public:
         for (int i = 0; i < buf.size(); ++i) {
             int first_digit = buf[i] / 16;
             int second_digit = buf[i] % 16;
-            temp_file << std::hex << first_digit << second_digit << std::dec;
+            // Insert two spaces between two hexadecimal digits.
+            // Spaces are allowed in template files.
+            temp_file << std::string(2, ' ');
+            if (2 * i + 1 < size) {
+                if (!invalid_chars) {
+                    temp_file << std::hex << first_digit << second_digit << std::dec;
+                } else {
+                    temp_file << "XY";
+                }
+            } else if (2 * i < size) {
+                if (!invalid_chars) {
+                    temp_file << std::hex << first_digit;
+                } else {
+                    temp_file << "X";
+                }
+            } else {
+                break;
+            }
         }
         temp_file.close();
         return (true);
@@ -965,8 +990,9 @@ TEST_F(TestControlTest, PacketTemplates) {
     for (int i = 0; i < template2.size(); ++i) {
         template2[i] = static_cast<uint8_t>(random() % 256);
     }
-    ASSERT_TRUE(createTemplateFile(file1, template1));
-    ASSERT_TRUE(createTemplateFile(file2, template2));
+    // Size of the file is 2 times larger than binary data size.
+    ASSERT_TRUE(createTemplateFile(file1, template1, template1.size() * 2));
+    ASSERT_TRUE(createTemplateFile(file2, template2, template2.size() * 2));
     CommandOptions& options = CommandOptions::instance();
     NakedTestControl tc;
 
@@ -983,6 +1009,32 @@ TEST_F(TestControlTest, PacketTemplates) {
     ASSERT_EQ(template2.size(), buf2.size());
     EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
     EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
+
+    // Try to read template file with odd number of digits.
+    std::string file3("../templates/test3.hex");
+    // Size of the file is 2 times larger than binary data size and it is always
+    // even number. Substracting 1 makes file size odd.
+    ASSERT_TRUE(createTemplateFile(file3, template1, template1.size() * 2 - 1));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file3 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+    // Try to read empty file.
+    std::string file4("../templates/test4.hex");
+    ASSERT_TRUE(createTemplateFile(file4, template2, 0));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file4 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::OutOfRange);
+
+    // Try reading file with non hexadecimal characters.
+    std::string file5("../templates/test5.hex");
+    ASSERT_TRUE(createTemplateFile(file5, template1, template1.size() * 2, true));
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1 -T " + file5 + " all")
+    );
+    EXPECT_THROW(tc.initPacketTemplates(), isc::BadValue);
 }
 
 TEST_F(TestControlTest, RateControl) {



More information about the bind10-changes mailing list