BIND 10 master, updated. 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f [master] Merge branch 'trac1955' with resolving conflicts.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Jun 12 11:24:38 UTC 2012


The branch, master has been updated
       via  6f914bb2c388eb4dd3e5c55297f8988ab9529b3f (commit)
       via  d17813bd0f896fcbd879a8b08d40c7e396dc2d42 (commit)
       via  6ae6562d99f3917bfaa379a6d2466417e57c4d6e (commit)
       via  0235da81624be30ea2aeadaf1c93fde1ba1bd057 (commit)
       via  8cecc1ec807f782e4fde4f5a21463c304bd07223 (commit)
       via  ce911b68e171ddced13c91ced5e7a1d417ad62ca (commit)
       via  fa56b67ff11e54e7eed6331c4470d37bcbc2c5bb (commit)
       via  6fc0bdf0ad2a49f5e98c1d78aadf7334c954a5a3 (commit)
       via  5496d551ef264130bb017a99906410fa27045cd9 (commit)
       via  652738d33dd4a62c676b3bcb26996d646253191c (commit)
       via  56951dcff8366dd0bccf09ae233ea90f8c90dbca (commit)
       via  be57f66a715103218b56b77536807d5c5714cbab (commit)
       via  53dfacddb52280654910206f96325c531a480ce0 (commit)
       via  d16a4ec35b968f8d7af36872a3df62bf7f8fc3d1 (commit)
       via  04b0e46c0bfee7922b271d61f8070cd35d4505c9 (commit)
       via  1a0a81eb61c625be4681c5b600e07ad9adfc68d9 (commit)
       via  81784141da4a239663f094e56a6a77643c8f81cb (commit)
       via  5c430ad5edbeb6e799ad5a80ce7f01e6d15abfc3 (commit)
       via  bff3d4786c50018143abe2316c9e837f24c52e81 (commit)
       via  25af754fb60cdb8e538eeb2111694bd3cf2bf89e (commit)
       via  968d486cb7c77cec9f47a3b155938c5c2cac6747 (commit)
       via  ff1617fd13a786d41439b7d653e798352e3789b7 (commit)
       via  9cd5b8d800ba00b9e0d3d07609853e1221550a2f (commit)
       via  0a3638e4bae9b7ace20bf59f5aa1ee1e1fe50d1a (commit)
       via  6643603e8bc40fed0bab6d6a4ca6b6d585be6001 (commit)
       via  689b286631a39e2c3273982c8ab5d527f7d6211b (commit)
       via  a6a3d1b172cfd5ba9db4ddb3b8ffcbf673d8e31f (commit)
       via  908b273b78455fc4893d3e9d3dd9c4ca42e934c6 (commit)
      from  32b494b4f1c01d1694fab23b4b172185bdb21769 (commit)

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

- Log -----------------------------------------------------------------
commit 6f914bb2c388eb4dd3e5c55297f8988ab9529b3f
Merge: 32b494b d17813b
Author: Marcin Siodelski <marcin at isc.org>
Date:   Tue Jun 12 13:16:16 2012 +0200

    [master] Merge branch 'trac1955' with resolving conflicts.

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

Summary of changes:
 doc/Doxyfile                                       |    2 +-
 src/bin/dhcp4/Makefile.am                          |    4 +
 src/bin/dhcp4/tests/Makefile.am                    |    4 +
 src/bin/dhcp6/Makefile.am                          |    4 +
 src/bin/dhcp6/tests/Makefile.am                    |    5 +
 src/lib/dhcp/Makefile.am                           |    4 +
 src/lib/dhcp/iface_mgr.cc                          |    8 +
 src/lib/dhcp/option.cc                             |    8 +
 src/lib/dhcp/option.h                              |    9 +
 src/lib/dhcp/pkt4.cc                               |    5 +
 src/lib/dhcp/pkt4.h                                |   47 +++
 src/lib/dhcp/pkt6.cc                               |    6 +
 src/lib/dhcp/pkt6.h                                |   47 +++
 src/lib/dhcp/tests/Makefile.am                     |    4 +-
 src/lib/dhcp/tests/option_unittest.cc              |   29 +-
 src/lib/dhcp/tests/pkt4_unittest.cc                |   28 ++
 src/lib/dhcp/tests/pkt6_unittest.cc                |   27 ++
 tests/tools/perfdhcp/Makefile.am                   |   11 +
 tests/tools/perfdhcp/command_options.cc            |    2 +-
 tests/tools/perfdhcp/command_options.h             |    2 +-
 tests/tools/perfdhcp/localized_option.h            |  123 +++++++
 tests/tools/perfdhcp/perf_pkt4.cc                  |   62 ++++
 tests/tools/perfdhcp/perf_pkt4.h                   |  113 ++++++
 tests/tools/perfdhcp/perf_pkt6.cc                  |   64 ++++
 tests/tools/perfdhcp/perf_pkt6.h                   |  113 ++++++
 tests/tools/perfdhcp/pkt_transform.cc              |  222 +++++++++++
 tests/tools/perfdhcp/pkt_transform.h               |  139 +++++++
 tests/tools/perfdhcp/tests/Makefile.am             |   12 +
 .../perfdhcp/tests/command_options_unittest.cc     |    2 +-
 .../perfdhcp/tests/localized_option_unittest.cc    |   48 +++
 tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc   |  384 ++++++++++++++++++++
 tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc   |  327 +++++++++++++++++
 32 files changed, 1858 insertions(+), 7 deletions(-)
 create mode 100644 tests/tools/perfdhcp/localized_option.h
 create mode 100644 tests/tools/perfdhcp/perf_pkt4.cc
 create mode 100644 tests/tools/perfdhcp/perf_pkt4.h
 create mode 100644 tests/tools/perfdhcp/perf_pkt6.cc
 create mode 100644 tests/tools/perfdhcp/perf_pkt6.h
 create mode 100644 tests/tools/perfdhcp/pkt_transform.cc
 create mode 100644 tests/tools/perfdhcp/pkt_transform.h
 create mode 100644 tests/tools/perfdhcp/tests/localized_option_unittest.cc
 create mode 100644 tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc
 create mode 100644 tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc

-----------------------------------------------------------------------
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 8730ae4..6d91bf2 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -579,7 +579,7 @@ INPUT                  = ../src/lib/exceptions ../src/lib/cc \
     ../src/lib/testutils ../src/lib/cache ../src/lib/server_common/ \
     ../src/bin/sockcreator/ ../src/lib/util/ ../src/lib/util/io/ \
     ../src/lib/resolve ../src/lib/acl ../src/bin/dhcp6 ../src/lib/dhcp \
-    ../src/bin/dhcp4 devel
+    ../src/bin/dhcp4 ../tests/tools/perfdhcp devel
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/src/bin/dhcp4/Makefile.am b/src/bin/dhcp4/Makefile.am
index c828fdc..8c804d6 100644
--- a/src/bin/dhcp4/Makefile.am
+++ b/src/bin/dhcp4/Makefile.am
@@ -32,6 +32,10 @@ pkglibexec_PROGRAMS = b10-dhcp4
 
 b10_dhcp4_SOURCES = main.cc dhcp4_srv.cc dhcp4_srv.h
 
+if USE_CLANGPP
+b10_dhcp4_CXXFLAGS = -Wno-unused-parameter
+endif
+
 b10_dhcp4_LDADD = $(top_builddir)/src/lib/dhcp/libdhcp++.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp4_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am
index a327e47..187e01b 100644
--- a/src/bin/dhcp4/tests/Makefile.am
+++ b/src/bin/dhcp4/tests/Makefile.am
@@ -47,6 +47,10 @@ dhcp4_unittests_SOURCES = ../dhcp4_srv.h ../dhcp4_srv.cc
 dhcp4_unittests_SOURCES += dhcp4_unittests.cc
 dhcp4_unittests_SOURCES += dhcp4_srv_unittest.cc
 
+if USE_CLANGPP
+dhcp4_unittests_CXXFLAGS = -Wno-unused-parameter
+endif
+
 dhcp4_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp4_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp4_unittests_LDADD = $(GTEST_LDADD)
diff --git a/src/bin/dhcp6/Makefile.am b/src/bin/dhcp6/Makefile.am
index 44b4e9b..7da8207 100644
--- a/src/bin/dhcp6/Makefile.am
+++ b/src/bin/dhcp6/Makefile.am
@@ -34,6 +34,10 @@ pkglibexec_PROGRAMS = b10-dhcp6
 
 b10_dhcp6_SOURCES = main.cc dhcp6_srv.cc dhcp6_srv.h
 
+if USE_CLANGPP
+b10_dhcp6_CXXFLAGS = -Wno-unused-parameter
+endif
+
 b10_dhcp6_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/bin/dhcp6/tests/Makefile.am b/src/bin/dhcp6/tests/Makefile.am
index a1a11a0..ee98049 100644
--- a/src/bin/dhcp6/tests/Makefile.am
+++ b/src/bin/dhcp6/tests/Makefile.am
@@ -43,6 +43,10 @@ dhcp6_unittests_SOURCES = ../dhcp6_srv.h ../dhcp6_srv.cc
 dhcp6_unittests_SOURCES += dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 
+if USE_CLANGPP
+dhcp6_unittests_CXXFLAGS = -Wno-unused-parameter
+endif
+
 dhcp6_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 dhcp6_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
 dhcp6_unittests_LDADD = $(GTEST_LDADD)
@@ -50,6 +54,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 9e6fb0c..8ddd490 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -31,3 +31,7 @@ libdhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libdhcp___la_LIBADD   = $(top_builddir)/src/lib/asiolink/libasiolink.la
 libdhcp___la_LIBADD  += $(top_builddir)/src/lib/util/libutil.la
 libdhcp___la_LDFLAGS  = -no-undefined -version-info 1:0:0
+
+if USE_CLANGPP
+libdhcp___la_CXXFLAGS += -Wno-unused-parameter
+endif
diff --git a/src/lib/dhcp/iface_mgr.cc b/src/lib/dhcp/iface_mgr.cc
index 824b9e3..253e061 100644
--- a/src/lib/dhcp/iface_mgr.cc
+++ b/src/lib/dhcp/iface_mgr.cc
@@ -606,6 +606,8 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
     pktinfo->ipi6_ifindex = pkt->getIndex();
     m.msg_controllen = cmsg->cmsg_len;
 
+    pkt->updateTimestamp();
+
     result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
         isc_throw(Unexpected, "Pkt6 send failed: sendmsg() returned " << result);
@@ -665,6 +667,8 @@ IfaceMgr::send(const Pkt4Ptr& pkt)
          << " over socket " << getSocket(*pkt) << " on interface "
          << getIface(pkt->getIface())->getFullName() << endl;
 
