]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[1959] Added support for packet templates.
authorMarcin Siodelski <marcin@isc.org>
Wed, 29 Aug 2012 16:01:01 +0000 (18:01 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 29 Aug 2012 16:01:01 +0000 (18:01 +0200)
14 files changed:
tests/tools/perfdhcp/localized_option.h
tests/tools/perfdhcp/perf_pkt4.cc
tests/tools/perfdhcp/perf_pkt4.h
tests/tools/perfdhcp/perf_pkt6.cc
tests/tools/perfdhcp/perf_pkt6.h
tests/tools/perfdhcp/pkt_transform.cc
tests/tools/perfdhcp/pkt_transform.h
tests/tools/perfdhcp/templates/discover-example.hex [new file with mode: 0644]
tests/tools/perfdhcp/templates/request4-example.hex [new file with mode: 0644]
tests/tools/perfdhcp/templates/request6-example.hex [new file with mode: 0644]
tests/tools/perfdhcp/templates/solicit-example.hex [new file with mode: 0644]
tests/tools/perfdhcp/test_control.cc
tests/tools/perfdhcp/test_control.h
tests/tools/perfdhcp/tests/test_control_unittest.cc

index 5374684f89855d026f47c6da3aff87a4f122a564..f93089fbade6f2487bd96ce5fec91bc67261d6ab 100644 (file)
@@ -16,6 +16,8 @@
 #define __LOCALIZED_OPTION_H
 
 #include <dhcp/pkt6.h>
+#include <dhcp/option6_ia.h>
+#include <util/buffer.h>
 
 namespace isc {
 namespace perfdhcp {
@@ -51,7 +53,7 @@ public:
                     uint16_t type,
                     const dhcp::OptionBuffer& data) :
         dhcp::Option(u, type, data),
-        offset_(0) {
+        offset_(0), option_valid_(true) {
     }
 
 
@@ -66,7 +68,49 @@ public:
                     const dhcp::OptionBuffer& data,
                     const size_t offset) :
         dhcp::Option(u, type, data),
-        offset_(offset) {
+        offset_(offset), option_valid_(true) {
+    }
+
+    /// \brief Copy constructor, creates LocalizedOption from Option6IA.
+    ///
+    /// This copy constructor creates regular option from Option6IA.
+    /// The data from Option6IA data members are copied to
+    /// option buffer in appropriate sequence.
+    ///
+    /// \param opt_ia option to be copied.
+    /// \param offset location of the option in a packet.
+    LocalizedOption(const boost::shared_ptr<dhcp::Option6IA>& opt_ia,
+                    const size_t offset) :
+        dhcp::Option(Option::V6, 0, dhcp::OptionBuffer()),
+        offset_(offset), option_valid_(false) {
+        // If given option is NULL we will mark this new option
+        // as invalid. User may query if option is valid when
+        // object is created.
+        if (opt_ia) {
+            // Set universe and type.
+            universe_ = opt_ia->getUniverse();
+            type_ = opt_ia->getType();
+            util::OutputBuffer buf(opt_ia->len() - opt_ia->getHeaderLen());
+            try {
+                // Try to pack option data into the temporary buffer.
+                opt_ia->pack(buf);
+                if (buf.getLength() > 0) {
+                    const char* buf_data = static_cast<const char*>(buf.getData());
+                    // Option has been packed along with option type flag
+                    // and transaction id so we have to skip first 4 bytes
+                    // when copying temporary buffer option buffer.
+                    data_.assign(buf_data + 4, buf_data + buf.getLength());
+                }
+                option_valid_ = true;
+            } catch (const Exception&) {
+                // If there was an exception somewhere when packing
+                // the data into the buffer we assume that option is
+                // not valid and should not be used.
+                option_valid_ = false;
+            }
+        } else {
+            option_valid_ = false;
+        }
     }
 
     /// \brief Constructor, sets default (0) option offset
@@ -84,7 +128,7 @@ public:
                     dhcp::OptionBufferConstIter first,
                     dhcp::OptionBufferConstIter last) :
         dhcp::Option(u, type, first, last),
-        offset_(0) {
+        offset_(0), option_valid_(true) {
     }
 
 
@@ -104,7 +148,7 @@ public:
                     dhcp::OptionBufferConstIter first,
                     dhcp::OptionBufferConstIter last, const size_t offset) :
         dhcp::Option(u, type, first, last),
-        offset_(offset) {
+        offset_(offset), option_valid_(true) {
     }
 
     /// \brief Returns offset of an option in a DHCP packet.
@@ -112,8 +156,16 @@ public:
     /// \return option offset in a packet
     size_t getOffset() const { return offset_; };
 
+    /// \brief Checks if option is valid.
+    ///
+    /// \return true, if option is valid.
+    virtual bool valid() {
+        return (Option::valid() && option_valid_);
+    }
+
 private:
     size_t offset_;   ///< Offset of DHCP option in a packet
+    bool option_valid_; ///< Is option valid.
 };
 
 
index 3f733afacc92cf4d9331e18558ebdd2796c96ca5..3ccef94e9880054a51f1fcdbaf27c3b68da9c307 100644 (file)
@@ -16,7 +16,6 @@
 #include <dhcp/dhcp6.h>
 
 #include "perf_pkt4.h"
-#include "pkt_transform.h"
 
 using namespace std;
 using namespace isc;
@@ -58,5 +57,14 @@ PerfPkt4::rawUnpack() {
     return (res);
 }
 
+void
+PerfPkt4::writeAt(size_t dest_pos,
+                  std::vector<uint8_t>::iterator first,
+                  std::vector<uint8_t>::iterator last) {
+    return (PktTransform::writeAt(data_, dest_pos, first, last));
+}
+
+
+
 } // namespace perfdhcp
 } // namespace isc
index f4cc440773cf47e29a6c79c53e4545dc102bccd0..1a19a08749efccdaa40884a7e53f7c160dc9c5bc 100644 (file)
@@ -20,6 +20,7 @@
 #include <dhcp/pkt4.h>
 
 #include "localized_option.h"
+#include "pkt_transform.h"
 
 namespace isc {
 namespace perfdhcp {
@@ -102,11 +103,37 @@ public:
     /// \return false If unpack operation failed.
     bool rawUnpack();
 
+    /// \brief Replace contents of buffer with data.
+    ///
+    /// Function replaces part of the buffer with data from vector.
+    ///
+    /// \param dest_pos position in buffer where data is replaced.
+    /// \param first beginning of data range in source vector.
+    /// \param last end of data range in source vector.
+    void writeAt(size_t dest_pos,
+                 std::vector<uint8_t>::iterator first,
+                 std::vector<uint8_t>::iterator last);
+
+    
+    /// \brief Replace contents of buffer with value.
+    ///
+    /// Function replaces part of buffer with value.
+    ///
+    /// \param dest_pos position in buffer where value is
+    /// to be written.
+    /// \param val value to be written.
+    template<typename T>
+    void writeValueAt(size_t dest_pos, T val) {
+        PktTransform::writeValueAt<T>(data_, dest_pos, val);
+    }
+
 private:
     size_t transid_offset_;      ///< transaction id offset
 
 };
 
+typedef boost::shared_ptr<PerfPkt4> PerfPkt4Ptr;
+
 } // namespace perfdhcp
 } // namespace isc
 
index 24cfb931a9b3c294912bdb90caed523f0d38c4be..56fe9dfd775cc6b59546c9c4373e92d2094ac395 100644 (file)
@@ -60,5 +60,13 @@ PerfPkt6::rawUnpack() {
     return (res);
 }
 
