Parcourir la source

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

Marcin Siodelski il y a 13 ans
Parent
commit
6f914bb2c3

+ 1 - 1
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

+ 4 - 0
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

+ 4 - 0
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)

+ 4 - 0
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

+ 5 - 0
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)

+ 4 - 0
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

+ 8 - 0
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,

+ 8 - 0
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() {
 
 }

+ 9 - 0
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();
 

+ 5 - 0
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

+ 47 - 0
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;

+ 6 - 0
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

+ 47 - 0
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;

+ 2 - 2
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

+ 28 - 1
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()));
+}
 }

+ 28 - 0
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

+ 27 - 0
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);
+}
+
 }

+ 11 - 0
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

+ 1 - 1
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

+ 1 - 1
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

+ 123 - 0
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

+ 62 - 0
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

+ 113 - 0
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

+ 64 - 0
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

+ 113 - 0
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

+ 222 - 0
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

+ 139 - 0
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

+ 12 - 0
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
 

+ 1 - 1
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

+ 48 - 0
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());
+}
+
+}

+ 384 - 0
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());
+}
+
+}

+ 327 - 0
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());
+
+}
+
+}