+    pkt->updateTimestamp();
+
     int result = sendmsg(getSocket(*pkt), &m, 0);
     if (result < 0) {
         isc_throw(Unexpected, "Pkt4 send failed.");
@@ -755,6 +759,8 @@ IfaceMgr::receive4() {
     // We have all data let's create Pkt4 object.
     Pkt4Ptr pkt = Pkt4Ptr(new Pkt4(buf, result));
 
+    pkt->updateTimestamp();
+
     unsigned int ifindex = iface->getIndex();
 
     IOAddress from(htonl(from_addr.sin_addr.s_addr));
@@ -899,6 +905,8 @@ Pkt6Ptr IfaceMgr::receive6() {
         return (Pkt6Ptr()); // NULL
     }
 
+    pkt->updateTimestamp();
+
     pkt->setLocalAddr(IOAddress::from_bytes(AF_INET6,
                       reinterpret_cast<const uint8_t*>(&to_addr)));
     pkt->setRemoteAddr(IOAddress::from_bytes(AF_INET6,
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc
index 03b4a3d..0c71606 100644
--- a/src/lib/dhcp/option.cc
+++ b/src/lib/dhcp/option.cc
@@ -270,6 +270,14 @@ void Option::setUint32(uint32_t value) {
   writeUint32(value, &data_[0]);
 }
 
+void Option::setData(const OptionBufferConstIter first,
+                     const OptionBufferConstIter last) {
+    // We will copy entire option buffer, so we have to resize data_.
+    data_.resize(std::distance(first, last));
+    std::copy(first, last, data_.begin());
+}
+
+
 Option::~Option() {
 
 }
diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h
index c7f5d10..0662967 100644
--- a/src/lib/dhcp/option.h
+++ b/src/lib/dhcp/option.h
@@ -244,6 +244,15 @@ public:
     /// @param value value to be set
     void setUint32(uint32_t value);
 
+    /// @brief Sets content of this option from buffer.
+    ///
+    /// Option will be resized to length of buffer.
+    ///
+    /// @param first iterator pointing begining of buffer to copy.
+    /// @param last iterator pointing to end of buffer to copy.
+    void setData(const OptionBufferConstIter first,
+                 const OptionBufferConstIter last);
+
     /// just to force that every option has virtual dtor
     virtual ~Option();
 
diff --git a/src/lib/dhcp/pkt4.cc b/src/lib/dhcp/pkt4.cc
index e456631..2c3f1eb 100644
--- a/src/lib/dhcp/pkt4.cc
+++ b/src/lib/dhcp/pkt4.cc
@@ -305,6 +305,11 @@ Pkt4::getOption(uint8_t type) {
     return boost::shared_ptr<isc::dhcp::Option>(); // NULL
 }
 
+void
+Pkt4::updateTimestamp() {
+    timestamp_ = boost::posix_time::microsec_clock::universal_time();
+}
+
 } // end of namespace isc::dhcp
 
 } // end of namespace isc
diff --git a/src/lib/dhcp/pkt4.h b/src/lib/dhcp/pkt4.h
index a3f683f..b72c03e 100644
--- a/src/lib/dhcp/pkt4.h
+++ b/src/lib/dhcp/pkt4.h
@@ -16,8 +16,10 @@
 #define PKT4_H
 
 #include <iostream>
+#include <time.h>
 #include <vector>
 #include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include "asiolink/io_address.h"
 #include "util/buffer.h"
 #include "dhcp/option.h"
@@ -202,6 +204,11 @@ public:
     void
     setGiaddr(const isc::asiolink::IOAddress& giaddr) { giaddr_ = giaddr; };
 
+    /// @brief Sets transaction-id value
+    ///
+    /// @param transid transaction-id to be set.
+    void setTransid(uint32_t transid) { transid_ = transid; }
+
     /// @brief Returns value of transaction-id field.
     ///
     /// @return transaction-id
@@ -321,6 +328,14 @@ public:
     /// @return interface name
     std::string getIface() const { return iface_; };
 
+    /// @brief Returns packet timestamp.
+    ///
+    /// Returns packet timestamp value updated when
+    /// packet is received or send.
+    ///
+    /// @return packet timestamp.
+    const boost::posix_time::ptime& getTimestamp() const { return timestamp_; }
+
     /// @brief Sets interface name.
     ///
     /// Sets interface name over which packet was received or is
@@ -387,6 +402,14 @@ public:
     /// @return remote port
     uint16_t getRemotePort() { return (remote_port_); }
 
+    /// @brief Update packet timestamp.
+    ///
+    /// Updates packet timestamp. This method is invoked
+    /// by interface manager just before sending or
+    /// just after receiving it.
+    /// @throw isc::Unexpected if timestamp update failed
+    void updateTimestamp();
+
 protected:
 
     /// converts DHCP message type to BOOTP op type
@@ -470,12 +493,26 @@ protected:
     // end of real DHCPv4 fields
 
     /// output buffer (used during message transmission)
+    ///
+    /// @warning This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     isc::util::OutputBuffer bufferOut_;
 
     /// that's the data of input buffer used in RX packet. Note that
     /// InputBuffer does not store the data itself, but just expects that
     /// data will be valid for the whole life of InputBuffer. Therefore we
     /// need to keep the data around.
+    ///
+    /// @warning This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     std::vector<uint8_t> data_;
 
     /// message type (e.g. 1=DHCPDISCOVER)
@@ -484,7 +521,17 @@ protected:
     uint8_t msg_type_;
 
     /// collection of options present in this message
+    ///
+    /// @warnig This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt4. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     isc::dhcp::Option::OptionCollection options_;
+
+    /// packet timestamp
+    boost::posix_time::ptime timestamp_;
 }; // Pkt4 class
 
 typedef boost::shared_ptr<Pkt4> Pkt4Ptr;
diff --git a/src/lib/dhcp/pkt6.cc b/src/lib/dhcp/pkt6.cc
index aea3cde..e869c7b 100644
--- a/src/lib/dhcp/pkt6.cc
+++ b/src/lib/dhcp/pkt6.cc
@@ -202,5 +202,11 @@ void Pkt6::repack() {
     bufferOut_.writeData(&data_[0], data_.size());
 }
 
+void
+Pkt6::updateTimestamp() {
+    timestamp_ = boost::posix_time::microsec_clock::universal_time();
+}
+
+
 } // end of isc::dhcp namespace
 } // end of isc namespace
diff --git a/src/lib/dhcp/pkt6.h b/src/lib/dhcp/pkt6.h
index 97ac996..2612f27 100644
--- a/src/lib/dhcp/pkt6.h
+++ b/src/lib/dhcp/pkt6.h
@@ -16,8 +16,10 @@
 #define PKT6_H
 
 #include <iostream>
+#include <time.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_array.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include "asiolink/io_address.h"
 #include "dhcp/option.h"
 
@@ -129,6 +131,11 @@ public:
     /// @param type message type to be set
     void setType(uint8_t type) { msg_type_=type; };
 
+    /// @brief Sets transaction-id value
+    ///
+    /// @param transid transaction-id to be set.
+    void setTransid(uint32_t transid) { transid_ = transid; }
+
     /// Returns value of transaction-id field
     ///
     /// @return transaction-id
@@ -220,6 +227,14 @@ public:
     /// @return interface name
     std::string getIface() const { return iface_; };
 
+    /// @brief Returns packet timestamp.
+    ///
+    /// Returns packet timestamp value updated when
+    /// packet is received or send.
+    ///
+    /// @return packet timestamp.
+    const boost::posix_time::ptime& getTimestamp() const { return timestamp_; }
+
     /// @brief Sets interface name.
     ///
     /// Sets interface name over which packet was received or is
@@ -231,8 +246,23 @@ public:
     /// TODO Need to implement getOptions() as well
 
     /// collection of options present in this message
+    ///
+    /// @warning This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt6. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     isc::dhcp::Option::OptionCollection options_;
 
+    /// @brief Update packet timestamp.
+    ///
+    /// Updates packet timestamp. This method is invoked
+    /// by interface manager just before sending or
+    /// just after receiving it.
+    /// @throw isc::Unexpected if timestamp update failed
+    void updateTimestamp();
+
 protected:
     /// Builds on wire packet for TCP transmission.
     ///
@@ -278,6 +308,13 @@ protected:
     uint32_t transid_;
 
     /// unparsed data (in received packets)
+    ///
+    /// @warning This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt6. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     OptionBuffer data_;
 
     /// name of the network interface the packet was received/to be sent over
@@ -304,7 +341,17 @@ protected:
     uint16_t remote_port_;
 
     /// output buffer (used during message transmission)
+    ///
+    /// @warning This protected member is accessed by derived
+    /// classes directly. One of such derived classes is
+    /// @ref perfdhcp::PerfPkt6. The impact on derived clasess'
+    /// behavior must be taken into consideration before making
+    /// changes to this member such as access scope restriction or
+    /// data format change etc.
     isc::util::OutputBuffer bufferOut_;
+
+    /// packet timestamp
+    boost::posix_time::ptime timestamp_;
 }; // Pkt6 class
 
 typedef boost::shared_ptr<Pkt6> Pkt6Ptr;
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index cc20bd5..a0cfe98 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -38,8 +38,8 @@ libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
 
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
-# log4cplus's streams.h.
-libdhcp___unittests_CXXFLAGS += -Wno-unused-variable
+# log4cplus's streams.h and unused parameters from boost::posix_time.
+libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
 endif
 libdhcp___unittests_LDADD  = $(GTEST_LDADD)
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/liblog.la
diff --git a/src/lib/dhcp/tests/option_unittest.cc b/src/lib/dhcp/tests/option_unittest.cc
index 5daf75d..9b046f0 100644
--- a/src/lib/dhcp/tests/option_unittest.cc
+++ b/src/lib/dhcp/tests/option_unittest.cc
@@ -485,7 +485,7 @@ TEST_F(OptionTest, setUintX) {
     uint8_t exp2[] = {125, 2, 12345/256, 12345%256};
     EXPECT_TRUE(0 == memcmp(exp2, outBuf_.getData(), 4));
 
-    // verity getUint32
+    // verify getUint32
     outBuf_.clear();
     opt4->setUint32(0x12345678);
     opt4->pack4(outBuf_);
@@ -495,4 +495,31 @@ TEST_F(OptionTest, setUintX) {
     uint8_t exp4[] = {125, 4, 0x12, 0x34, 0x56, 0x78};
     EXPECT_TRUE(0 == memcmp(exp4, outBuf_.getData(), 6));
 }
+
+TEST_F(OptionTest, setData) {
+    // verify data override with new buffer larger than
+    // initial option buffer size
+    OptionPtr opt1(new Option(Option::V4, 125,
+                              buf_.begin(), buf_.begin() + 10));
+    buf_.resize(20, 1);
+    opt1->setData(buf_.begin(), buf_.end());
+    opt1->pack4(outBuf_);
+    ASSERT_EQ(outBuf_.getLength() - opt1->getHeaderLen(), buf_.size());
+    const uint8_t* test_data = static_cast<const uint8_t*>(outBuf_.getData());
+    EXPECT_TRUE(0 == memcmp(&buf_[0], test_data + opt1->getHeaderLen(),
+                            buf_.size()));
+
+    // verify data override with new buffer shorter than
+    // initial option buffer size
+    OptionPtr opt2(new Option(Option::V4, 125,
+                              buf_.begin(), buf_.begin() + 10));
+    outBuf_.clear();
+    buf_.resize(5, 1);
+    opt2->setData(buf_.begin(), buf_.end());
+    opt2->pack4(outBuf_);
+    ASSERT_EQ(outBuf_.getLength() - opt1->getHeaderLen(), buf_.size());
+    test_data = static_cast<const uint8_t*>(outBuf_.getData());
+    EXPECT_TRUE(0 == memcmp(&buf_[0], test_data + opt1->getHeaderLen(),
+                            buf_.size()));
+}
 }
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index bed8c2f..e443b65 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -598,4 +598,32 @@ TEST(Pkt4Test, metaFields) {
     delete pkt;
 }
 