+void
+PerfPkt6::writeAt(size_t dest_pos,
+                     std::vector<uint8_t>::iterator first,
+                     std::vector<uint8_t>::iterator last) {
+    return (PktTransform::writeAt(data_, dest_pos, first, last));
+}
+
+
 } // namespace perfdhcp
 } // namespace isc
index 94fe47bada36f3ee94d0ce89e2e8cdf23e3a6463..25fb4e53a4b6513b7193323cbc32b74711ebe954 100644 (file)
@@ -20,6 +20,7 @@
 #include <dhcp/pkt6.h>
 
 #include "localized_option.h"
+#include "pkt_transform.h"
 
 namespace isc {
 namespace perfdhcp {
@@ -102,11 +103,36 @@ public:
     /// \return false if unpack operation failed.
     bool rawUnpack();
 
+    /// \brief Replace contents of buffer with data.
+    ///
+    /// Function replaces part of the buffer with data from vector.
+    ///
+    /// \param dest_pos position in buffer where data is replaced.
+    /// \param first beginning of data range in source vector.
+    /// \param last end of data range in source vector.
+    void writeAt(size_t dest_pos,
+                 std::vector<uint8_t>::iterator first,
+                 std::vector<uint8_t>::iterator last);
+
+    /// \brief Replace contents of buffer with value.
+    ///
+    /// Function replaces part of buffer with value.
+    ///
+    /// \param dest_pos position in buffer where value is
+    /// to be written.
+    /// \param val value to be written.
+    template<typename T>
+    void writeValueAt(size_t dest_pos, T val) {
+        PktTransform::writeValueAt<T>(data_, dest_pos, val);
+    }
+
 private:
     size_t transid_offset_;      ///< transaction id offset
 
 };
 
+typedef boost::shared_ptr<PerfPkt6> PerfPkt6Ptr;
+
 } // namespace perfdhcp
 } // namespace isc
 
index 5ed39bfc5f5d5849bdf94b589b18e4fa5b7ac928..d87ca19c3b3ea4b4523151ca817c16accbc36f98 100644 (file)
@@ -216,7 +216,18 @@ PktTransform::unpackOptions(const OptionBuffer& in_buffer,
                         in_buffer.begin() + offset + opt_len);
     }
 }
-
+    
+void
+PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
+                      dhcp::OptionBuffer::iterator first,
+                      dhcp::OptionBuffer::iterator last) {
+    int i = 0;
+    for (std::vector<uint8_t>::iterator it = first;
+         it != last;
+         ++it, ++i) {
+        in_buffer[dest_pos + i] = *it;
+    }
+}
 
 } // namespace perfdhcp
 } // namespace isc
index 7fb19f48c4a4df8a5ffab3d07bb574aeb32853bc..e7665a2dc84755eb2782b5b12567aaf526d7aa2e 100644 (file)
@@ -92,6 +92,33 @@ public:
                        const size_t transid_offset,
                        uint32_t& transid);
 
+    /// \brief Replace contents of buffer with vector.
+    ///
+    /// Function replaces data of the buffer with data from vector.
+    ///
+    /// \param in_buffer destination buffer.
+    /// \param dest_pos position in destination buffer.
+    /// \param first beginning of data range in source vector.
+    /// \param last end of data range in source vector.
+    static void writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
+                        std::vector<uint8_t>::iterator first,
+                        std::vector<uint8_t>::iterator last);
+
+    /// \brief Replace contents of one vector with uint16 value.
+    ///
+    /// Function replaces data inside one vector with uint16_t value.
+    ///
+    /// \param in_buffer destination buffer.
+    /// \param dest_pos position in destination buffer.
+    /// \param val value to be written.
+    template<typename T>
+    static void writeValueAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
+                        T val) {
+        for (int i = 0; i < sizeof(T); ++i) {
+            in_buffer[dest_pos + i] = (val >> (sizeof(T) - 8 * i - 1)) & 0xFF;
+        }
+    }
+
 private:
     /// \brief Replaces contents of options in a buffer.
     ///
@@ -131,6 +158,7 @@ private:
     /// \throw isc::Unexpected if options unpack failed.
     static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
                               const dhcp::Option::OptionCollection& options);
+
 };
 
 } // namespace perfdhcp
diff --git a/tests/tools/perfdhcp/templates/discover-example.hex b/tests/tools/perfdhcp/templates/discover-example.hex
new file mode 100644 (file)
index 0000000..9a6e5ea
--- /dev/null
@@ -0,0 +1 @@
+01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/request4-example.hex b/tests/tools/perfdhcp/templates/request4-example.hex
new file mode 100644 (file)
index 0000000..32447d6
--- /dev/null
@@ -0,0 +1 @@
+01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/request6-example.hex b/tests/tools/perfdhcp/templates/request6-example.hex
new file mode 100644 (file)
index 0000000..1e3e76f
--- /dev/null
@@ -0,0 +1 @@
+03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000
\ No newline at end of file
diff --git a/tests/tools/perfdhcp/templates/solicit-example.hex b/tests/tools/perfdhcp/templates/solicit-example.hex
new file mode 100644 (file)
index 0000000..41c5ad3
--- /dev/null
@@ -0,0 +1 @@
+015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000
\ No newline at end of file
index c58631f841e6f59a946eaf3de00a385978cd1e3e..6da254918244a733327ffab19ae4227ee79eda85 100644 (file)
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+
+#include <fstream>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/dhcp4.h>
+#include <dhcp/option6_ia.h>
 #include "test_control.h"
 #include "command_options.h"
+#include "perf_pkt4.h"
+#include "perf_pkt6.h"
 
 using namespace std;
 using namespace boost;
@@ -91,13 +96,13 @@ TestControl::byte2Hex(const uint8_t b) const {
     const int b0 = b % 16;
     ostringstream stream;
     stream << std::hex << b1 << b0 << std::dec;
-    return stream.str();
+    return (stream.str());
 }
 
 bool
 TestControl::checkExitConditions() const {
     if (interrupted_) {
-        return(true);
+        return (true);
     }
     CommandOptions& options = CommandOptions::instance();
     bool test_period_reached = false;
@@ -117,9 +122,9 @@ TestControl::checkExitConditions() const {
     }
     if (test_period_reached) {
         if (testDiags('e')) {
-            std::cout << "Reached test period." << std::endl;
+            std::cout << "reached test-period." << std::endl;
         }
-        return(true);
+        return (true);
     }
 
     bool max_requests = false;
@@ -153,9 +158,9 @@ TestControl::checkExitConditions() const {
     }
     if (max_requests) {
         if (testDiags('e')) {
-            std::cout << "Reached test period." << std::endl;
+            std::cout << "Reached max requests limit." << std::endl;
         }
-        return(true);
+        return (true);
     }
 
     // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
@@ -191,7 +196,7 @@ TestControl::checkExitConditions() const {
         if (testDiags('e')) {
             std::cout << "Reached maximum drops number." << std::endl;
         }
-        return(true);
+        return (true);
     }
 
     // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
@@ -236,20 +241,19 @@ TestControl::checkExitConditions() const {
         if (testDiags('e')) {
             std::cout << "Reached maximum percentage of drops." << std::endl;
         }
-        return(true);
+        return (true);
     }
-
-    return(false);
+    return (false);
 }
 
 OptionPtr
 TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
                                  const OptionBuffer& buf) {
     if (buf.size() == 2) {
-        return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf));
+        return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)));
     } else if (buf.size() == 0) {
-        return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
-                                    OptionBuffer(2, 0)));
+        return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
+                                     OptionBuffer(2, 0))));
     }
     isc_throw(isc::BadValue,
               "elapsed time option buffer size has to be 0 or 2");
