Browse Source

[github22] Squashed changes from pallotron/perfdhcp_random_mac_from_list.

This change includes MAC address lists for perfdhcp as well as
a command line option for generating relayed traffic in DHCPv6.
This patch was contributed by Angelo Failla.
Marcin Siodelski 8 years ago
parent
commit
277322dc7b

+ 2 - 2
configure.ac

@@ -1094,12 +1094,12 @@ elif test "${log4cplus_path}" != "yes" ; then
   LOG4CPLUS_LIBS="-L${log4cplus_path}/lib"
 else
 # If not specified, try some common paths.
-	log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
+	log4cplusdirs="/usr /usr/local /usr/pkg /opt /opt/local"
 	for d in $log4cplusdirs
 	do
 		if test -f $d/include/log4cplus/logger.h; then
 			LOG4CPLUS_INCLUDES="-I$d/include"
-			LOG4CPLUS_LIBS="-L$d/lib"
+			LOG4CPLUS_LIBS="-L$d/lib -L$d/lib64"
 			break
 		fi
 	done

+ 78 - 9
src/bin/perfdhcp/command_options.cc

@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <stdint.h>
 #include <unistd.h>
+#include <fstream>
 
 
 using namespace std;
@@ -117,6 +118,8 @@ CommandOptions::reset() {
     mac_template_.assign(mac, mac + 6);
     duid_template_.clear();
     base_.clear();
+    mac_file_list_.clear();
+    mac_list_.clear();
     num_request_.clear();
     period_ = 0;
     drop_time_set_ = 0;
@@ -133,6 +136,7 @@ CommandOptions::reset() {
     broadcast_ = false;
     rapid_commit_ = false;
     use_first_ = false;
+    use_relayed_v6_ = false;
     template_file_.clear();
     rnd_offset_.clear();
     xid_offset_.clear();
@@ -205,10 +209,11 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
 
     std::ostringstream stream;
     stream << "perfdhcp";
+    int num_mac_list_files = 0;
 
     // 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:"
+    while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:a:L:M:"
                         "s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
         stream << " -" << static_cast<char>(opt);
         if (optarg) {
@@ -219,6 +224,19 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
             use_first_ = true;
             break;
 
+        // act as a relay Agent (single char option and R/r are taken already).
+        case 'A':
+            use_relayed_v6_ = true;
+            // TODO: actually use level, at the moment we support only 1 level.
+            // See comment in https://github.com/isc-projects/kea/pull/22#issuecomment-243405600
+            int level;
+            level = positiveInteger(
+                        " -A<encapusulation_levels> must be a positive integer");
+            if (level != 1) {
+              isc_throw(isc::InvalidParameter, "-A only supports 1 at the moment.");
+            }
+            break;
+
         case '4':
             check(ipversion_ == 6, "IP version already set to 6");
             ipversion_ = 4;
@@ -340,6 +358,13 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
                   boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
             break;
 
+        case 'M':
+            check(num_mac_list_files >= 1, "only -M option can be specified");
+            num_mac_list_files++;
+            mac_file_list_ = std::string(optarg);
+            loadMacs();
+            break;
+
         case 'n':
             num_req = positiveInteger("value of num-request:"
                                       " -n<value> must be a positive integer");
@@ -554,7 +579,7 @@ CommandOptions::decodeBase(const std::string& base) {
 
     // Currently we only support mac and duid
     if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) {
-        decodeMac(b);
+        decodeMacBase(b);
     } else if (b.substr(0, 5) == "duid=") {
         decodeDuid(b);
     } else {
@@ -565,7 +590,7 @@ CommandOptions::decodeBase(const std::string& base) {
 }
 
 void
-CommandOptions::decodeMac(const std::string& base) {
+CommandOptions::decodeMacBase(const std::string& base) {
     // Strip string from mac=
     size_t found = base.find('=');
     static const char* errmsg = "expected -b<base> format for"
@@ -685,12 +710,45 @@ CommandOptions::convertHexString(const std::string& text) const {
     return ui;
 }
 
+void CommandOptions::loadMacs() {
+  std::string line;
+  std::ifstream infile(mac_file_list_.c_str());
+  while (std::getline(infile, line)) {
+    check(decodeMacString(line), "invalid mac in input");
+  }
+}
+
+bool CommandOptions::decodeMacString(const std::string& line) {
+  // decode mac string into a vector of uint8_t returns true in case of error.
+  std::istringstream s(line);
+  std::string token;
+  std::vector<uint8_t> mac;
+  while(std::getline(s, token, ':')) {
+    // Convert token to byte value using std::istringstream
+    if (token.length() > 0) {
+      unsigned int ui = 0;
+      try {
+        // Do actual conversion
+        ui = convertHexString(token);
+      } catch (isc::InvalidParameter&) {
+        return (true);
+      }
+      // If conversion succeeded store byte value
+      mac.push_back(ui);
+    }
+  }
+  mac_list_.push_back(mac);
+  return (false);
+}
+
 void
 CommandOptions::validate() const {
     check((getIpVersion() != 4) && (isBroadcast() != 0),
           "-B is not compatible with IPv6 (-6)");
     check((getIpVersion() != 6) && (isRapidCommit() != 0),
           "-6 (IPv6) must be set to use -c");
+    check(getIpVersion() == 4 && isUseRelayedV6(),
+          "Can't use -4 with -A, it's a V6 only option.");
     check((getIpVersion() != 6) && (getReleaseRate() != 0),
           "-F<release-rate> may be used with -6 (IPv6) only");
     check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
@@ -757,7 +815,8 @@ CommandOptions::validate() const {
     check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
           "second/request -T<template-file> must be set to "
           "use -I<ip-offset>");
-
+    check((!getMacListFile().empty() && base_.size() > 0),
+          "Can't use -b with -M option");
 }
 
 void
@@ -871,6 +930,9 @@ CommandOptions::printCommandLine() const {
     if (use_first_) {
         std::cout << "use-first" << std::endl;
     }
+    if (!mac_file_list_.empty()) {
+        std::cout << "mac-file-list=" << mac_file_list_ << std::endl;
+    }
     for (size_t i = 0; i < template_file_.size(); ++i) {
         std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
     }
@@ -910,15 +972,16 @@ CommandOptions::printCommandLine() const {
 void
 CommandOptions::usage() const {
     std::cout <<
-        "perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
+        "perfdhcp [-hv] [-4|-6] [-A<encapusulation_levels>] [-e<lease-type>]"
+        "         [-r<rate>] [-f<renew-rate>]\n"
         "         [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
         "         [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
         "         [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
         "         [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
-        "         [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
-        "         [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
-        "         [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>]\n"
-        "         [server]\n"
+        "         [-c] [-1] [-M<mac_list_file>] [-T<template-file>]\n"
+        "         [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
+        "         [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n"
+        "         [-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"
@@ -979,6 +1042,10 @@ CommandOptions::usage() const {
         "    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"
+        "-M<mac-file-list>: A text file containing a list of macs, one per line.\n"
+        "   If provided a random mac will be choosen for every exchange.\n"
+        "   Must not be used in conjunction with the -b parameter.\n"
+        "   In the v6 case MAC addresses are used to generate DUID-LLs.\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"
@@ -1018,6 +1085,8 @@ CommandOptions::usage() const {
         "    the exchange rate (given by -r<rate>).  Furthermore the sum of\n"
         "    this value and the renew-rate (given by -f<rate) must be equal\n"
         "    to or less than the exchange rate.\n"
+        "-A<encapusulation_levels>: When acting in DHCPv6 mode, send out relay packets.\n"
+        "    <encapusulation_levels> specifies how many relays forwarded this message\n"
         "\n"
         "The remaining options are used only in conjunction with -r:\n"
         "\n"

+ 34 - 1
src/bin/perfdhcp/command_options.h

@@ -269,11 +269,29 @@ public:
     /// \return true if server-iD to be taken from first package.
     bool isUseFirst() const { return use_first_; }
 
+    /// \brief Check if generated DHCPv6 messages shuold appear as relayed.
+    ///
+    /// \return true if generated traffic should appear as relayed.
+    bool isUseRelayedV6() const { return use_relayed_v6_; }
+
     /// \brief Returns template file names.
     ///
     /// \return template file names.
     std::vector<std::string> getTemplateFiles() const { return template_file_; }
 
+    /// \brief Returns location of the file containing list of MAC addresses
+    ///
+    /// MAC addresses read from the file are used by the perfdhcp in message
+    /// exchanges with the DHCP server.
+    ///
+    /// \return mac_template file name.
+    std::string getMacListFile() const { return mac_file_list_; }
+
+    /// \brief Returns the list of macs, every mac is a vector<uint8_t>
+    ///
+    /// \return mac_list_ vector of vectors.
+    const std::vector<std::vector<uint8_t> >& getAllMacs() const { return mac_list_; }
+
     /// brief Returns template offsets for xid.
     ///
     /// \return template offsets for xid.
@@ -427,7 +445,7 @@ private:
     ///
     /// \param base Base string given as -b mac=00:01:02:03:04:05.
     /// \throws isc::InvalidParameter if mac address is invalid.
-    void decodeMac(const std::string& base);
+    void decodeMacBase(const std::string& base);
 
     /// \brief Decodes base DUID provided with -b<base>.
     ///
@@ -454,6 +472,14 @@ private:
     /// \throw isc::InvalidParameter if string does not represent hex byte.
     uint8_t convertHexString(const std::string& hex_text) const;
 
+    /// \brief Opens the text file containing list of macs (one per line)
+    /// and adds them to the mac_list_ vector.
+    void loadMacs();
+
+    /// \brief Decodes a mac string into a vector of uint8_t and adds it to the
+    /// mac_list_ vector.
+    bool decodeMacString(const std::string& line);
+
     /// 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_;
@@ -528,6 +554,11 @@ private:
     /// that are used for initiating exchanges. Template packets
     /// read from files are later tuned with variable data.
     std::vector<std::string> template_file_;
+    /// A file containing a list of macs, one per line. This can be used if
+    /// you don't want to genrate Mac starting from a base mac but rather provide
+    /// the tool with a list of macs it should randomize on.
+    std::string mac_file_list_;
+    std::vector<std::vector<uint8_t> > mac_list_;
     /// Offset of transaction id in template files. First vector
     /// element points to offset for DISCOVER/SOLICIT messages,
     /// second element points to transaction id offset for
@@ -551,6 +582,8 @@ private:
     std::string wrapped_;
     /// Server name specified as last argument of command line.
     std::string server_name_;
+    /// Controls whether generated dhcpv6 test traffic should be relayed.
+    bool use_relayed_v6_;
 };
 
 } // namespace perfdhcp

+ 11 - 0
src/bin/perfdhcp/perfdhcp.xml

@@ -37,6 +37,7 @@
             <command>perfdhcp</command>
             <arg><option>-1</option></arg>
             <arg><option>-4|-6</option></arg>
+            <arg><option>-A</option></arg>
             <arg><option>-a <replaceable class="parameter">aggressivity</replaceable></option></arg>
             <arg><option>-b <replaceable class="parameter">base</replaceable></option></arg>
             <arg><option>-B</option></arg>
@@ -236,6 +237,16 @@
             </varlistentry>
 
             <varlistentry>
+                <term><option>-A</option></term>
+                <listitem>
+                    <para>
+                        When acting as a DHCPv6 behave like a relay Agent.
+                        Send out packets as if they were relayed from an agent.
+                    </para>
+                </listitem>
+            </varlistentry>
+
+            <varlistentry>
                 <term><option>-a <replaceable class="parameter">aggressivity</replaceable></option></term>
                 <listitem>
                     <para>

+ 106 - 44
src/bin/perfdhcp/test_control.cc

@@ -21,6 +21,7 @@
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/foreach.hpp>
 
+#include <algorithm>
 #include <fstream>
 #include <stdio.h>
 #include <stdlib.h>
@@ -79,8 +80,9 @@ TestControl::instance() {
     return (test_control);
 }
 
-TestControl::TestControl() {
-    reset();
+TestControl::TestControl()
+    : number_generator_(0, CommandOptions::instance().getAllMacs().size()) {
+  reset();
 }
 
 void
@@ -464,38 +466,51 @@ TestControl::factoryRequestList4(Option::Universe u,
 }
 
 std::vector<uint8_t>
-TestControl::generateMacAddress(uint8_t& randomized) const {
+TestControl::generateMacAddress(uint8_t& randomized) {
     CommandOptions& options = CommandOptions::instance();
-    uint32_t clients_num = options.getClientsNum();
-    if (clients_num < 2) {
-        return (options.getMacTemplate());
-    }
-    // Get the base MAC address. We are going to randomize part of it.
-    std::vector<uint8_t> mac_addr(options.getMacTemplate());
-    if (mac_addr.size() != HW_ETHER_LEN) {
-        isc_throw(BadValue, "invalid MAC address template specified");
-    }
-    uint32_t r = macaddr_gen_->generate();
-    randomized = 0;
-    // Randomize MAC address octets.
-    for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
-         it >= mac_addr.begin();
-         --it) {
-        // Add the random value to the current octet.
-        (*it) += r;
-        ++randomized;
-        if (r < 256) {
-            // If we are here it means that there is no sense
-            // to randomize the remaining octets of MAC address
-            // because the following bytes of random value
-            // are zero and it will have no effect.
-            break;
-        }
-        // Randomize the next octet with the following
-        // byte of random value.
-        r >>= 8;
+
+    vector<vector<uint8_t> > macs = options.getAllMacs();
+    // if we are using the -M option return a random one from the list...
+    if (macs.size() > 0) {
+      uint16_t r = number_generator_();
+      if (r >= macs.size()) {
+        r = 0;
+      }
+      return macs[r];
+
+    } else {
+      // ... otherwise use the standard behavior
+      uint32_t clients_num = options.getClientsNum();
+      if (clients_num < 2) {
+          return (options.getMacTemplate());
+      }
+      // Get the base MAC address. We are going to randomize part of it.
+      std::vector<uint8_t> mac_addr(options.getMacTemplate());
+      if (mac_addr.size() != HW_ETHER_LEN) {
+          isc_throw(BadValue, "invalid MAC address template specified");
+      }
+      uint32_t r = macaddr_gen_->generate();
+      randomized = 0;
+      // Randomize MAC address octets.
+      for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
+           it >= mac_addr.begin();
+           --it) {
+          // Add the random value to the current octet.
+          (*it) += r;
+          ++randomized;
+          if (r < 256) {
+              // If we are here it means that there is no sense
+              // to randomize the remaining octets of MAC address
+              // because the following bytes of random value
+              // are zero and it will have no effect.
+              break;
+          }
+          // Randomize the next octet with the following
+          // byte of random value.
+          r >>= 8;
+      }
+      return (mac_addr);
     }
-    return (mac_addr);
 }
 
 OptionPtr
@@ -508,20 +523,50 @@ TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const {
 }
 
 std::vector<uint8_t>
-TestControl::generateDuid(uint8_t& randomized) const {
+TestControl::generateDuid(uint8_t& randomized) {
     CommandOptions& options = CommandOptions::instance();
-    uint32_t clients_num = options.getClientsNum();
-    if ((clients_num == 0) || (clients_num == 1)) {
-        return (options.getDuidTemplate());
-    }
-    // Get the base DUID. We are going to randomize part of it.
-    std::vector<uint8_t> duid(options.getDuidTemplate());
-    // @todo: add support for DUIDs of different sizes.
     std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
-    duid.resize(duid.size());
-    std::copy(mac_addr.begin(), mac_addr.end(),
-              duid.begin() + duid.size() - mac_addr.size());
-    return (duid);
+    vector<vector<uint8_t> > macs = options.getAllMacs();
+    // pick a random mac address if we are using option -M..
+    if (macs.size() > 0) {
+      uint16_t r = number_generator_();
+      if (r >= macs.size()) {
+        r = 0;
+      }
+      std::vector<uint8_t> mac = macs[r];
+      // DUID_LL is in this format
+      //  0                   1                   2                   3
+      //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      // |               3               |    hardware type (16 bits)    |
+      // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      // .                                                               .
+      // .             link-layer address (variable length)              .
+      // .                                                               .
+      // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+      // No C++11 so initializer list support, building a vector<uint8_t> is a
+      // pain...
+      uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
+      // copy duid_ll array into the vector
+      std::vector<uint8_t> duid(duid_ll,
+                                duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
+      // put the mac address bytes at the end
+      std::copy(mac.begin(), mac.end(), duid.begin() + 4);
+      return (duid);
+    } else {
+      uint32_t clients_num = options.getClientsNum();
+      if ((clients_num == 0) || (clients_num == 1)) {
+          return (options.getDuidTemplate());
+      }
+      // Get the base DUID. We are going to randomize part of it.
+      std::vector<uint8_t> duid(options.getDuidTemplate());
+      // @todo: add support for DUIDs of different sizes.
+      duid.resize(duid.size());
+      std::copy(mac_addr.begin(), mac_addr.end(),
+                duid.begin() + duid.size() - mac_addr.size());
+      return (duid);
+    }
 }
 
 uint32_t
@@ -745,7 +790,12 @@ TestControl::openSocket() const {
 
     if (port == 0) {
         if (family == AF_INET6) {
+            // need server port (547) because the server is acting as a relay agent
             port = DHCP6_CLIENT_PORT;
+            // if acting as a relay agent change port.
+            if (options.isUseRelayedV6()) {
+              port = DHCP6_SERVER_PORT;
+            }
         } else if (options.getIpVersion() == 4) {
             port = 67; //  TODO: find out why port 68 is wrong here.
         }
@@ -2192,6 +2242,18 @@ TestControl::setDefaults6(const TestControlSocket& socket,
     pkt->setLocalAddr(socket.addr_);
     // The remote server's name or IP.
     pkt->setRemoteAddr(IOAddress(options.getServerName()));
+
+    // only act as a relay agent when told so.
+    // TODO: support more level of encapsulation, at the moment we only support
+    // one, via -A1 option.
+    if (options.isUseRelayedV6()) {
+      Pkt6::RelayInfo relay_info;
+      relay_info.msg_type_ = DHCPV6_RELAY_FORW;
+      relay_info.hop_count_ = 1;
+      relay_info.linkaddr_ = IOAddress(socket.addr_);
+      relay_info.peeraddr_ = IOAddress(socket.addr_);
+      pkt->addRelayInfo(relay_info);
+    }
 }
 
 bool

+ 6 - 2
src/bin/perfdhcp/test_control.h

@@ -15,6 +15,7 @@
 #include <dhcp/dhcp6.h>
 #include <dhcp/pkt4.h>
 #include <dhcp/pkt6.h>
+#include <util/random/random_number_generator.h>
 
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -281,6 +282,9 @@ protected:
     /// only via \ref instance method.
     TestControl();
 
+    /// Generate uniformly distributed integers in range of [min, max]
+    isc::util::random::UniformRandomIntegerGenerator number_generator_;
+
     /// \brief Check if test exit conditions fulfilled.
     ///
     /// Method checks if the test exit conditions are fulfilled.
@@ -465,7 +469,7 @@ protected:
     /// is ignored).
     /// \throw isc::BadValue if \ref generateMacAddress throws.
     /// \return vector representing DUID.
-    std::vector<uint8_t> generateDuid(uint8_t& randomized) const;
+    std::vector<uint8_t> generateDuid(uint8_t& randomized);
 
     /// \brief Generate MAC address.
     ///
@@ -481,7 +485,7 @@ protected:
     /// \throw isc::BadValue if MAC address template (default or specified
     /// from the command line) has invalid size (expected 6 octets).
     /// \return generated MAC address.
-    std::vector<uint8_t> generateMacAddress(uint8_t& randomized) const;
+    std::vector<uint8_t> generateMacAddress(uint8_t& randomized);
 
     /// \brief generate transaction id.
     ///

+ 46 - 1
src/bin/perfdhcp/tests/command_options_unittest.cc

@@ -7,9 +7,10 @@
 #include <config.h>
 
 #include <cstddef>
+#include <fstream>
+#include <gtest/gtest.h>
 #include <stdint.h>
 #include <string>
-#include <gtest/gtest.h>
 #include <boost/date_time/posix_time/posix_time.hpp>
 
 #include <dhcp/iface_mgr.h>
@@ -152,6 +153,19 @@ protected:
         return (CommandOptionsHelper::process(cmdline));
     }
 
+    /// \brief Get full path to a file in testdata directory.
+    ///
+    /// \param filename filename being appended to absolute
+    /// path to testdata directory
+    ///
+    /// \return full path to a file in testdata directory.
+    std::string getFullPath(const std::string& filename) const {
+        std::ostringstream stream;
+        stream << TEST_DATA_DIR << "/" << filename;
+        return (stream.str());
+    }
+
+
     /// \brief Check default initialized values
     ///
     /// Check if initialized values are correct
@@ -263,6 +277,15 @@ TEST_F(CommandOptionsTest, UseFirst) {
     EXPECT_NO_THROW(process("perfdhcp -1 -B -l ethx all"));
     EXPECT_TRUE(opt.isUseFirst());
 }
+
+TEST_F(CommandOptionsTest, UseRelayV6) {
+    CommandOptions& opt = CommandOptions::instance();
+    EXPECT_NO_THROW(process("perfdhcp -6 -A1 -l ethx all"));
+    EXPECT_TRUE(opt.isUseRelayedV6());
+    // -4 and -A must not coexist
+    EXPECT_THROW(process("perfdhcp -4 -A1 -l ethx all"), isc::InvalidParameter);
+}
+
 TEST_F(CommandOptionsTest, IpVersion) {
     CommandOptions& opt = CommandOptions::instance();
     EXPECT_NO_THROW(process("perfdhcp -6 -l ethx -c -i all"));
@@ -821,3 +844,25 @@ TEST_F(CommandOptionsTest, Server) {
     ASSERT_NO_THROW(process("perfdhcp -6 abc"));
     EXPECT_EQ("abc", opt.getServerName());
 }
+
+TEST_F(CommandOptionsTest, LoadMacsFromFile) {
+  CommandOptions &opt = CommandOptions::instance();
+
+  std::string mac_list_full_path = getFullPath("mac_list.txt");
+  std::ostringstream cmd;
+  cmd << "perfdhcp -M " << mac_list_full_path << " abc";
+  EXPECT_NO_THROW(process(cmd.str()));
+  EXPECT_EQ(mac_list_full_path, opt.getMacListFile());
+
+  std::vector<std::vector<uint8_t> > m = opt.getAllMacs();
+  EXPECT_EQ(4, m.size());
+}
+
+TEST_F(CommandOptionsTest, LoadMacsFromFileNegativeCases) {
+  // Negative test cases
+  // Too many -M parameters, expected only 1
+  EXPECT_THROW(process("perfdhcp -M foo -M foo1 all"), isc::InvalidParameter);
+  // -M option can't use with -b option
+  EXPECT_THROW(process("perfdhcp -M foo -b mac=1234 all"),
+               isc::InvalidParameter);
+}

+ 78 - 0
src/bin/perfdhcp/tests/test_control_unittest.cc

@@ -1119,6 +1119,33 @@ TEST_F(TestControlTest, GenerateDuid) {
     // Simulate 50 clients. Different DUID will be generated.
     ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
     testDuid();
+
+    // Checks that the random mac address returned by generateDuid
+    // is in the list of mac addresses in the mac_list.txt data file
+    std::string mac_list_full_path = getFullPath("mac_list.txt");
+    std::ostringstream cmd;
+    cmd << "perfdhcp -M " << mac_list_full_path << " abc";
+    ASSERT_NO_THROW(processCmdLine(cmd.str()));
+    // Initialize Test Controller.
+    NakedTestControl tc;
+    uint8_t randomized = 0;
+    std::vector<uint8_t> generated_duid = tc.generateDuid(randomized);
+    // check that generated_duid is DUID_LL
+    DuidPtr duid(new DUID(generated_duid));
+    ASSERT_EQ(duid->getType(), DUID::DUID_LL);
+    // make sure it's on the list
+    CommandOptions& options = CommandOptions::instance();
+    vector<vector<uint8_t> > macs = options.getAllMacs();
+    // duid_ll is made of 2 bytes of duid type, 2 bytes of hardwaretype,
+    // then 6 bytes of mac
+    vector<uint8_t> mac(6);
+    std::copy(
+      generated_duid.begin() + 4, generated_duid.begin() + 10, mac.begin());
+    // check that mac is in macs
+    ASSERT_TRUE(
+        std::find(macs.begin(), macs.end(), mac) !=
+        macs.end()
+    );
 }
 
 TEST_F(TestControlTest, MisMatchVerionServer) {
@@ -1142,6 +1169,21 @@ TEST_F(TestControlTest, GenerateMacAddress) {
     // Simulate 50 clients. Different MAC addresses will be generated.
     ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
     testMacAddress();
+
+    // Checks that the random mac address returned by generateMacAddress
+    // is in the list of mac addresses in the mac_list.txt data file
+    std::string mac_list_full_path = getFullPath("mac_list.txt");
+    std::ostringstream cmd;
+    cmd << "perfdhcp -M " << mac_list_full_path << " abc";
+    ASSERT_NO_THROW(processCmdLine(cmd.str()));
+    // Initialize Test Controller.
+    NakedTestControl tc;
+    uint8_t randomized = 0;
+    std::vector<uint8_t> mac = tc.generateMacAddress(randomized);
+    CommandOptions& options = CommandOptions::instance();
+    vector<vector<uint8_t> > macs = options.getAllMacs();
+    // check that mac is in macs
+    ASSERT_NE(std::find(macs.begin(), macs.end(), mac), macs.end());
 }
 
 TEST_F(TestControlTest, Options4) {
@@ -1368,6 +1410,42 @@ TEST_F(TestControlTest, Packet6) {
     }
 }
 
+TEST_F(TestControlTest, Packet6Relayed) {
+    // Use Interface Manager to get the local loopback interface.
+    // If the interface can't be found we don't want to fail test.
+    std::string loopback_iface(getLocalLoopback());
+    if (!loopback_iface.empty()) {
+        ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
+                                       " -A1 -L 10547 servers"));
+        NakedTestControl tc;
+        int sock_handle = 0;
+        // Create the socket. It will be needed to set packet's
+        // parameters.
+        ASSERT_NO_THROW(sock_handle = tc.openSocket());
+        TestControl::TestControlSocket sock(sock_handle);
+        uint32_t transid = 123;
+        boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+        // Set packet's parameters.
+        ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
+        // Validate if parameters have been set correctly.
+        EXPECT_EQ(loopback_iface, pkt6->getIface());
+        EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
+        EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
+        EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
+        EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
+        EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
+        // check relay info
+        EXPECT_EQ(pkt6->relay_info_.size(), 1);
+        EXPECT_EQ(pkt6->relay_info_[0].hop_count_, 1);
+        EXPECT_EQ(pkt6->relay_info_[0].msg_type_, DHCPV6_RELAY_FORW);
+        EXPECT_EQ(pkt6->relay_info_[0].linkaddr_, sock.addr_);
+        EXPECT_EQ(pkt6->relay_info_[0].peeraddr_, sock.addr_);
+    } else {
+        std::cout << "Unable to find the loopback interface. Skip test. "
+                  << std::endl;
+    }
+}
+
 TEST_F(TestControlTest, Packet4Exchange) {
     // Get the local loopback interface to open socket on
     // it and test packets exchanges. We don't want to fail

+ 1 - 0
src/bin/perfdhcp/tests/testdata/Makefile.am

@@ -2,3 +2,4 @@ SUBDIRS = .
 
 EXTRA_DIST = discover-example.hex request4-example.hex
 EXTRA_DIST += solicit-example.hex request6-example.hex
+EXTRA_DIST += mac_list.txt

+ 4 - 0
src/bin/perfdhcp/tests/testdata/mac_list.txt

@@ -0,0 +1,4 @@
+11:22:33:44:55:66
+11:22:33:44:55:77
+11:22:33:44:55:88
+11:22:33:44:55:99