+TEST(Pkt4Test, Timestamp) {
+    scoped_ptr<Pkt4> pkt(new Pkt4(DHCPOFFER, 1234));
+
+    // Just after construction timestamp is invalid
+    ASSERT_TRUE(pkt->getTimestamp().is_not_a_date_time());
+
+    // Update packet time.
+    pkt->updateTimestamp();
+
+    // Get updated packet time.
+    boost::posix_time::ptime ts_packet = pkt->getTimestamp();
+
+    // After timestamp is updated it should be date-time.
+    ASSERT_FALSE(ts_packet.is_not_a_date_time());
+
+    // Check current time.
+    boost::posix_time::ptime ts_now =
+        boost::posix_time::microsec_clock::universal_time();
+
+    // Calculate period between packet time and now.
+    boost::posix_time::time_period ts_period(ts_packet, ts_now);
+
+    // Duration should be positive or zero.
+    EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
+}
+
+
+
 } // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index e07ea9f..d6ca9b1 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -16,6 +16,7 @@
 #include <iostream>
 #include <sstream>
 #include <arpa/inet.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
 #include <gtest/gtest.h>
 
 #include <asiolink/io_address.h>
@@ -204,4 +205,30 @@ TEST_F(Pkt6Test, addGetDelOptions) {
     delete parent;
 }
 
+TEST_F(Pkt6Test, Timestamp) {
+    boost::scoped_ptr<Pkt6> pkt(new Pkt6(DHCPV6_SOLICIT, 0x020304));
+
+    // Just after construction timestamp is invalid
+    ASSERT_TRUE(pkt->getTimestamp().is_not_a_date_time());
+
+    // Update packet time.
+    pkt->updateTimestamp();
+
+    // Get updated packet time.
+    boost::posix_time::ptime ts_packet = pkt->getTimestamp();
+
+    // After timestamp is updated it should be date-time.
+    ASSERT_FALSE(ts_packet.is_not_a_date_time());
+
+    // Check current time.
+    boost::posix_time::ptime ts_now =
+        boost::posix_time::microsec_clock::universal_time();
+
+    // Calculate period between packet time and now.
+    boost::posix_time::time_period ts_period(ts_packet, ts_now);
+
+    // Duration should be positive or zero.
+    EXPECT_TRUE(ts_period.length().total_microseconds() >= 0);
+}
+
 }
diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am
index 12383ac..f338168 100644
--- a/tests/tools/perfdhcp/Makefile.am
+++ b/tests/tools/perfdhcp/Makefile.am
@@ -14,8 +14,19 @@ endif
 
 lib_LTLIBRARIES = libperfdhcp++.la
 libperfdhcp___la_SOURCES = command_options.cc command_options.h
+libperfdhcp___la_SOURCES += localized_option.h
+libperfdhcp___la_SOURCES += perf_pkt6.cc perf_pkt6.h
+libperfdhcp___la_SOURCES += perf_pkt4.cc perf_pkt4.h
+libperfdhcp___la_SOURCES += pkt_transform.cc pkt_transform.h
+
 libperfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
+if USE_CLANGPP
+libperfdhcp___la_CXXFLAGS += -Wno-unused-parameter
+endif
+
 libperfdhcp___la_LIBADD = $(top_builddir)/src/lib/exceptions/libexceptions.la
+libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
+libperfdhcp___la_LIBADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
 
 pkglibexec_PROGRAMS  = perfdhcp
 perfdhcp_SOURCES  = perfdhcp.c