@@ -259,7 +263,7 @@ OptionPtr
 TestControl::factoryGeneric(Option::Universe u, uint16_t type,
                             const OptionBuffer& buf) {
     OptionPtr opt(new Option(u, type, buf));
-    return opt;
+    return (opt);
 }
 
 OptionPtr
@@ -274,13 +278,13 @@ TestControl::factoryIana6(Option::Universe, uint16_t,
     for (int i = 0;  i < buf.size(); ++i) {
         buf_ia_na.push_back(buf[i]);
     }
-    return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na));
+    return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
 }
 
 OptionPtr
 TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
                                  const OptionBuffer&) {
-    return OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer()));
+    return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())));
 }
 
 OptionPtr
@@ -288,11 +292,11 @@ TestControl::factoryOptionRequestOption6(Option::Universe,
                                          uint16_t,
                                          const OptionBuffer&) {
     const uint8_t buf_array[] = {
-        D6O_NAME_SERVERS,
-        D6O_DOMAIN_SEARCH
+        0, D6O_NAME_SERVERS,
+        0, D6O_DOMAIN_SEARCH
     };
     OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
-    return OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options));
+    return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)));
 }
 
 
@@ -313,15 +317,15 @@ TestControl::factoryRequestList4(Option::Universe u,
     OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
     OptionPtr opt(new Option(u, type, buf));
     opt->setData(buf_with_options.begin(), buf_with_options.end());
-    return opt;
+    return (opt);
 }
 
 std::vector<uint8_t>
-TestControl::generateMacAddress() const {
+TestControl::generateMacAddress(uint8_t& randomized) const {
     CommandOptions& options = CommandOptions::instance();
     uint32_t clients_num = options.getClientsNum();
     if ((clients_num == 0) || (clients_num == 1)) {
-        return options.getMacPrefix();
+        return (options.getMacPrefix());
     }
     // Get the base MAC address. We are going to randomize part of it.
     std::vector<uint8_t> mac_addr(options.getMacPrefix());
@@ -333,12 +337,14 @@ TestControl::generateMacAddress() const {
     // will guarantee that every client has exactly one random MAC
     // address assigned.
     r %= clients_num;
+    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
@@ -350,24 +356,24 @@ TestControl::generateMacAddress() const {
         // byte of random value.
         r >>= 8;
     }
-    return mac_addr;
+    return (mac_addr);
 }
 
 std::vector<uint8_t>
-TestControl::generateDuid() const {
+TestControl::generateDuid(uint8_t& randomized) const {
     CommandOptions& options = CommandOptions::instance();
     uint32_t clients_num = options.getClientsNum();
     if ((clients_num == 0) || (clients_num == 1)) {
-        return options.getDuidPrefix();
+        return (options.getDuidPrefix());
     }
     // Get the base DUID. We are going to randomize part of it.
     std::vector<uint8_t> duid(options.getDuidPrefix());
-    std::vector<uint8_t> mac_addr(generateMacAddress());
+    std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
     duid.resize(duid.size() - mac_addr.size());
     for (int i = 0; i < mac_addr.size(); ++i) {
         duid.push_back(mac_addr[i]);
     }
-    return duid;
+    return (duid);
 }
 
 uint64_t
@@ -413,20 +419,28 @@ uint64_t
 TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
     uint8_t ip_version = CommandOptions::instance().getIpVersion();
     if (ip_version == 4) {
-        return(stats_mgr4_->getRcvdPacketsNum(xchg_type));
+        return (stats_mgr4_->getRcvdPacketsNum(xchg_type));
     }
-    return(stats_mgr6_->
-           getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+    return (stats_mgr6_->
+            getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
 }
 
 uint64_t
 TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
     uint8_t ip_version = CommandOptions::instance().getIpVersion();
     if (ip_version == 4) {
-        return(stats_mgr4_->getSentPacketsNum(xchg_type));
+        return (stats_mgr4_->getSentPacketsNum(xchg_type));
+    }
+    return (stats_mgr6_->
+            getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+}
+
+TestControl::TemplateBuffer
+TestControl::getTemplateBuffer(const size_t idx) const {
+    if (template_buffers_.size() > idx) {
+        return (template_buffers_[idx]);
     }
-    return(stats_mgr6_->
-           getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+    return (TemplateBuffer());
 }
 
 void
@@ -436,13 +450,13 @@ TestControl::handleInterrupt(int) {
 
 void
 TestControl::initPacketTemplates() {
+    template_buffers_.clear();
     CommandOptions& options = CommandOptions::instance();
     std::vector<std::string> template_files = options.getTemplateFiles();
     for (std::vector<std::string>::const_iterator it = template_files.begin();
          it != template_files.end();
          ++it) {
-
-        std::cout << "Open file " << *it << std::endl;
+        readPacketTemplate(*it);
     }
 }
 
@@ -565,7 +579,7 @@ TestControl::openSocket() const {
         }
     }
 
-    return(sock);
+    return (sock);
 }
 
 void
@@ -574,7 +588,6 @@ TestControl::printDiagnostics() const {
     if (testDiags('a')) {
         // Print all command line parameters.
         options.printCommandLine();
-        
         // Print MAC and DUID.
         std::cout << "Set MAC to " << vector2Hex(options.getMacPrefix(), "::")
                   << std::endl;
@@ -661,7 +674,38 @@ TestControl::vector2Hex(const std::vector<uint8_t>& vec,
             stream << separator << byte2Hex(*it);
         }
     }
-    return(stream.str());
+    return (stream.str());
+}
+
+void
+TestControl::readPacketTemplate(const std::string& file_name) {
+    std::ifstream temp_file;
+    temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
+    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) {
+        temp_file.close();
+        isc_throw(BadValue, "odd number of digits in template file");
+    }
+    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);
+    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");
+        }
+        stringstream s;
+        s << "0x" << hex_digits[i] << hex_digits[i+1];
+        int b;
+        s >> std::hex >> b;
+        binary_stream.push_back(static_cast<uint8_t>(b));
+    }
+    template_buffers_.push_back(binary_stream);
 }
 
 void
@@ -673,7 +717,11 @@ TestControl::receivePacket4(const TestControlSocket& socket,
         CommandOptions::ExchangeMode xchg_mode =
             CommandOptions::instance().getExchangeMode();
         if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
-            sendRequest4(socket, pkt4);
+            if (template_buffers_.size() < 2) {
+                sendRequest4(socket, discover_pkt4, pkt4);
+            } else {
+                sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4);
+            }
         }
     } else if (pkt4->getType() == DHCPACK) {
         stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4);
@@ -690,7 +738,11 @@ TestControl::receivePacket6(const TestControlSocket& socket,
         CommandOptions::ExchangeMode xchg_mode =
             CommandOptions::instance().getExchangeMode();
         if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
-            sendRequest6(socket, solicit_pkt6, pkt6);
+            if (template_buffers_.size() < 2) {
+                sendRequest6(socket, solicit_pkt6, pkt6);
+            } else {
+                sendRequest6(socket, template_buffers_[1], solicit_pkt6, pkt6);
+            }
         }
     } else if (packet_type == DHCPV6_REPLY) {
         stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
@@ -740,6 +792,7 @@ TestControl::registerOptionFactories4() const {
         LibDHCP::OptionFactoryRegister(Option::V4,
                                        DHO_DHCP_MESSAGE_TYPE,
                                        &TestControl::factoryGeneric);
+        // DHCP_SERVER_IDENTIFIER option factory.
         LibDHCP::OptionFactoryRegister(Option::V4,
                                        DHO_DHCP_SERVER_IDENTIFIER,
                                        &TestControl::factoryGeneric);
@@ -755,21 +808,27 @@ void
 TestControl::registerOptionFactories6() const {
     static bool factories_registered = false;
     if (!factories_registered) {
+        // D60_ELAPSED_TIME
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_ELAPSED_TIME,
                                        &TestControl::factoryElapsedTime6);
+        // D6O_RAPID_COMMIT
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_RAPID_COMMIT,
                                        &TestControl::factoryRapidCommit6);
+        // D6O_ORO (option request option) factory.
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_ORO,
                                        &TestControl::factoryOptionRequestOption6);
+        // D6O_CLIENTID option factory.
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_CLIENTID,
                                        &TestControl::factoryGeneric);
+        // D6O_SERVERID option factory.
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_SERVERID,
                                        &TestControl::factoryGeneric);
+        // D6O_IA_NA option factory.
         LibDHCP::OptionFactoryRegister(Option::V6,
                                        D6O_IA_NA,
                                        &TestControl::factoryIana6);
@@ -821,39 +880,57 @@ TestControl::run() {
                   "command options must be parsed before running a test");
     }
 
+    // Diagnostics is command line options mainly.
     printDiagnostics();
-
+    // Option factories have to be registered.
     registerOptionFactories();
     TestControlSocket socket(openSocket());
-
     // Initialize packet templates.
     initPacketTemplates();
-
     // Initialize randomization seed.
     if (options.isSeeded()) {
         srandom(options.getSeed());
     }
-
     // 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) {
-            sendDiscover4(socket, do_preload);
+            // No template buffer means no -T option specified.
+            // We will build packet ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendDiscover4(socket, do_preload);
+            } else {
+                const uint8_t template_idx = 0;
+                sendDiscover4(socket, template_buffers_[template_idx],
+                              do_preload);
+            }
         } else if (options.getIpVersion() == 6) {
-            sendSolicit6(socket, do_preload);
+            // No template buffer means no -T option specified.
+            // We will build packet ourselfs.
+            if (template_buffers_.size() == 0) {
+                sendSolicit6(socket, do_preload);
+            } else {
+                const uint8_t template_idx = 0;
+                sendSolicit6(socket, template_buffers_[template_idx],
+                             do_preload);
+            }
         }
     }
-
+    // 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();
         if ((packets_due == 0) && testDiags('i')) {
             if (options.getIpVersion() == 4) {
@@ -866,19 +943,36 @@ 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) {
-                sendDiscover4(socket);
+                // No template packets means that no -T option was specified.
+                // We have to build packets ourselfs.
+                if (template_buffers_.size() == 0) {
+                    sendDiscover4(socket);
+                } else {
+                    const uint8_t template_idx = 0;
+                    sendDiscover4(socket, template_buffers_[template_idx]);
+                }
             } else {
-                sendSolicit6(socket);
+                // No template packets means that no -T option was specified.
+                // We have to build packets ourselfs.
+                if (template_buffers_.size() == 0) {
+                    sendSolicit6(socket);
+                } else {
+                    const uint8_t template_idx = 0;
+                    sendSolicit6(socket, template_buffers_[template_idx]);
+                }
             }
         }
+        // Report delay means that user requested printing number
+        // of sent/received/dropped packets repeatedly.
         if (options.getReportDelay() > 0) {
             printIntermediateStats();
         }
     }
     printStats();
+    // Print server id.
     if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
         std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
     }
@@ -893,7 +987,8 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
                            const bool preload /*= false*/) {
     last_sent_ = microsec_clock::universal_time();
     // Generate the MAC address to be passed in the packet.
-    std::vector<uint8_t> mac_address = generateMacAddress();
+    uint8_t randomized = 0;
+    std::vector<uint8_t> mac_address = generateMacAddress(randomized);
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
@@ -911,6 +1006,10 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     // Set client's and server's ports as well as server's address,
     // and local (relay) address.
     setDefaults4(socket, pkt4);
+
+    // Set hardware address
+    pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
+
     pkt4->pack();
     IfaceMgr::instance().send(pkt4);
     if (!preload) {
@@ -922,8 +1021,70 @@ TestControl::sendDiscover4(const TestControlSocket& socket,
     }
 }
 
+void
+TestControl::sendDiscover4(const TestControlSocket& socket,
+                           const std::vector<uint8_t>& template_buf,
+                           const bool preload /* = false */) {
+    // 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;
+    // Generate the MAC address to be passed in the packet.
+    uint8_t randomized = 0;
+    std::vector<uint8_t> mac_address = generateMacAddress(randomized);
+    // 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];
+    }
+    // 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;
+    // Create temporary buffer with template contents. We will
+    // modify this temporary buffer but we don't want to modify
+    // the original template.
+    std::vector<uint8_t> in_buf(template_buf.begin(),
+                                template_buf.end());
+    // Check if we are not going out of bounds.
+    if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
+        isc_throw(OutOfRange, "randomization offset is out of bounds");
+    }
+    PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
+                                  transid_offset,
+                                  transid));
+
+    // Replace MAC address in the template with actual MAC address.
+    pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
+    // Create a packet from the temporary buffer.
+    setDefaults4(socket, static_pointer_cast<Pkt4>(pkt4));
+    // Pack the input packet buffer to output buffer so as it can
+    // be sent to server.
+    pkt4->rawPack();
+    IfaceMgr::instance().send(static_pointer_cast<Pkt4>(pkt4));
+    if (!preload) {
+        if (!stats_mgr4_) {
+            isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
+                      "hasn't been initialized");
+        }
+        // Update packet stats.
+        stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
+                                    static_pointer_cast<Pkt4>(pkt4));
+    }
+}
+
 void
 TestControl::sendRequest4(const TestControlSocket& socket,
+                          const dhcp::Pkt4Ptr& discover_pkt4,
                           const dhcp::Pkt4Ptr& offer_pkt4) {
     const uint32_t transid = generateTransid();
     Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
@@ -958,7 +1119,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     OptionPtr opt_requested_address =
         OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS,
                              OptionBuffer()));
-    opt_requested_address->setUint32(yiaddr);
+    opt_requested_address->setUint32(static_cast<uint32_t>(yiaddr));
     pkt4->addOption(opt_requested_address);
     OptionPtr opt_parameter_list =
         Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
@@ -966,6 +1127,15 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     // Set client's and server's ports as well as server's address,
     // and local (relay) address.
     setDefaults4(socket, pkt4);