diff --git a/tests/tools/perfdhcp/command_options.cc b/tests/tools/perfdhcp/command_options.cc
index 7b62076..5b4f424 100644
--- a/tests/tools/perfdhcp/command_options.cc
+++ b/tests/tools/perfdhcp/command_options.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
diff --git a/tests/tools/perfdhcp/command_options.h b/tests/tools/perfdhcp/command_options.h
index 033d29a..9196857 100644
--- a/tests/tools/perfdhcp/command_options.h
+++ b/tests/tools/perfdhcp/command_options.h
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
diff --git a/tests/tools/perfdhcp/localized_option.h b/tests/tools/perfdhcp/localized_option.h
new file mode 100644
index 0000000..5374684
--- /dev/null
+++ b/tests/tools/perfdhcp/localized_option.h
@@ -0,0 +1,123 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __LOCALIZED_OPTION_H
+#define __LOCALIZED_OPTION_H
+
+#include <dhcp/pkt6.h>
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief DHCP option at specific offset
+///
+/// This class represents DHCP option with data placed at specified
+/// offset in DHCP message.
+/// Objects of this type are intended to be used when DHCP packets
+/// are created from templates (e.g. read from template file).
+/// Such packets have number of options with contents that have to be
+/// replaced before sending: e.g. DUID can be randomized.
+/// If option of this type is added to \ref PerfPkt6 options collection,
+/// \ref perfdhcp::PerfPkt6 will call \ref getOffset on this object
+/// to retrieve user-defined option position and replace contents of
+/// the output buffer at this offset before packet is sent to the server.
+/// (\see perfdhcp::PerfPkt6::rawPack).
+/// In order to read on-wire data from incoming packet client class
+/// has to specify options of \ref perfdhcp::LocalizedOption type
+/// with expected offsets of these options in a packet. The
+/// \ref perfdhcp::PerfPkt6 will use offsets to read fragments
+/// of packet and store them in options' buffers.
+/// (\see perfdhcp::PerfPkt6::rawUnpack).
+///
+class LocalizedOption : public dhcp::Option {
+public:
+    /// \brief Constructor, sets default (0) option offset
+    ///
+    /// \param u specifies universe (V4 or V6)
+    /// \param type option type (0-255 for V4 and 0-65535 for V6)
+    /// \param data content of the option
+    LocalizedOption(dhcp::Option::Universe u,
+                    uint16_t type,
+                    const dhcp::OptionBuffer& data) :
+        dhcp::Option(u, type, data),
+        offset_(0) {
+    }
+
+
+    /// \brief Constructor, used to create localized option from buffer
+    ///
+    /// \param u specifies universe (V4 or V6)
+    /// \param type option type (0-255 for V4 and 0-65535 for V6)
+    /// \param data content of the option
+    /// \param offset location of option in a packet (zero is default)
+    LocalizedOption(dhcp::Option::Universe u,
+                    uint16_t type,
+                    const dhcp::OptionBuffer& data,
+                    const size_t offset) :
+        dhcp::Option(u, type, data),
+        offset_(offset) {
+    }
+
+    /// \brief Constructor, sets default (0) option offset
+    ///
+    /// This contructor is similar to the previous one, but it does not take
+    /// the whole vector<uint8_t>, but rather subset of it.
+    ///
+    /// \param u specifies universe (V4 or V6)
+    /// \param type option type (0-255 for V4 and 0-65535 for V6)
+    /// \param first iterator to the first element that should be copied
+    /// \param last iterator to the next element after the last one
+    ///        to be copied.
+    LocalizedOption(dhcp::Option::Universe u,
+                    uint16_t type,
+                    dhcp::OptionBufferConstIter first,
+                    dhcp::OptionBufferConstIter last) :
+        dhcp::Option(u, type, first, last),
+        offset_(0) {
+    }
+
+
+    /// \brief Constructor, used to create option from buffer iterators
+    ///
+    /// This contructor is similar to the previous one, but it does not take
+    /// the whole vector<uint8_t>, but rather subset of it.
+    ///
+    /// \param u specifies universe (V4 or V6)
+    /// \param type option type (0-255 for V4 and 0-65535 for V6)
+    /// \param first iterator to the first element that should be copied
+    /// \param last iterator to the next element after the last one
+    ///        to be copied.
+    /// \param offset offset of option in a packet (zero is default)
+    LocalizedOption(dhcp::Option::Universe u,
+                    uint16_t type,
+                    dhcp::OptionBufferConstIter first,
+                    dhcp::OptionBufferConstIter last, const size_t offset) :
+        dhcp::Option(u, type, first, last),
+        offset_(offset) {
+    }
+
+    /// \brief Returns offset of an option in a DHCP packet.
+    ///
+    /// \return option offset in a packet
+    size_t getOffset() const { return offset_; };
+
+private:
+    size_t offset_;   ///< Offset of DHCP option in a packet
+};
+
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // __LOCALIZED_OPTION_H
diff --git a/tests/tools/perfdhcp/perf_pkt4.cc b/tests/tools/perfdhcp/perf_pkt4.cc
new file mode 100644
index 0000000..3f733af
--- /dev/null
+++ b/tests/tools/perfdhcp/perf_pkt4.cc
@@ -0,0 +1,62 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/libdhcp++.h>
+#include <dhcp/dhcp6.h>
+
+#include "perf_pkt4.h"
+#include "pkt_transform.h"
+
+using namespace std;
+using namespace isc;
+using namespace dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+PerfPkt4::PerfPkt4(const uint8_t* buf,
+                   size_t len,
+                   size_t transid_offset,
+                   uint32_t transid) :
+    Pkt4(buf, len),
+    transid_offset_(transid_offset) {
+    setTransid(transid);
+}
+
+bool
+PerfPkt4::rawPack() {
+    return (PktTransform::pack(dhcp::Option::V4,
+                               data_,
+                               options_,
+                               getTransidOffset(),
+                               getTransid(),
+                               bufferOut_));
+}
+
+bool
+PerfPkt4::rawUnpack() {
+    uint32_t transid = getTransid();
+    bool res = PktTransform::unpack(dhcp::Option::V4,
+                                    data_,
+                                    options_,
+                                    getTransidOffset(),
+                                    transid);
+    if (res) {
+        setTransid(transid);
+    }
+    return (res);
+}
+
+} // namespace perfdhcp
+} // namespace isc
diff --git a/tests/tools/perfdhcp/perf_pkt4.h b/tests/tools/perfdhcp/perf_pkt4.h
new file mode 100644
index 0000000..f4cc440
--- /dev/null
+++ b/tests/tools/perfdhcp/perf_pkt4.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PERF_PKT4_H
+#define __PERF_PKT4_H
+
+#include <time.h>
+#include <boost/shared_ptr.hpp>
+#include <dhcp/pkt4.h>
+
+#include "localized_option.h"
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief PerfPkt4 (DHCPv4 packet)
+///
+/// This class extends the functionality of \ref isc::dhcp::Pkt4 by adding the
+/// ability to specify an options offset in the DHCP message and to override
+/// options' contents.  This is particularly useful when we create a packet
+/// object using a template file (i.e. do not build it dynamically). The client
+/// class should read data from the template file and pass it to this class in
+/// a buffer.
+///
+/// The contents of such a packet can be later partially replaced, notably the
+/// selected options and the transaction ID.  (The transaction ID and its
+/// offset in the template file are passed via the constructor.)
+///
+/// In order to replace contents of the options, the client class has to
+/// create a collection of \ref LocalizedOption, adding them using
+/// \ref dhcp::Pkt4::addOption.
+///
+/// \note If you don't use template files simply use constructors
+/// inherited from parent class and isc::dhcp::Option type instead
+
+class PerfPkt4 : public dhcp::Pkt4 {
+public:
+
+    /// Localized option pointer type.
+    typedef boost::shared_ptr<LocalizedOption> LocalizedOptionPtr;
+
+    /// \brief Constructor, used to create  messages from packet
+    /// template files.
+    ///
+    /// Creates a new DHCPv4 message using the provided buffer.
+    /// The transaction ID and its offset are specified via this
+    /// constructor. The transaction ID is stored in outgoing message
+    /// when client class calls \ref PerfPkt4::rawPack. Transaction id
+    /// offset value is used for incoming and outgoing messages to
+    /// identify transaction ID field's position in incoming and outgoing
+    /// messages.
+    ///
+    /// \param buf buffer holding contents of the message (this can
+    /// be directly read from template file).
+    /// \param len length of the data in the buffer.
+    /// \param transid_offset transaction id offset in a message.
+    /// \param transid transaction id to be stored in outgoing message.
+    PerfPkt4(const uint8_t* buf,
+             size_t len,
+             size_t transid_offset = 1,
+             uint32_t transid = 0);
+
+    /// \brief Returns transaction id offset in packet buffer
+    ///
+    /// \return Transaction ID offset in packet buffer
+    size_t getTransidOffset() const { return transid_offset_; };
+
+    /// \brief Prepares on-wire format from raw buffer.
+    ///
+    /// The method copies the buffer provided in the constructor to the
+    /// output buffer and replaces the transaction ID and selected
+    /// options with new data.
+    ///
+    /// \note Use this method to prepare an on-wire DHCPv4 message
+    /// when you use template packets that require replacement
+    /// of selected options' contents before sending.
+    ///
+    /// \return false ID pack operation failed.
+    bool rawPack();
+
+    /// \brief Handles limited binary packet parsing for packets with
+    /// custom offsets of options and transaction ID
+    ///
+    /// This method handles the parsing of packets that have custom offsets
+    /// of options or transaction ID. Use
+    /// \ref isc::dhcp::Pkt4::addOption to specify which options to parse.
+    /// Options should be of the \ref isc::perfdhcp::LocalizedOption
+    /// type with offset values provided. Each added option will
+    /// be updated with actual data read from the binary packet buffer.
+    ///
+    /// \return false If unpack operation failed.
+    bool rawUnpack();
+
+private:
+    size_t transid_offset_;      ///< transaction id offset
+
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // __PERF_PKT4_H
diff --git a/tests/tools/perfdhcp/perf_pkt6.cc b/tests/tools/perfdhcp/perf_pkt6.cc
new file mode 100644
index 0000000..24cfb93
--- /dev/null
+++ b/tests/tools/perfdhcp/perf_pkt6.cc
@@ -0,0 +1,64 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+#include <exceptions/exceptions.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/dhcp6.h>
+
+#include "perf_pkt6.h"
+#include "pkt_transform.h"
+
+using namespace std;
+using namespace isc;
+using namespace dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+PerfPkt6::PerfPkt6(const uint8_t* buf,
+                   size_t len,
+                   size_t transid_offset,
+                   uint32_t transid) :
+    Pkt6(buf, len, Pkt6::UDP),
+    transid_offset_(transid_offset) {
+    setTransid(transid);
+}
+
+bool
+PerfPkt6::rawPack() {
+    return (PktTransform::pack(dhcp::Option::V6,
+                               data_,
+                               options_,
+                               getTransidOffset(),
+                               getTransid(),
+                               bufferOut_));
+}
+
+bool
+PerfPkt6::rawUnpack() {
+    uint32_t transid = getTransid();
+    bool res =  PktTransform::unpack(dhcp::Option::V6,
+                                     data_,
+                                     options_,
+                                     getTransidOffset(),
+                                     transid);
+    if (res) {
+        setTransid(transid);
+    }
+    return (res);
+}
+
+} // namespace perfdhcp
+} // namespace isc
diff --git a/tests/tools/perfdhcp/perf_pkt6.h b/tests/tools/perfdhcp/perf_pkt6.h
new file mode 100644
index 0000000..94fe47b
--- /dev/null
+++ b/tests/tools/perfdhcp/perf_pkt6.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PERF_PKT6_H
+#define __PERF_PKT6_H
+
+#include <time.h>
+#include <boost/shared_ptr.hpp>
+#include <dhcp/pkt6.h>
+
+#include "localized_option.h"
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief PerfPkt6 (DHCPv6 packet)
+///
+/// This class extends the functionality of \ref isc::dhcp::Pkt6 by
+/// adding the ability to specify an options offset in the DHCP message
+/// and so override the options' contents. This is particularly useful when we
+/// create a packet object using a template file (i.e. do not build it
+/// dynamically). The client class should read the data from the template file
+/// and pass it to this class as a buffer.
+///
+/// The contents of such packet can be later partially replaced: in particular,
+/// selected options and the transaction ID can be altered. (The transaction
+/// ID and its offset in the template file is passed via the constructor.)
+///
+/// In order to replace the contents of options, the client class has to
+/// create a collection of \ref LocalizedOption by adding them using
+/// \ref dhcp::Pkt6::addOption.
+///
+/// \note If you don't use template files, simply use constructors
+/// inherited from parent class and the \ref isc::dhcp::Option type instead.
+
+class PerfPkt6 : public dhcp::Pkt6 {
+public:
+
+    /// Localized option pointer type.
+    typedef boost::shared_ptr<LocalizedOption> LocalizedOptionPtr;
+
+    /// \brief Constructor, used to create messages from packet
+    /// template files.
+    ///
+    /// Creates a new DHCPv6 message using the provided buffer.
+    /// The transaction ID and its offset are specified via this
+    /// constructor. The transaction ID is stored in outgoing message
+    /// when client class calls \ref PerfPkt6::rawPack. Transaction id
+    /// offset value is used for incoming and outgoing messages to
+    /// identify transaction ID field's position in incoming and outgoing
+    /// messages.
+    ///
+    /// \param buf buffer holding contents of the message (this can
+    /// be directly read from template file).
+    /// \param len length of the data in the buffer.
+    /// \param transid_offset transaction id offset in a message.
+    /// \param transid transaction id to be stored in outgoing message.
+    PerfPkt6(const uint8_t* buf,
+             size_t len,
+             size_t transid_offset = 1,
+             uint32_t transid = 0);
+
+    /// \brief Returns transaction id offset in packet buffer
+    ///
+    /// \return Transaction ID offset in the packet buffer.
+    size_t getTransidOffset() const { return transid_offset_; };
+
+    /// \brief Prepares on-wire format from raw buffer
+    ///
+    /// The method copies the buffer provided in constructor to the
+    /// output buffer and replaces the transaction ID and selected
+    /// options with new data.
+    ///
+    /// \note Use this method to prepare an on-wire DHCPv6 message
+    /// when you use template packets that require replacement
+    /// of selected options' contents before sending.
+    ///
+    /// \return false ID pack operation failed.
+    bool rawPack();
+
+    /// \brief Handles limited binary packet parsing for packets with
+    /// custom offsets of options and transaction id
+    ///
+    /// This methoid handles the parsing of packets that have custom offsets
+    /// of options or transaction ID. Use
+    /// \ref isc::dhcp::Pkt4::addOption to specify which options to parse.
+    /// Options should be of the \ref isc::perfdhcp::LocalizedOption
+    /// type with offset values provided. Each added option will
+    /// be updated with actual data read from the binary packet buffer.
+    ///
+    /// \return false if unpack operation failed.
+    bool rawUnpack();
+
+private:
+    size_t transid_offset_;      ///< transaction id offset
+
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // __PERF_PKT6_H
diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc
new file mode 100644
index 0000000..5ed39bf
--- /dev/null
+++ b/tests/tools/perfdhcp/pkt_transform.cc
@@ -0,0 +1,222 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <iostream>
+
+#include <exceptions/exceptions.h>
+#include <dhcp/option.h>
+#include <dhcp/libdhcp++.h>
+#include <dhcp/dhcp6.h>
+
+#include "pkt_transform.h"
+#include "localized_option.h"
+
+using namespace std;
+using namespace isc;
+using namespace dhcp;
+
+namespace isc {
+namespace perfdhcp {
+
+bool
+PktTransform::pack(const Option::Universe universe,
+                   const OptionBuffer& in_buffer,
+                   const Option::OptionCollection& options,
+                   const size_t transid_offset,
+                   const uint32_t transid,
+                   util::OutputBuffer& out_buffer) {
+
+    // Always override the packet if function is called.
+    out_buffer.clear();
+    // Write whole buffer to output buffer.
+    out_buffer.writeData(&in_buffer[0], in_buffer.size());
+
+    uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
+
+    if ((transid_offset + transid_len >= in_buffer.size()) ||
+        (transid_offset == 0)) {
+        cout << "Failed to build packet: provided transaction id offset: "
+             << transid_offset <<  " is out of bounds (expected 1.."
+             << in_buffer.size()-1 << ")." << endl;
+        return (false);
+    }
+
+    try {
+        size_t offset_ptr = transid_offset;
+        if (universe == Option::V4) {
+            out_buffer.writeUint8At(transid >> 24 & 0xFF, offset_ptr++);
+        }
+        out_buffer.writeUint8At(transid >> 16 & 0xFF, offset_ptr++);
+        out_buffer.writeUint8At(transid >> 8 & 0xFF, offset_ptr++);
+        out_buffer.writeUint8At(transid & 0xFF, offset_ptr++);
+
+        // We already have packet template stored in output buffer
+        // but still some options have to be updated if client
+        // specified them along with their offsets in the buffer.
+        PktTransform::packOptions(in_buffer, options, out_buffer);
+    } catch (const isc::BadValue& e) {
+        cout << "Building packet failed: " << e.what() << endl;
+        return (false);
+    }
+    return (true);
+}
+
+bool
+PktTransform::unpack(const Option::Universe universe,
+                     const OptionBuffer& in_buffer,
+                     const Option::OptionCollection& options,
+                     const size_t transid_offset,
+                     uint32_t& transid) {
+
+    uint8_t transid_len = (universe == Option::V6) ? 3 : 4;
+
+    // Validate transaction id offset.
+    if ((transid_offset + transid_len + 1 > in_buffer.size()) ||
+        (transid_offset == 0)) {
+        cout << "Failed to parse packet: provided transaction id offset: "
+             << transid_offset <<  " is out of bounds (expected 1.."
+             << in_buffer.size()-1 << ")." << endl;
+        return (false);
+    }
+
+    // Read transaction id from the buffer.
+    // For DHCPv6 we transaction id is 3 bytes long so the high byte
+    // of transid will be zero.
+    OptionBufferConstIter it = in_buffer.begin() + transid_offset;
+    transid = 0;
+    for (int i = 0; i < transid_len; ++i, ++it) {
+        // Read next byte and shift it left to its position in
+        // transid (shift by the number of bytes read so far.
+        transid += *it << (transid_len - i - 1) * 8;
+    }
+
+    try {
+        PktTransform::unpackOptions(in_buffer, options);
+    } catch (const isc::BadValue& e) {
+        cout << "Packet parsing failed: " << e.what() << endl;
+        return (false);
+    }
+
+    return (true);
+}
+
+void
+PktTransform::packOptions(const OptionBuffer& in_buffer,
+                          const Option::OptionCollection& options,
+                          util::OutputBuffer& out_buffer) {
+    try {
+        // If there are any options on the list, we will use provided
+        // options offsets to override them in the output buffer
+        // with new contents.
+        for (Option::OptionCollection::const_iterator it = options.begin();
+             it != options.end(); ++it) {
+            // Get options with their position (offset).
+            boost::shared_ptr<LocalizedOption> option =
+                boost::dynamic_pointer_cast<LocalizedOption>(it->second);
+            if (option == NULL) {
+                isc_throw(isc::BadValue, "option is null");
+            }
+            uint32_t offset = option->getOffset();
+            if ((offset == 0) ||
+                (offset + option->len() > in_buffer.size())) {
+                isc_throw(isc::BadValue,
+                          "option offset for option: " << option->getType()
+                          << " is out of bounds (expected 1.."
+                          << in_buffer.size() - option->len() << ")");
+            }
+
+            // Create temporary buffer to store option contents.
+            util::OutputBuffer buf(option->len());
+            // Pack option contents into temporary buffer.
+            option->pack(buf);
+            // OutputBuffer class has nice functions that write
+            // data at the specified position so we can use it to
+            // inject contents of temporary buffer to output buffer.
+            const uint8_t *buf_data =
+                static_cast<const uint8_t*>(buf.getData());
+            for (int i = 0; i < buf.getLength(); ++i) {
+                out_buffer.writeUint8At(buf_data[i], offset + i);
+            }
+        }
+    }
+    catch (const Exception&) {
+        isc_throw(isc::BadValue, "failed to pack options into buffer.");
+    }
+}
+
+void
+PktTransform::unpackOptions(const OptionBuffer& in_buffer,
+                            const Option::OptionCollection& options) {
+    for (Option::OptionCollection::const_iterator it = options.begin();
+         it != options.end(); ++it) {
+
+        boost::shared_ptr<LocalizedOption> option =
+            boost::dynamic_pointer_cast<LocalizedOption>(it->second);
+        if (option == NULL) {
+            isc_throw(isc::BadValue, "option is null");
+        }
+        size_t opt_pos = option->getOffset();
+        if (opt_pos == 0) {
+            isc_throw(isc::BadValue, "failed to unpack packet from raw buffer "
+                      "(Option position not specified)");
+        } else if (opt_pos + option->getHeaderLen() > in_buffer.size()) {
+            isc_throw(isc::BadValue,
+                      "failed to unpack options from from raw buffer "
+                      "(Option position out of bounds)");
+        }
+
+        size_t offset = opt_pos;
+        size_t offset_step = 1;
+        uint16_t opt_type = 0;
+        if (option->getUniverse() == Option::V6) {
+            offset_step = 2;
+            // For DHCPv6 option type is in first two octets.
+            opt_type = in_buffer[offset] * 256 + in_buffer[offset + 1];
+        } else {
+            // For DHCPv4 option type is in first octet.
+            opt_type = in_buffer[offset];
+        }
+        // Check if we got expected option type.
+        if (opt_type != option->getType()) {
+            isc_throw(isc::BadValue,
+                      "failed to unpack option from raw buffer "
+                      "(option type mismatch)");
+        }
+
+        // Get option length which is supposed to be after option type.
+        offset += offset_step;
+        uint16_t opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
+        if (option->getUniverse() == Option::V6) {
+            opt_len = in_buffer[offset] * 256 + in_buffer[offset + 1];
+        } else {
+            opt_len = in_buffer[offset];
+        }
+
+        // Check if packet is not truncated.
+        if (offset + option->getHeaderLen() + opt_len > in_buffer.size()) {
+            isc_throw(isc::BadValue,
+                      "failed to unpack option from raw buffer "
+                      "(option truncated)");
+        }
+
+        // Seek to actual option data and replace it.
+        offset += offset_step;
+        option->setData(in_buffer.begin() + offset,
+                        in_buffer.begin() + offset + opt_len);
+    }
+}
+
+
+} // namespace perfdhcp
+} // namespace isc
diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h
new file mode 100644
index 0000000..7fb19f4
--- /dev/null
+++ b/tests/tools/perfdhcp/pkt_transform.h
@@ -0,0 +1,139 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef __PKT_TRANSFORM_H
+#define __PKT_TRANSFORM_H
+
+#include <dhcp/option.h>
+
+#include "localized_option.h"
+
+namespace isc {
+namespace perfdhcp {
+
+/// \brief Read and write raw data to DHCP packets.
+///
+/// This class provides static functions to read/write raw data from/to the
+/// packet buffer. When reading data with the unpack() method, the
+/// corresponding options objects are updated.  When writing to the packet
+/// buffer with pack(), options objects carry input data to be written.
+///
+/// This class is used both by \ref PerfPkt4 and
+/// \ref PerfPkt6 classes in case DHCP packets are created
+/// from template files. In this case, some of the template
+/// packet's options are replaced before sending it to the
+/// server. Offset of specific options are provided from the
+/// command line by the perfdhcp tool user, and passed in an
+/// options collection.
+class PktTransform {
+public:
+
+    /// \brief Prepares on-wire format from raw buffer.
+    ///
+    /// The method copies the input buffer and options contents
+    /// to the output buffer. The input buffer must contain whole
+    /// initial packet data. Parts of this data will be
+    /// overriden by options data specified in an options
+    /// collection. Such options must have their offsets within
+    /// a packet specified (see \ref LocalizedOption to find out
+    /// how to specify options offset).
+    ///
+    /// \note The specified options must fit into the size of the
+    /// initial packet data. A call to this method will fail
+    /// if the option's offset + its size is beyond the packet's size.
+    ///
+    /// \param universe Universe used, V4 or V6
+    /// \param in_buffer Input buffer holding intial packet
+    /// data, this can be directly read from template file
+    /// \param options Options collection with offsets
+    /// \param transid_offset offset of transaction id in a packet,
+    /// transaction ID will be written to output buffer at this
+    /// offset
+    /// \param transid Transaction ID value
+    /// \param out_buffer Output buffer holding "packed" data
+    ///
+    /// \return false, if pack operation failed.
+    static bool pack(const dhcp::Option::Universe universe,
+                     const dhcp::OptionBuffer& in_buffer,
+                     const dhcp::Option::OptionCollection& options,
+                     const size_t transid_offset,
+                     const uint32_t transid,
+                     util::OutputBuffer& out_buffer);
+
+    /// \brief Handles selective binary packet parsing.
+    ///
+    /// This method handles the parsing of packets that have non-default
+    /// options or transaction ID offsets. The client class has to use
+    /// \ref isc::dhcp::Pkt6::addOption to specify which options to parse.
+    /// Each option should be of the \ref isc::perfdhcp::LocalizedOption
+    /// type with the offset value specified.
+    ///
+    /// \param universe universe used, V4 or V6
+    /// \param in_buffer input buffer to be parsed
+    /// \param options options collection with options offsets
+    /// \param transid_offset offset of transaction id in input buffer
+    /// \param transid transaction id value read from input buffer
+    ///
+    /// \return false, if unpack operation failed.
+    static bool unpack(const dhcp::Option::Universe universe,
+                       const dhcp::OptionBuffer& in_buffer,
+                       const dhcp::Option::OptionCollection& options,
+                       const size_t transid_offset,
+                       uint32_t& transid);
+
+private:
+    /// \brief Replaces contents of options in a buffer.
+    ///
+    /// The method uses a localized options collection to
+    /// replace parts of packet data (e.g. data read
+    /// from template file).
+    /// This private method is called from \ref PktTransform::pack
+    ///
+    /// \param in_buffer input buffer holding initial packet data.
+    /// \param out_buffer output buffer with "packed" options.
+    /// \param options options collection with actual data and offsets.
+    ///
+    /// \throw isc::Unexpected if options update failed.
+    static void packOptions(const dhcp::OptionBuffer& in_buffer,
+                            const dhcp::Option::OptionCollection& options,
+                            util::OutputBuffer& out_buffer);
+
+    /// \brief Reads contents of specified options from buffer.
+    ///
+    /// The method reads options data from the input buffer
+    /// and stores it in options objects. Offsets of the options
+    /// must be specified (see \ref LocalizedOption to find out how to specify
+    /// the option offset).
+    /// This private method is called by \ref PktTransform::unpack.
+    ///
+    /// \note This method iterates through all options in an
+    /// options collection, checks the offset of the option
+    /// in input buffer and reads data from the buffer to
+    /// update the option's buffer. If the provided options collection
+    /// is empty, a call to this method will have no effect.
+    ///
+    /// \param universe universe used, V4 or V6
+    /// \param in_buffer input buffer to be parsed.
+    /// \param options oprions collection with their offsets
+    /// in input buffer specified.
+    ///
+    /// \throw isc::Unexpected if options unpack failed.
+    static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
+                              const dhcp::Option::OptionCollection& options);
+};
+
+} // namespace perfdhcp
+} // namespace isc
+
+#endif // __PKT_TRANSFORM_H
diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am
index c94ecba..3147fea 100644
--- a/tests/tools/perfdhcp/tests/Makefile.am
+++ b/tests/tools/perfdhcp/tests/Makefile.am
@@ -15,14 +15,26 @@ if HAVE_GTEST
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += command_options_unittest.cc
+run_unittests_SOURCES += perf_pkt6_unittest.cc
+run_unittests_SOURCES += perf_pkt4_unittest.cc
+run_unittests_SOURCES += localized_option_unittest.cc
 run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