+
+    // Set hardware address
+    const uint8_t* chaddr = offer_pkt4->getChaddr();
+    std::vector<uint8_t> mac_address(chaddr, chaddr + HW_ETHER_LEN);
+    pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
+    // Set elapsed time.
+    uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
+    pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
+    // Prepare on wire data to send.
     pkt4->pack();
     IfaceMgr::instance().send(pkt4);
     if (!stats_mgr4_) {
@@ -975,6 +1145,124 @@ TestControl::sendRequest4(const TestControlSocket& socket,
     stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
 }
 
+void
+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];
+    }
+    // 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;
+    // Create temporaru buffer from the template.
+    std::vector<uint8_t> in_buf(template_buf.begin(),
+                                template_buf.end());
+    // Check if given randomization offset is not out of bounds.
+    if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
+        isc_throw(OutOfRange, "randomization offset is out of bounds");
+    }
+
+    // Create packet from the temporary buffer.
+    PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
+                                  transid_offset,
+                                  transid));
+
+     // Set hardware address from OFFER packet received.
+    const uint8_t* chaddr = offer_pkt4->getChaddr();
+    std::vector<uint8_t> mac_address(chaddr, chaddr + HW_ETHER_LEN);
+    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();
+    }
+    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();
+    }
+    if (CommandOptions::instance().isUseFirst() &&
+        (first_packet_serverid_.size() > 0)) {
+        boost::shared_ptr<LocalizedOption>
+            opt_serverid(new LocalizedOption(Option::V4,
+                                             DHO_DHCP_SERVER_IDENTIFIER,
+                                             first_packet_serverid_,
+                                             sid_offset));
+        pkt4->addOption(opt_serverid);
+    } else {
+        // Copy the contents of server identifier received in
+        // OFFER packet to put this into REQUEST.
+        OptionPtr opt_serverid_offer =
+            offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
+        if (!opt_serverid_offer) {
+            isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
+                      << "in OFFER message");
+        }
+        boost::shared_ptr<LocalizedOption>
+            opt_serverid(new LocalizedOption(Option::V4,
+                                             DHO_DHCP_SERVER_IDENTIFIER,
+                                             opt_serverid_offer->getData(),
+                                             sid_offset));
+        pkt4->addOption(opt_serverid);
+        if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
+            first_packet_serverid_ = opt_serverid_offer->getData();
+        }
+    }
+
+    /// Set client address.
+    asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
+    if (!yiaddr.getAddress().is_v4()) {
+        isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
+                  " IPv4 address");
+    }
+
+    // Get the actual offset of requested ip.
+    size_t rip_offset = DHCPV4_REQUESTED_IP_OFFSET;
+    if (options.getRequestedIpOffset() > 0) {
+        rip_offset = options.getRequestedIpOffset();
+    }
+    // Place requested IP option at specified position (rip_offset).
+    boost::shared_ptr<LocalizedOption>
+        opt_requested_ip(new LocalizedOption(Option::V4,
+                                             DHO_DHCP_REQUESTED_ADDRESS,
+                                             OptionBuffer(),
+                                             rip_offset));
+    // The IOAddress is castable to uint32_t and returns exactly what we need.
+    opt_requested_ip->setUint32(static_cast<uint32_t>(yiaddr));
+    pkt4->addOption(opt_requested_ip);
+
+    setDefaults4(socket, static_pointer_cast<Pkt4>(pkt4));
+    // Prepare on-wire data.
+    pkt4->rawPack();
+    IfaceMgr::instance().send(static_pointer_cast<Pkt4>(pkt4));
+    if (!stats_mgr4_) {
+        isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
+                  "hasn't been initialized");
+    }
+    // Update packet stats.
+    stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
+                                static_pointer_cast<Pkt4>(pkt4));
+}
 
 void
 TestControl::sendRequest6(const TestControlSocket& socket,
@@ -982,31 +1270,22 @@ TestControl::sendRequest6(const TestControlSocket& socket,
                           const Pkt6Ptr& advertise_pkt6) {
     const uint32_t transid = generateTransid();
     Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
-    // Calculate elapsed time
-    ptime solicit_time = solicit_pkt6->getTimestamp();
-    ptime advertise_time = advertise_pkt6->getTimestamp();
-    if (solicit_time.is_not_a_date_time()) {
-        isc_throw(Unexpected, "timestamp was not set for SOLICIT packet");
-    }
-    if (advertise_time.is_not_a_date_time()) {
-        isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet");
-    }
-    time_period period(solicit_time, advertise_time);
-    if (period.is_null()) {
-        pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
-    } else {
-        OptionBuffer buf();
-        const uint32_t elapsed_time = period.length().total_seconds();
-        OptionPtr opt_elapsed_time =
-            Option::factory(Option::V6, D6O_ELAPSED_TIME);
-        opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time));
-        pkt6->addOption(opt_elapsed_time);
-    }
+    // Set elapsed time.
+    const uint32_t elapsed_time =
+        getElapsedTime<Pkt6Ptr>(solicit_pkt6, advertise_pkt6);
+    OptionPtr opt_elapsed_time =
+        Option::factory(Option::V6, D6O_ELAPSED_TIME);
+    opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time / 10));
+    pkt6->addOption(opt_elapsed_time);
+    // Set client id.
     OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
     if (!opt_clientid) {
         isc_throw(Unexpected, "client id not found in received packet");
     }
     pkt6->addOption(opt_clientid);
+
+    // Use first flags indicates that we want to use the server
+    // id captured in fisrt packet.
     if (CommandOptions::instance().isUseFirst() &&
         (first_packet_serverid_.size() > 0)) {
         pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
@@ -1021,14 +1300,17 @@ TestControl::sendRequest6(const TestControlSocket& socket,
         }
         pkt6->addOption(opt_serverid);
     }
+    // Set IA_NA option.
     OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA);
     if (!opt_ia_na) {
         isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
                   "packet");
     }
     pkt6->addOption(opt_ia_na);
-    setDefaults6(socket, pkt6);
 
+    // Set default packet data.
+    setDefaults6(socket, pkt6);
+    // Prepare on-wire data.
     pkt6->pack();
     IfaceMgr::instance().send(pkt6);
     if (!stats_mgr6_) {
@@ -1038,14 +1320,140 @@ TestControl::sendRequest6(const TestControlSocket& socket,
     stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
 }
 
+void
+TestControl::sendRequest6(const TestControlSocket& socket,
+                          const std::vector<uint8_t>& template_buf,
+                          const Pkt6Ptr& solicit_pkt6,
+                          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];
+    }
+    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();
+    }
+    uint32_t elapsed_time =
+        getElapsedTime<Pkt6Ptr>(solicit_pkt6, advertise_pkt6);
+    boost::shared_ptr<LocalizedOption>
+        opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
+                                             OptionBuffer(), elp_offset));
+    opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time / 10));
+    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();
+    }
+    if (CommandOptions::instance().isUseFirst() &&
+        (first_packet_serverid_.size() > 0)) {
+        boost::shared_ptr<LocalizedOption>
+            opt_serverid(new LocalizedOption(Option::V6,
+                                             D6O_SERVERID,
+                                             first_packet_serverid_,
+                                             sid_offset));
+        pkt6->addOption(opt_serverid);
+
+    } else {
+        // Copy the contents of server identifier received in
+        // ADVERTISE packet to put this into REQUEST.
+        OptionPtr opt_serverid_advertise =
+            advertise_pkt6->getOption(D6O_SERVERID);
+        if (!opt_serverid_advertise) {
+            isc_throw(BadValue, "there is no SERVERID option "
+                      << "in ADVERTISE message");
+        }
+        boost::shared_ptr<LocalizedOption>
+            opt_serverid(new LocalizedOption(Option::V6,
+                                             D6O_SERVERID,
+                                             opt_serverid_advertise->getData(),
+                                             sid_offset));
+        pkt6->addOption(opt_serverid);
+        if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
+            first_packet_serverid_ = opt_serverid_advertise->getData();
+        }
+    }
+    // Set IA_NA
+    boost::shared_ptr<Option6IA> opt_ia_na_advertise =
+       static_pointer_cast<Option6IA>(advertise_pkt6->getOption(D6O_IA_NA));
+    if (!opt_ia_na_advertise) {
+        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();
+    }
+    boost::shared_ptr<LocalizedOption>
+        opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
+    if (!opt_ia_na->valid()) {
+        isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
+    }
+    pkt6->addOption(opt_ia_na);
+    // Set server id.
+    OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
+    if (!opt_serverid_advertise) {
+        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();
+    }
+    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];
+    }
+    OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
+    if (!opt_clientid_advertise) {
+        isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
+    }
+    rand_offset -= (opt_clientid_advertise->len() - 1);
+    // Set client id.
+    boost::shared_ptr<LocalizedOption>
+        opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
+                                         opt_clientid_advertise->getData(),
+                                         rand_offset));
+    pkt6->addOption(opt_clientid);
+    // Set default packet data.
+    setDefaults6(socket, pkt6);
+    // Prepare on wire data.
+    pkt6->rawPack();
+    // Send packet.
+    IfaceMgr::instance().send(pkt6);
+    if (!stats_mgr6_) {
+        isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
+                  "hasn't been initialized");
+    }
+    // Update packet stats.
+    stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+
+}
+
 void
 TestControl::sendSolicit6(const TestControlSocket& socket,
                           const bool preload /*= false*/) {
     last_sent_ = microsec_clock::universal_time();
-    // Generate the MAC address to be passed in the packet.
-    std::vector<uint8_t> mac_address = generateMacAddress();
     // Generate DUID to be passed to the packet
-    std::vector<uint8_t> duid = generateDuid();
+    uint8_t randomized = 0;
+    std::vector<uint8_t> duid = generateDuid(randomized);
     // Generate trasnaction id to be set for the new exchange.
     const uint32_t transid = generateTransid();
     Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
@@ -1072,6 +1480,58 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
     }
 }
 
+void
+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];
+    }
+    // Generate trasnaction id to be set for the new exchange.
+    const uint32_t transid = generateTransid();
+    // Create packet.
+    PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
+                                  transid_offset, transid));
+    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];
+    }
+    // randomized will pick number of bytes randomized so we can
+    // just use part of the generated duid and substitude a few bytes
+    /// in template.
+    uint8_t randomized = 0;
+    std::vector<uint8_t> duid = generateDuid(randomized);
+    if (rand_offset > template_buf.size()) {
+        isc_throw(OutOfRange, "randomization offset is out of bounds");
+    }
+    // Store random part of the DUID into the packet.
+    pkt6->writeAt(rand_offset - randomized + 1,
+                  duid.end() - randomized, duid.end());
+
+    // Prepare on-wire data.
+    pkt6->rawPack();
+    setDefaults6(socket, pkt6);
+    // Send solicit packet.
+    IfaceMgr::instance().send(pkt6);
+    if (!preload) {
+        if (!stats_mgr6_) {
+            isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
+                      "hasn't been initialized");
+        }
+        // Update packet stats.
+        stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
+    }
+}
+
+
 void
 TestControl::setDefaults4(const TestControlSocket& socket,
                           const Pkt4Ptr& pkt) {
@@ -1090,9 +1550,6 @@ TestControl::setDefaults4(const TestControlSocket& socket,
     pkt->setLocalAddr(IOAddress(socket.getAddress()));
     // Set relay (GIADDR) address to local address.
     pkt->setGiaddr(IOAddress(socket.getAddress()));
-    std::vector<uint8_t> mac = generateMacAddress();
-    // Set hardware address
-    pkt->setHWAddr(HTYPE_ETHER, mac.size(), mac);
     // Pretend that we have one relay (which is us).
     pkt->setHops(1);
 }
@@ -1119,9 +1576,9 @@ bool
 TestControl::testDiags(const char diag) const {
     std::string diags(CommandOptions::instance().getDiags());
     if (diags.find(diag) != std::string::npos) {
-        return true;
+        return (true);
     }
-    return false;
+    return (false);
 }
 
 void
index 9b634ce5d779aafa0e51450b7bdfec23f07ff2df..2bace3a22ee7c96eaf54112931fa800ee627ce75 100644 (file)
@@ -48,20 +48,41 @@ namespace perfdhcp {
 class TestControl : public boost::noncopyable {
 public:
 
-    // Statistics Manager for DHCPv4.
+    /// 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;
+    /// Pointer to Statistics Manager for DHCPv4;
     typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
-    // Statictics Manager for DHCPv6.
+    /// Statictics Manager for DHCPv6.
     typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
-    // Pointer to Statistics Manager for DHCPv6.
+    /// Pointer to Statistics Manager for DHCPv6.
     typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
-    // Packet exchange type.
+    /// Packet exchange type.
     typedef StatsMgr<>::ExchangeType ExchangeType;
-    // Packet template buffer.
+    /// Packet template buffer.
     typedef std::vector<uint8_t> TemplateBuffer;
-    //Packet template buffers list.
-    typedef std::list<TemplateBuffer> TemplateBufferList;
+    //Packet template buffers list.
+    typedef std::vector<TemplateBuffer> TemplateBufferCollection;
 
     /// \brief Socket wrapper class.
     ///
@@ -302,9 +323,10 @@ protected:
     /// from the MAC address, this function uses \ref generateMacAddress
     /// internally to randomize the DUID.
     ///
+    /// \param randomized number of bytes randomized.
     /// \throw isc::BadValue if \ref generateMacAddress throws.
     /// \return vector representing DUID.
-    std::vector<uint8_t> generateDuid() const;
+    std::vector<uint8_t> generateDuid(uint8_t& randomized) const;
 
     /// \brief Generate MAC address.
     ///
@@ -315,10 +337,11 @@ protected:
     /// Based on this the random value is generated and added to
     /// the MAC address prefix (default MAC address).
     ///
+    /// \param randomized number of bytes randomized.
     /// \throw isc::BadValue if MAC address prefix (default or specified
     /// from the command line) has invalid size (expected 6 octets).
     /// \return generated MAC address.
-    std::vector<uint8_t> generateMacAddress() const;
+    std::vector<uint8_t> generateMacAddress(uint8_t& randomized) const;
 
     /// \brief generate transaction id.
     ///
@@ -337,6 +360,21 @@ protected:
     /// \return number of exchanges to be started immediatelly.
     uint64_t getNextExchangesNum() const;
 
+    /// \brief Return template buffer.
+    ///
+    /// Method returns template buffer at specified index.
+    ///
+    /// \param idx index of template buffer.
+    /// \return reference to template buffer or empty buffer if index
+    /// is out of bounds.
+    TemplateBuffer getTemplateBuffer(const size_t idx) const;
+
+    /// \brief Reads packet templates from files.
+    ///
+    /// Method iterates through all specified template files, reads
+    /// their content and stores it in class internal buffers
+    ///
+    /// \throw isc::BadValue if any of the template files does not exist
     void initPacketTemplates();
 
     /// \brief Initializes Statistics Manager.
@@ -448,9 +486,9 @@ protected:
     void registerOptionFactories() const;
 
 
-    /// \brief Resets internal state of the object. 
+    /// \brief Resets internal state of the object.
     ///
-    /// Method resets internal state of the object. It has to be 
+    /// Method resets internal state of the object. It has to be
     /// called before new test is started.
     void reset();
 
@@ -472,16 +510,46 @@ protected:
     void sendDiscover4(const TestControlSocket& socket,
                        const bool preload = false);
 
+    /// \brief Send DHCPv4 DISCOVER message from template.
+    ///
+    /// Method sends DHCPv4 DISCOVER message from template. The
+    /// template data is exepcted to be in binary format. Provided
+    /// buffer is copied and parts of it are replaced with actual
+    /// data (e.g. MAC address, transaction id etc.).
+    ///
+    /// \param socket socket to be used to send the message.
+    /// \param template_buf buffer holding template packet.
+    /// \param preload preload mode, packets not included in statistics.
+    /// \throw isc::OutOfRange if randomization offset is out of bounds.
+    void sendDiscover4(const TestControlSocket& socket,
+                       const std::vector<uint8_t>& template_buf,
+                       const bool preload = false);
+
     /// \brief Send DHCPv4 REQUEST message.
     ///
     /// Method creates and sends DHCPv4 REQUEST message to the server.
     ///
     /// \param socket socket to be used to send message.
+    /// \param discover_pkt4 DISCOVER packet sent.
     /// \param offer_pkt4 OFFER packet object.
     /// \throw isc::Unexpected if unexpected error occured.
     /// \throw isc::InvalidOperation if Statistics Manager has not been
     /// initialized.
     void sendRequest4(const TestControlSocket& socket,
+                      const dhcp::Pkt4Ptr& discover_pkt4,
+                      const dhcp::Pkt4Ptr& offer_pkt4);
+
+    /// \brief Send DHCPv4 REQUEST message from template.
+    ///
+    /// Method sends DHCPv4 REQUEST message from template.
+    ///
+    /// \param socket socket to be used to send message.
+    /// \param template_buf buffer holding template packet.
+    /// \param discover_pkt4 DISCOVER packet sent.
+    /// \param offer_pkt4 OFFER packet received.
+    void sendRequest4(const TestControlSocket& socket,
+                      const std::vector<uint8_t>& template_buf,
+                      const dhcp::Pkt4Ptr& discover_pkt4,
                       const dhcp::Pkt4Ptr& offer_pkt4);
 
     /// \brief Send DHCPv6 REQUEST message.
@@ -506,6 +574,19 @@ protected:
                       const dhcp::Pkt6Ptr& solicit_pkt6,
                       const dhcp::Pkt6Ptr& advertise_pkt6);
 
+    /// \brief Send DHCPv6 REQUEST message from template.
+    ///
+    /// Method sends DHCPv6 REQUEST message from template.
+    ///
+    /// \param socket socket to be used to send message.
+    /// \param template_buf packet template buffer.
+    /// \param solicit_pkt6 SOLICIT packet object.
+    /// \param advertise_pkt6 ADVERTISE packet object.
+    void sendRequest6(const TestControlSocket& socket,
+                      const std::vector<uint8_t>& template_buf,
+                      const dhcp::Pkt6Ptr& solicit_pkt6,
+                      const dhcp::Pkt6Ptr& advertise_pkt6);
+
     /// \brief Send DHCPv6 SOLICIT message.
     ///
     /// Method creates and sends DHCPv6 SOLICIT message to the server
@@ -522,6 +603,17 @@ protected:
     void sendSolicit6(const TestControlSocket& socket,
                       const bool preload = false);
 
+    /// \brief Send DHCPv6 SOLICIT message from template.
+    ///
+    /// Method sends DHCPv6 SOLICIT message from template.
+    ///
+    /// \param socket socket to be used to send the message.
+    /// \param template_buf packet template buffer.
+    /// \param preload mode, packets not included in statistics.
+    void sendSolicit6(const TestControlSocket& socket,
+                      const std::vector<uint8_t>& template_buf,
+                      const bool preload = false);
+
     /// \brief Set default DHCPv4 packet parameters.
     ///
     /// This method sets default parameters on the DHCPv4 packet:
@@ -573,10 +665,27 @@ private:
     /// \return hex string.
     std::string byte2Hex(const uint8_t b) const;
 
-    /// \brief Generate transaction id using random function.
-    ///
-    /// \return generated transaction id value.
-    static uint32_t generateTransidRandom();
+    /// \brief Calculate elapsed time between two packets.
+    ///
+    /// \param T Pkt4Ptr or Pkt6Ptr class.
+    /// \param pkt1 first packet.
+    /// \param pkt2 second packet.
+    /// \return elapsed time in milliseconds between pkt1 and pkt2.
+    template<class T>
+    uint32_t getElapsedTime(const T& pkt1, const T& pkt2) {
+        using namespace boost::posix_time;
+        ptime pkt1_time = pkt1->getTimestamp();
+        ptime pkt2_time = pkt2->getTimestamp();
+        if (pkt1_time.is_not_a_date_time() || 
+            pkt2_time.is_not_a_date_time()) {
+            return (0);
+        }
+        time_period elapsed_period(pkt1_time, pkt2_time);
+        if (elapsed_period.is_null()) {
+            return (0);
+        }
+        return(elapsed_period.length().total_milliseconds());
+    }
 
     /// \brief Get number of received packets.
     ///
@@ -604,6 +713,18 @@ private:
     /// \param sig signal (ignored)
     static void handleInterrupt(int sig);
 
+    /// \brief Print main diagnostics data.
+    ///
+    /// Method prints main diagnostics data.
+    void printDiagnostics() 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.
+    void readPacketTemplate(const std::string& file_name);
+
     /// \brief Convert vector in hexadecimal string.
     ///
     /// \param vec vector to be converted.
@@ -611,11 +732,6 @@ private:
     std::string vector2Hex(const std::vector<uint8_t>& vec,
                            const std::string& separator = "") const;
 
-    /// \brief Print main diagnostics data.
-    ///
-    /// Method prints main diagnostics data.
-    void printDiagnostics() const;
-
     boost::posix_time::ptime send_due_;    ///< Due time to initiate next chunk
                                            ///< of exchanges.
     boost::posix_time::ptime last_sent_;   ///< Indicates when the last exchange
@@ -629,10 +745,10 @@ private:
     TransidGeneratorPtr transid_gen_; ///< Transaction id generator.
 
     /// Buffer holiding server id received in first packet
-    dhcp::OptionBuffer first_packet_serverid_; 
+    dhcp::OptionBuffer first_packet_serverid_;
 
     /// Packet template buffers.
-    TemplateBufferList template_buffers_;
+    TemplateBufferCollection template_buffers_;
 
     static bool interrupted_;  ///< Is program interrupted.
 };
index 2284042d58e5cc88fd2e08eab151da397f624ea7..d72cd0f755f967b346862b292b577db43ccfbd02 100644 (file)
@@ -60,7 +60,7 @@ public:
         ///
         /// \return generated transaction id.
         virtual uint32_t generate() {
-            return ++transid_;
+            return (++transid_);
         }
     private:
         uint32_t transid_; ///< Last generated transaction id.
@@ -76,6 +76,8 @@ public:
     using TestControl::generateDuid;
     using TestControl::generateMacAddress;
     using TestControl::getNextExchangesNum;
+    using TestControl::getTemplateBuffer;
+    using TestControl::initPacketTemplates;
     using TestControl::initializeStatsMgr;
     using TestControl::openSocket;
     using TestControl::receivePacket4;
@@ -109,7 +111,7 @@ public:
 
     static uint32_t generateTransidIncremental() {
         static uint32_t transid(1);
-        return ++transid;
+        return (++transid);
     }
 
     /// \brief Get local loopback interface name.
@@ -134,11 +136,11 @@ public:
                  ++addr_it) {
                 if (asiolink::IOAddress("127.0.0.1").getAddress() ==
                     addr_it->getAddress()) {
-                    return iface->getName();
+                    return (iface->getName());
                 }
             }
         }