+run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
 
 run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
 run_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
 
+if USE_CLANGPP
+run_unittests_CXXFLAGS = -Wno-unused-parameter
+endif
+
 run_unittests_LDADD  = $(GTEST_LDADD)
 run_unittests_LDADD += $(top_builddir)/src/lib/util/libutil.la
 run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libexceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libasiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la
 run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 endif
 
diff --git a/tests/tools/perfdhcp/tests/command_options_unittest.cc b/tests/tools/perfdhcp/tests/command_options_unittest.cc
index c92edd0..8e1053d 100644
--- a/tests/tools/perfdhcp/tests/command_options_unittest.cc
+++ b/tests/tools/perfdhcp/tests/command_options_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
diff --git a/tests/tools/perfdhcp/tests/localized_option_unittest.cc b/tests/tools/perfdhcp/tests/localized_option_unittest.cc
new file mode 100644
index 0000000..e51560e
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/localized_option_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <gtest/gtest.h>
+
+#include <dhcp/option.h>
+#include <dhcp/dhcp6.h>
+
+#include "../localized_option.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+namespace {
+
+TEST(LocalizedOptionTest, Constructor) {
+    OptionBuffer opt_buf;
+    // Create option with default offset.
+    boost::scoped_ptr<LocalizedOption> opt1(new LocalizedOption(Option::V6,
+                                                                D6O_CLIENTID,
+                                                                opt_buf));
+    EXPECT_EQ(Option::V6, opt1->getUniverse());
+    EXPECT_EQ(D6O_CLIENTID, opt1->getType());
+    EXPECT_EQ(0, opt1->getOffset());
+
+    // Create option with non-default offset.
+    boost::scoped_ptr<LocalizedOption> opt2(new LocalizedOption(Option::V6,
+                                                                D6O_CLIENTID,
+                                                                opt_buf,
+                                                                40));
+    EXPECT_EQ(40, opt2->getOffset());
+}
+
+}
diff --git a/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc
new file mode 100644
index 0000000..3863faa
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc
@@ -0,0 +1,384 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/dhcp4.h>
+
+#include "../localized_option.h"
+#include "../perf_pkt4.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+typedef PerfPkt4::LocalizedOptionPtr LocalizedOptionPtr;
+
+namespace {
+
+// A dummy MAC address, padded with 0s
+const uint8_t dummyChaddr[16] = {0, 1, 2, 3, 4, 5, 0, 0,
+                                 0, 0, 0, 0, 0, 0, 0, 0 };
+
+// Let's use some creative test content here (128 chars + \0)
+const uint8_t dummyFile[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit. Proin mollis placerat metus, at "
+    "lacinia orci ornare vitae. Mauris amet.";
+
+// Yet another type of test content (64 chars + \0)
+const uint8_t dummySname[] = "Lorem ipsum dolor sit amet, consectetur "
+    "adipiscing elit posuere.";
+
+class PerfPkt4Test : public ::testing::Test {
+public:
+    PerfPkt4Test() {
+    }
+
+    /// \brief Returns buffer with sample DHCPDISCOVER message.
+    ///
+    /// This method creates buffer containing on-wire data of
+    /// DHCPDICOSVER message. This buffer is used by tests below
+    /// to create DHCPv4 test packets.
+    ///
+    /// \return vector containing on-wire data
+    std::vector<uint8_t>& capture() {
+
+        // That is only part of the header. It contains all "short" fields,
+        // larger fields are constructed separately.
+        uint8_t hdr[] = {
+            1, 6, 6, 13,            // op, htype, hlen, hops,
+            0x12, 0x34, 0x56, 0x78, // transaction-id
+            0, 42, 0x80, 0x00,      // 42 secs, BROADCAST flags
+            192, 0, 2, 1,           // ciaddr
+            1, 2, 3, 4,             // yiaddr
+            192, 0, 2, 255,         // siaddr
+            255, 255, 255, 255,     // giaddr
+        };
+
+        uint8_t v4Opts[] = {
+            DHO_HOST_NAME, 3, 0,   1,  2,  // Host name option.
+            DHO_BOOT_SIZE, 3, 10, 11, 12,  // Boot file size option
+            DHO_MERIT_DUMP, 3, 20, 21, 22, // Merit dump file
+            DHO_DHCP_MESSAGE_TYPE, 1, 1,   // DHCP message type.
+            128, 3, 30, 31, 32,
+            254, 3, 40, 41, 42,
+        };
+
+        // Initialize the vector with the header fields defined above.
+        static std::vector<uint8_t> buf(hdr, hdr + sizeof(hdr));
+
+        // If this is a first call to this function. Initialize
+        // remaining data.
+        if (buf.size() == sizeof(hdr)) {
+
+            // Append the large header fields.
+            std::copy(dummyChaddr, dummyChaddr + Pkt4::MAX_CHADDR_LEN,
+                      back_inserter(buf));
+            std::copy(dummySname, dummySname + Pkt4::MAX_SNAME_LEN,
+                      back_inserter(buf));
+            std::copy(dummyFile, dummyFile + Pkt4::MAX_FILE_LEN,
+                      back_inserter(buf));
+
+            // Append magic cookie.
+            buf.push_back(0x63);
+            buf.push_back(0x82);
+            buf.push_back(0x53);
+            buf.push_back(0x63);
+
+            // Append options.
+            std::copy(v4Opts, v4Opts + sizeof(v4Opts), back_inserter(buf));
+        }
+        return buf;
+    }
+};
+
+TEST_F(PerfPkt4Test, Constructor) {
+    // Initialize some dummy payload.
+    uint8_t data[250];
+    for (int i = 0; i < 250; ++i) {
+        data[i] = i;
+    }
+
+    // Test constructor to be used for incoming messages.
+    // Use default (1) offset value and don't specify transaction id.
+    const size_t offset_transid[] = { 1, 10 };
+    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(data,
+                                                  sizeof(data),
+                                                  offset_transid[0]));
+    EXPECT_EQ(1, pkt1->getTransidOffset());
+
+    // Test constructor to be used for outgoing messages.
+    // Use non-zero offset and specify transaction id.
+    const uint32_t transid = 0x010203;
+    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(data, sizeof(data),
+                                                  offset_transid[1],
+                                                  transid));
+    EXPECT_EQ(transid, pkt2->getTransid());
+    EXPECT_EQ(offset_transid[1], pkt2->getTransidOffset());
+
+    // Test default constructor. Transaction id offset is expected to be 1.
+    boost::scoped_ptr<PerfPkt4> pkt3(new PerfPkt4(data, sizeof(data)));
+    EXPECT_EQ(1, pkt3->getTransidOffset());
+}
+
+TEST_F(PerfPkt4Test, RawPack) {
+    // Create new packet.
+    std::vector<uint8_t> buf = capture();
+    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+    // Initialize options data.
+    uint8_t buf_hostname[] = { DHO_HOST_NAME, 3, 4, 5, 6 };
+    uint8_t buf_boot_filesize[] = { DHO_BOOT_SIZE, 3, 1, 2, 3 };
+    OptionBuffer vec_hostname(buf_hostname + 2,
+                              buf_hostname + sizeof(buf_hostname));
+    OptionBuffer vec_boot_filesize(buf_boot_filesize + 2,
+                                   buf_boot_filesize + sizeof(buf_hostname));
+
+    // Create options objects.
+    const size_t offset_hostname = 240;
+    LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4,
+                                                        DHO_HOST_NAME,
+                                                        vec_hostname,
+                                                        offset_hostname));
+    const size_t offset_boot_filesize = 245;
+    LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4,
+                                                             DHO_BOOT_SIZE,
+                                                             vec_boot_filesize,
+                                                             offset_boot_filesize));
+
+    // Try to add options to packet.
+    ASSERT_NO_THROW(pkt->addOption(pkt_boot_filesize));
+    ASSERT_NO_THROW(pkt->addOption(pkt_hostname));
+
+    // We have valid options addedwith valid offsets so
+    // pack operation should succeed.
+    ASSERT_TRUE(pkt->rawPack());
+
+    // Buffer should now contain new values of DHO_HOST_NAME and
+    // DHO_BOOT_SIZE options.
+    util::OutputBuffer pkt_output = pkt->getBuffer();
+    ASSERT_EQ(buf.size(), pkt_output.getLength());
+    const uint8_t* out_buf_data =
+        static_cast<const uint8_t*>(pkt_output.getData());
+
+    // Check if options we read from buffer is valid.
+    EXPECT_EQ(0, memcmp(buf_hostname,
+                        out_buf_data + offset_hostname,
+                        sizeof(buf_hostname)));
+    EXPECT_EQ(0, memcmp(buf_boot_filesize,
+                        out_buf_data + offset_boot_filesize,
+                        sizeof(buf_boot_filesize)));
+}
+
+TEST_F(PerfPkt4Test, RawUnpack) {
+    // Create new packet.
+    std::vector<uint8_t> buf = capture();
+    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+    // Create options (existing in the packet) and specify their offsets.
+    const size_t offset_merit = 250;
+    LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
+                                                     DHO_MERIT_DUMP,
+                                                     OptionBuffer(),
+                                                     offset_merit));
+
+    const size_t  offset_msg_type = 255;
+    LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
+                                                        DHO_DHCP_MESSAGE_TYPE,
+                                                        OptionBuffer(),
+                                                        offset_msg_type));
+    // Addition should be successful
+    ASSERT_NO_THROW(pkt->addOption(opt_merit));
+    ASSERT_NO_THROW(pkt->addOption(opt_msg_type));
+
+    // Option fit to packet boundaries and offsets are valid,
+    // so this should unpack successfully.
+    ASSERT_TRUE(pkt->rawUnpack());
+
+    // At this point we should have updated options data (read from buffer).
+    // Let's try to retrieve them.
+    opt_merit = boost::dynamic_pointer_cast<LocalizedOption>
+        (pkt->getOption(DHO_MERIT_DUMP));
+    opt_msg_type = boost::dynamic_pointer_cast<LocalizedOption>
+        (pkt->getOption(DHO_DHCP_MESSAGE_TYPE));
+    ASSERT_TRUE(opt_merit);
+    ASSERT_TRUE(opt_msg_type);
+
+    // Get first option payload.
+    OptionBuffer opt_merit_data = opt_merit->getData();
+
+    // Define reference data.
+    uint8_t buf_merit[] = { 20, 21, 22 };
+
+    // Validate first option data.
+    ASSERT_EQ(sizeof(buf_merit), opt_merit_data.size());
+    EXPECT_TRUE(std::equal(opt_merit_data.begin(),
+                           opt_merit_data.end(),
+                           buf_merit));
+
+    // Get second option payload.
+    OptionBuffer opt_msg_type_data = opt_msg_type->getData();
+
+    // Expect one byte of message type payload.
+    ASSERT_EQ(1, opt_msg_type_data.size());
+    EXPECT_EQ(1, opt_msg_type_data[0]);
+}
+
+TEST_F(PerfPkt4Test, InvalidOptions) {
+    // Create new packet.
+    std::vector<uint8_t> buf = capture();
+    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size()));
+
+    // Create option with invalid offset.
+    // This option is at offset 250 (not 251).
+    const size_t offset_merit = 251;
+    LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4,
+                                                     DHO_MERIT_DUMP,
+                                                     OptionBuffer(),
+                                                     offset_merit));
+    ASSERT_NO_THROW(pkt1->addOption(opt_merit));
+
+    cout << "Testing unpack of invalid options. "
+         << "This may produce spurious errors." << endl;
+
+    // Unpack is expected to fail because it is supposed to read
+    // option type from buffer and match it with DHO_MERIT_DUMP.
+    // It will not match because option is shifted by on byte.
+    ASSERT_FALSE(pkt1->rawUnpack());
+
+    // Create another packet.
+    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size()));
+
+    // Create DHO_DHCP_MESSAGE_TYPE option that has the wrong offset.
+    // With this offset, option goes beyond packet size (268).
+    const size_t offset_msg_type = 266;
+    LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4,
+                                                        DHO_DHCP_MESSAGE_TYPE,
+                                                        OptionBuffer(1, 2),
+                                                        offset_msg_type));
+    // Adding option is expected to be successful because no
+    // offset validation takes place at this point.
+    ASSERT_NO_THROW(pkt2->addOption(opt_msg_type));
+
+    // This is expected to fail because option is out of bounds.
+    ASSERT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt4Test, TruncatedPacket) {
+    // Get the whole packet and truncate it to 249 bytes.
+    std::vector<uint8_t> buf = capture();
+    buf.resize(249);
+    boost::scoped_ptr<PerfPkt4> pkt(new PerfPkt4(&buf[0], buf.size()));
+
+    // Option DHO_BOOT_SIZE is now truncated because whole packet
+    // is truncated. This option ends at 249 while last index of
+    // truncated packet is now 248.
+    const size_t offset_boot_filesize = 245;
+    LocalizedOptionPtr opt_boot_filesize(new LocalizedOption(Option::V4,
+                                                             DHO_BOOT_SIZE,
+                                                             OptionBuffer(3, 1),
+                                                             offset_boot_filesize));
+    ASSERT_NO_THROW(pkt->addOption(opt_boot_filesize));
+
+    cout << "Testing pack and unpack of options in truncated "
+         << "packet. This may produce spurious errors." << endl;
+
+    // Both pack and unpack are expected to fail because
+    // added option is out of bounds.
+    EXPECT_FALSE(pkt->rawUnpack());
+    EXPECT_FALSE(pkt->rawPack());
+}
+
+TEST_F(PerfPkt4Test, PackTransactionId) {
+    // Create dummy packet that consists of zeros.
+    std::vector<uint8_t> buf(268, 0);
+
+    const size_t offset_transid[] = { 10, 265 };
+    const uint32_t transid = 0x0102;
+    // Initialize transaction id 0x00000102 at offset 10.
+    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&buf[0], buf.size(),
+                                                  offset_transid[0],
+                                                  transid));
+
+    // Pack will inject transaction id at offset 10 into the
+    // packet buffer.
+    ASSERT_TRUE(pkt1->rawPack());
+
+    // Get packet's output buffer and make sure it has valid size.
+    util::OutputBuffer out_buf = pkt1->getBuffer();
+    ASSERT_EQ(buf.size(), out_buf.getLength());
+    const uint8_t *out_buf_data =
+        static_cast<const uint8_t*>(out_buf.getData());
+
+    // Initialize reference data for transaction id.
+    const uint8_t ref_data[] = { 0, 0, 1, 2 };
+
+    // Expect that reference transaction id matches what we have
+    // read from buffer.
+    EXPECT_EQ(0, memcmp(ref_data, out_buf_data + offset_transid[0], 4));
+
+    cout << "Testing pack with invalid transaction id offset. "
+         << "This may produce spurious errors" << endl;
+
+    // Create packet with invalid transaction id offset.
+    // Packet length is 268, transaction id is 4 bytes long so last byte of
+    // transaction id is out of bounds.
+    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&buf[0], buf.size(),
+                                                  offset_transid[1],
+                                                  transid));
+    EXPECT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt4Test, UnpackTransactionId) {
+    // Initialize packet data, lebgth 268, zeros only.
+    std::vector<uint8_t> in_data(268, 0);
+
+    // Assume that transaction id is at offset 100.
+    // Fill 4 bytes at offset 100 with dummy transaction id.
+    for (int i = 100; i < 104; ++i) {
+        in_data[i] = i - 99;
+    }
+
+    // Create packet from initialized buffer.
+    const size_t offset_transid[] = { 100, 270 };
+    boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
+                                                  in_data.size(),
+                                                  offset_transid[0]));
+    ASSERT_TRUE(pkt1->rawUnpack());
+
+    // Get unpacked transaction id and compare with reference.
+    EXPECT_EQ(0x01020304, pkt1->getTransid());
+
+    // Create packet with transaction id at invalid offset.
+    boost::scoped_ptr<PerfPkt4> pkt2(new PerfPkt4(&in_data[0],
+                                                  in_data.size(),
+                                                  offset_transid[1]));
+
+    cout << "Testing unpack of transaction id at invalid offset. "
+         << "This may produce spurious errors." << endl;
+
+    // Unpack is supposed to fail because transaction id is at
+    // out of bounds offset.
+    EXPECT_FALSE(pkt2->rawUnpack());
+}
+
+}
diff --git a/tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc b/tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc
new file mode 100644
index 0000000..de134cc
--- /dev/null
+++ b/tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc
@@ -0,0 +1,327 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcp/dhcp6.h>
+
+#include "../localized_option.h"
+#include "../perf_pkt6.h"
+
+using namespace std;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::perfdhcp;
+
+typedef PerfPkt6::LocalizedOptionPtr LocalizedOptionPtr;
+
+namespace {
+
+class PerfPkt6Test : public ::testing::Test {
+public:
+    PerfPkt6Test() {
+    }
+
+    /// \brief Returns captured SOLICIT packet.
+    ///
+    /// Captured SOLICIT packet with transid=0x3d79fb and options: client-id,
+    /// in_na, dns-server, elapsed-time, option-request
+    /// This code was autogenerated
+    /// (see src/bin/dhcp6/tests/iface_mgr_unittest.c),
+    /// but we spent some time to make is less ugly than it used to be.
+    ///
+    /// \return pointer to Pkt6 that represents received SOLICIT
+    PerfPkt6* capture() {
+        uint8_t data[98];
+        data[0]  = 1;
+        data[1]  = 1;       data[2]  = 2;     data[3] = 3;      data[4]  = 0;
+        data[5]  = 1;       data[6]  = 0;     data[7] = 14;     data[8]  = 0;
+        data[9]  = 1;       data[10] = 0;     data[11] = 1;     data[12] = 21;
+        data[13] = 158;     data[14] = 60;    data[15] = 22;    data[16] = 0;
+        data[17] = 30;      data[18] = 140;   data[19] = 155;   data[20] = 115;
+        data[21] = 73;      data[22] = 0;     data[23] = 3;     data[24] = 0;
+        data[25] = 40;      data[26] = 0;     data[27] = 0;     data[28] = 0;
+        data[29] = 1;       data[30] = 255;   data[31] = 255;   data[32] = 255;
+        data[33] = 255;     data[34] = 255;   data[35] = 255;   data[36] = 255;
+        data[37] = 255;     data[38] = 0;     data[39] = 5;     data[40] = 0;
+        data[41] = 24;      data[42] = 32;    data[43] = 1;     data[44] = 13;
+        data[45] = 184;     data[46] = 0;     data[47] = 1;     data[48] = 0;
+        data[49] = 0;       data[50] = 0;     data[51] = 0;     data[52] = 0;
+        data[53] = 0;       data[54] = 0;     data[55] = 0;     data[56] = 18;
+        data[57] = 52;      data[58] = 255;   data[59] = 255;   data[60] = 255;
+        data[61] = 255;     data[62] = 255;   data[63] = 255;   data[64] = 255;
+        data[65] = 255;     data[66] = 0;     data[67] = 23;    data[68] = 0;
+        data[69] = 16;      data[70] = 32;    data[71] = 1;     data[72] = 13;
+        data[73] = 184;     data[74] = 0;     data[75] = 1;     data[76] = 0;
+        data[77] = 0;       data[78] = 0;     data[79] = 0;     data[80] = 0;
+        data[81] = 0;       data[82] = 0;     data[83] = 0;     data[84] = 221;
+        data[85] = 221;     data[86] = 0;     data[87] = 8;     data[88] = 0;
+        data[89] = 2;       data[90] = 0;     data[91] = 100;   data[92] = 0;
+        data[93] = 6;       data[94] = 0;     data[95] = 2;     data[96] = 0;
+        data[97] = 23;
+
+        PerfPkt6* pkt = new PerfPkt6(data, sizeof(data));
+
+        return (pkt);
+    }
+
+    /// \brief Returns truncated SOLICIT packet.
+    ///
+    /// Returns truncated SOLICIT packet which will be used for
+    /// negative tests: e.g. pack options out of packet.
+    ///
+    /// \return pointer to Pkt6 that represents truncated SOLICIT
+    PerfPkt6* captureTruncated() {
+        uint8_t data[17];
+        data[0]  = 1;
+        data[1]  = 1;       data[2]  = 2;     data[3] = 3;      data[4]  = 0;
+        data[5]  = 1;       data[6]  = 0;     data[7] = 14;     data[8]  = 0;
+        data[9]  = 1;       data[10] = 0;     data[11] = 1;     data[12] = 21;
+        data[13] = 158;     data[14] = 60;    data[15] = 22;    data[16] = 0;
+
+        PerfPkt6* pkt = new PerfPkt6(data, sizeof(data));
+
+        return (pkt);
+    }
+
+
+};
+
+TEST_F(PerfPkt6Test, Constructor) {
+    // Data to be used to create packet.
+    uint8_t data[] = { 0, 1, 2, 3, 4, 5 };
+
+    // Test constructor to be used for incoming messages.
+    // Use default (1) offset value and don't specify transaction id.
+    boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data, sizeof(data)));
+    EXPECT_EQ(sizeof(data), pkt1->getData().size());
+    EXPECT_EQ(0, memcmp(&pkt1->getData()[0], data, sizeof(data)));
+    EXPECT_EQ(1, pkt1->getTransidOffset());
+
+    // Test constructor to be used for outgoing messages.
+    // Use non-zero offset and specify transaction id.
+    const size_t offset_transid = 10;
+    const uint32_t transid = 0x010203;
+    boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data, sizeof(data),
+                                                  offset_transid, transid));
+    EXPECT_EQ(sizeof(data), pkt2->getData().size());
+    EXPECT_EQ(0, memcmp(&pkt2->getData()[0], data, sizeof(data)));
+    EXPECT_EQ(0x010203, pkt2->getTransid());
+    EXPECT_EQ(10, pkt2->getTransidOffset());
+}
+
+TEST_F(PerfPkt6Test, RawPackUnpack) {
+    // Create first packet.
+    boost::scoped_ptr<PerfPkt6> pkt1(capture());
+
+    // Create some input buffers to initialize options.
+    uint8_t buf_elapsed_time[] = { 1, 1 };
+    uint8_t buf_duid[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
+
+    // Create options.
+    const size_t offset_elapsed_time = 86;
+    OptionBuffer vec_elapsed_time(buf_elapsed_time,
+                                  buf_elapsed_time + sizeof(buf_elapsed_time));
+    LocalizedOptionPtr pkt1_elapsed_time(new LocalizedOption(Option::V6,
+                                                             D6O_ELAPSED_TIME,
+                                                             vec_elapsed_time,
+                                                             offset_elapsed_time));
+    const size_t offset_duid = 4;
+    OptionBuffer vec_duid(buf_duid, buf_duid + sizeof(buf_duid));
+    LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+                                                     D6O_CLIENTID,
+                                                     vec_duid,
+                                                     offset_duid));
+
+    // Add option to packet and create on-wire format from added options.
+    // Contents of options will override contents of packet buffer.
+    ASSERT_NO_THROW(pkt1->addOption(pkt1_elapsed_time));
+    ASSERT_NO_THROW(pkt1->addOption(pkt1_duid));
+    ASSERT_TRUE(pkt1->rawPack());
+
+    // Reset so as we can reuse them for another packet.
+    vec_elapsed_time.clear();
+    vec_duid.clear();
+
+    // Get output buffer from packet 1 to create new packet
+    // that will be later validated.
+    util::OutputBuffer pkt1_output = pkt1->getBuffer();
+    ASSERT_EQ(pkt1_output.getLength(), pkt1->getData().size());
+    const uint8_t* pkt1_output_data = static_cast<const uint8_t*>
+        (pkt1_output.getData());
+    boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(pkt1_output_data,
+                                                  pkt1_output.getLength()));
+
+    // Create objects specifying options offset in a packet.
+    // Offsets will inform pkt2 object where to read data from.
+    LocalizedOptionPtr pkt2_elapsed_time(new LocalizedOption(Option::V6,
+                                                             D6O_ELAPSED_TIME,
+                                                             vec_elapsed_time,
+                                                             offset_elapsed_time));
+    LocalizedOptionPtr pkt2_duid(new LocalizedOption(Option::V6,
+                                                     D6O_CLIENTID,
+                                                     vec_duid,
+                                                     offset_duid));
+    // Add options to packet to pass their offsets.
+    pkt2->addOption(pkt2_elapsed_time);
+    pkt2->addOption(pkt2_duid);
+
+    // Unpack: get relevant parts of buffer data into option objects.
+    ASSERT_TRUE(pkt2->rawUnpack());
+
+    // Once option data is stored in options objects we pull it out.
+    pkt2_elapsed_time = boost::dynamic_pointer_cast<LocalizedOption>
+        (pkt2->getOption(D6O_ELAPSED_TIME));
+    pkt2_duid = boost::dynamic_pointer_cast<LocalizedOption>
+        (pkt2->getOption(D6O_CLIENTID));
+
+    // Check if options are present. They have to be there since
+    // we have added them ourselfs.
+    ASSERT_TRUE(pkt2_elapsed_time);
+    ASSERT_TRUE(pkt2_duid);
+
+    // Expecting option contents be the same as original.
+    OptionBuffer pkt2_elapsed_time_data = pkt2_elapsed_time->getData();
+    OptionBuffer pkt2_duid_data = pkt2_duid->getData();
+    EXPECT_EQ(0x0101, pkt2_elapsed_time->getUint16());
+    EXPECT_TRUE(std::equal(pkt2_duid_data.begin(),
+                           pkt2_duid_data.end(),
+                           buf_duid));
+}
+
+TEST_F(PerfPkt6Test, InvalidOptions) {
+    // Create packet.
+    boost::scoped_ptr<PerfPkt6> pkt1(capture());
+    OptionBuffer vec_server_id;
+    vec_server_id.resize(10);
+    // Testing invalid offset of the option (greater than packet size)
+    const size_t offset_serverid[] = { 150, 85 };
+    LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6,
+                                                         D6O_SERVERID,
+                                                         vec_server_id,
+                                                         offset_serverid[0]));
+    pkt1->addOption(pkt1_serverid);
+    // Pack has to fail due to invalid offset.
+    EXPECT_FALSE(pkt1->rawPack());
+
+    // Create packet.
+    boost::scoped_ptr<PerfPkt6> pkt2(capture());
+    // Testing offset of the option (lower than pakcet size but
+    // tail of the option out of bounds).
+    LocalizedOptionPtr pkt2_serverid(new LocalizedOption(Option::V6,
+                                                         D6O_SERVERID,
+                                                         vec_server_id,
+                                                         offset_serverid[1]));
+    pkt2->addOption(pkt2_serverid);
+    // Pack must fail due to invalid offset.
+    EXPECT_FALSE(pkt2->rawPack());
+}
+
+
+TEST_F(PerfPkt6Test, TruncatedPacket) {
+    cout << "Testing parsing options from truncated packet."
+         << "This may produce spurious errors" << endl;
+
+    // Create truncated (in the middle of duid options)
+    boost::scoped_ptr<PerfPkt6> pkt1(captureTruncated());
+    OptionBuffer vec_duid;
+    vec_duid.resize(30);
+    const size_t offset_duid = 4;
+    LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6,
+                                                     D6O_CLIENTID,
+                                                     vec_duid,
+                                                     offset_duid));
+    pkt1->addOption(pkt1_duid);
+    // Pack/unpack must fail because length of the option read from buffer
+    // will extend over the actual packet length.
+    EXPECT_FALSE(pkt1->rawUnpack());
+    EXPECT_FALSE(pkt1->rawPack());
+}
+
+TEST_F(PerfPkt6Test, PackTransactionId) {
+    uint8_t data[100];
+    memset(&data, 0, sizeof(data));
+
+    const size_t offset_transid[] = { 50, 100 };
+    const uint32_t transid = 0x010203;
+
+    // Create dummy packet that is simply filled with zeros.
+    boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+                                                  sizeof(data),
+                                                  offset_transid[0],
+                                                  transid));
+
+    // Reference data are non zero so we can detect them in dummy packet.
+    uint8_t ref_data[3] = { 1, 2, 3 };
+
+    // This will store given transaction id in the packet data at
+    // offset of 50.
+    ASSERT_TRUE(pkt1->rawPack());
+
+    // Get the output buffer so we can validate it.
+    util::OutputBuffer out_buf = pkt1->getBuffer();
+    ASSERT_EQ(sizeof(data), out_buf.getLength());
+    const uint8_t *out_buf_data = static_cast<const uint8_t*>
+        (out_buf.getData());
+
+    // Validate transaction id.
+    EXPECT_EQ(0, memcmp(out_buf_data + offset_transid[0], ref_data, 3));
+
+
+    // Out of bounds transaction id offset.
+    boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+                                                  sizeof(data),
+                                                  offset_transid[1],
+                                                  transid));
+    cout << "Testing out of bounds offset. "
+        "This may produce spurious errors ..." << endl;
+    EXPECT_FALSE(pkt2->rawPack());
+}
+
+TEST_F(PerfPkt6Test, UnpackTransactionId) {
+    // Initialize data for dummy packet (zeros only).
+    uint8_t data[100] = { 0 };
+
+    // Generate transaction id = 0x010203 and inject at offset = 50.
+    for (int i = 50; i <  53; ++i) {
+        data[i] = i - 49;
+    }
+    // Create packet and point out that transaction id is at offset 50.
+    const size_t offset_transid[] = { 50, 300 };
+    boost::scoped_ptr<PerfPkt6> pkt1(new PerfPkt6(data,
+                                                  sizeof(data),
+                                                  offset_transid[0]));
+
+    // Get transaction id out of buffer and store in class member.
+    ASSERT_TRUE(pkt1->rawUnpack());
+    // Test value of transaction id.
+    EXPECT_EQ(0x010203, pkt1->getTransid());
+
+    // Out of bounds transaction id offset.
+    boost::scoped_ptr<PerfPkt6> pkt2(new PerfPkt6(data,
+                                                  sizeof(data),
+                                                  offset_transid[1]));
+    cout << "Testing out of bounds offset. "
+        "This may produce spurious errors ..." << endl;
+    EXPECT_FALSE(pkt2->rawUnpack());
+
+}
+
+}



More information about the bind10-changes mailing list