-        return("");
+        return ("");
     }
 
     /// \brief Match requested options in the buffer with given list.
@@ -163,7 +165,7 @@ public:
                 }
             }
         }
-        return matched_num;
+        return (matched_num);
     }
 
     /// \brief Calculate the maximum vectors' mismatch position.
@@ -193,7 +195,7 @@ public:
                 n %= 256;
             }
         }
-        return unequal_pos;
+        return (unequal_pos);
     }
 
     /// brief Test generation of mulitple DUIDs
@@ -235,7 +237,8 @@ public:
                 new_duid = old_duid;
             } else {
                 std::swap(old_duid, new_duid);
-                new_duid = tc.generateDuid();
+                uint8_t randomized = 0;
+                new_duid = tc.generateDuid(randomized);
             }
             // The DUID-LLT is expected to start with DUID_LLT value
             // of 1 and hardware ethernet type equal to 1 (HWETHER_TYPE).
@@ -309,10 +312,19 @@ public:
     /// \param iterations_performed actual number of iterations.
     void testPkt4Exchange(int iterations_num,
                           int receive_num,
+                          bool use_templates,
                           int& iterations_performed) const {
         int sock_handle = 0;
         NakedTestControl tc;
         tc.initializeStatsMgr();
+
+        // Use templates files to crate packets.
+        if (use_templates) {
+            tc.initPacketTemplates();
+            ASSERT_GT(tc.getTemplateBuffer(0).size(), 0);
+            ASSERT_GT(tc.getTemplateBuffer(1).size(), 0);
+        }
+
         // Incremental transaction id generator will generate
         // predictable values of transaction id for each iteration.
         // This is important because we need to simulate reponses
@@ -326,7 +338,11 @@ public:
         TestControl::TestControlSocket sock(sock_handle);
         uint32_t transid = 0;
         for (int i = 0; i < iterations_num; ++i) {
-            ASSERT_NO_THROW(tc.sendDiscover4(sock));
+            if (use_templates) {
+                ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0)));
+            } else {
+                ASSERT_NO_THROW(tc.sendDiscover4(sock));
+            }
             ++transid;
             // Do not simulate responses for packets later
             // that specified as receive_num. This simulates
@@ -359,10 +375,19 @@ public:
     /// \param iterations_performed actual number of iterations.
     void testPkt6Exchange(int iterations_num,
                           int receive_num,
+                          bool use_templates,
                           int& iterations_performed) const {
         int sock_handle = 0;
         NakedTestControl tc;
         tc.initializeStatsMgr();
+
+        // Use templates files to crate packets.
+        if (use_templates) {
+            tc.initPacketTemplates();
+            ASSERT_GT(tc.getTemplateBuffer(0).size(), 0);
+            ASSERT_GT(tc.getTemplateBuffer(1).size(), 0);
+        }
+
         // Incremental transaction id generator will generate
         // predictable values of transaction id for each iteration.
         // This is important because we need to simulate reponses
@@ -379,7 +404,11 @@ public:
             // Do not simulate responses for packets later
             // that specified as receive_num. This simulates
             // packet drops.
-            ASSERT_NO_THROW(tc.sendSolicit6(sock));
+            if (use_templates) {
+                ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0)));
+            } else {
+                ASSERT_NO_THROW(tc.sendSolicit6(sock));
+            }
             ++transid;
             if (i < receive_num) {
                 boost::shared_ptr<Pkt6>
@@ -423,7 +452,8 @@ public:
         // Do many iterations to generate and test MAC address values.
         for (int i = 0; i < clients_num * 10; ++i) {
             // Generate new MAC address.
-            MacAddress new_mac(tc.generateMacAddress());
+            uint8_t randomized = 0;
+            MacAddress new_mac(tc.generateMacAddress(randomized));
             // Get the mismatch position (counting from the end) of
             // mismatched octet between previously generated MAC address
             // and current.
@@ -471,7 +501,7 @@ private:
         offer->addOption(opt_msg_type);
         offer->addOption(opt_serverid);
         offer->updateTimestamp();
-        return(offer);
+        return (offer);
     }
 
     boost::shared_ptr<Pkt6>
@@ -479,14 +509,15 @@ private:
         OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
         OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
         NakedTestControl tc;
-        std::vector<uint8_t> duid(tc.generateDuid());
+        uint8_t randomized = 0;
+        std::vector<uint8_t> duid(tc.generateDuid(randomized));
         OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
         boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
         advertise->addOption(opt_ia_na);
         advertise->addOption(opt_serverid);
         advertise->addOption(opt_clientid);
         advertise->updateTimestamp();
-        return(advertise);
+        return (advertise);
     }
 
 };
@@ -634,8 +665,8 @@ TEST_F(TestControlTest, Options6) {
     OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO));
     // Prepare the reference buffer with requested options.
     const uint8_t requested_options[] = {
-        D6O_NAME_SERVERS,
-        D6O_DOMAIN_SEARCH
+        0, D6O_NAME_SERVERS,
+        0, D6O_DOMAIN_SEARCH
     };
     OptionBuffer
         requested_options_ref(requested_options,
@@ -751,22 +782,28 @@ TEST_F(TestControlTest, Packet4Exchange) {
     // The actual number of iterations will be stored in the
     // following variable.
     int iterations_performed = 0;
-    testPkt4Exchange(iterations_num, iterations_num, iterations_performed);
+    bool use_templates = false;
+    testPkt4Exchange(iterations_num, iterations_num, use_templates, iterations_performed);
     // The command line restricts the number of iterations to 10
     // with -n 10 parameter.
     EXPECT_EQ(10, iterations_performed);
 
     // With the following command line we restrict the maximum
     // number of dropped packets to 20% of all.
+    // Use templates for this test.
     processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -r 100 -R 20 -n 20 -D 10% -L 10547 127.0.0.1");
+                   + " -r 100 -R 20 -n 20 -D 10% -L 10547"
+                   + " -T ../templates/discover-example.hex"
+                   + " -T ../templates/request4-example.hex"
+                   + " 127.0.0.1");
     // The number iterations is restricted by the percentage of
     // dropped packets (-D 10%). We also have to bump up the number
     // of iterations because the percentage limitation checks starts
     // at packet #10. We expect that at packet #12 the 10% threshold
     // will be reached.
     const int received_num = 10;
-    testPkt4Exchange(iterations_num, received_num, iterations_performed);
+    use_templates = true;
+    testPkt4Exchange(iterations_num, received_num, use_templates, iterations_performed);
     EXPECT_EQ(12, iterations_performed);
 }
 
@@ -788,22 +825,38 @@ TEST_F(TestControlTest, Packet6Exchange) {
     int iterations_performed = 0;
     // Set number of received packets equal to number of iterations.
     // This simulates no packet drops.
-    testPkt6Exchange(iterations_num, iterations_num, iterations_performed);
+    bool use_templates = false;
+    testPkt6Exchange(iterations_num, iterations_num, use_templates, iterations_performed);
     // Actual number of iterations should be 10.
     EXPECT_EQ(10, iterations_performed);
 
     // The maximum number of dropped packets is 3 (because of -D 3).
+    use_templates = true;
     processCmdLine("perfdhcp -l " + loopback_iface
-                   + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547 ::1");
+                   + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547"
+                   + " -T ../templates/solicit-example.hex"
+                   + " -T ../templates/request6-example.hex ::1");
     // For the first 3 packets we are simulating responses from server.
     // For other packets we don't so packet as 4,5,6 will be dropped and
     // then test should be interrupted and actual number of iterations will
     // be 6.
     const int received_num = 3;
-    testPkt6Exchange(iterations_num, received_num, iterations_performed);
+    testPkt6Exchange(iterations_num, received_num, use_templates, iterations_performed);
     EXPECT_EQ(6, iterations_performed);
 }
 
+TEST_F(TestControlTest, PacketTemplates) {
+    CommandOptions& options = CommandOptions::instance();
+    NakedTestControl tc;
+
+    ASSERT_NO_THROW(
+        processCmdLine("perfdhcp -l 127.0.0.1"
+                       " -T ../templates/discover-example.hex"
+                       " -T ../templates/request4-example.hex all")
+    );
+    ASSERT_NO_THROW(tc.initPacketTemplates());
+}
+
 TEST_F(TestControlTest, RateControl) {
     // We don't specify the exchange rate here so the aggressivity
     // value will determine how many packets are to be send each