From: Marcin Siodelski Date: Wed, 30 May 2012 12:59:28 +0000 (+0200) Subject: [1956] Basic unit tests added for PerfPkt6 class. X-Git-Tag: trac2351_base~226^2~58^2~6^2~25 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a6a3d1b172cfd5ba9db4ddb3b8ffcbf673d8e31f;p=thirdparty%2Fkea.git [1956] Basic unit tests added for PerfPkt6 class. --- diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc index 03b4a3d64c..ed3f9dda32 100644 --- a/src/lib/dhcp/option.cc +++ b/src/lib/dhcp/option.cc @@ -270,6 +270,18 @@ 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(last - first); + OptionBufferConstIter x = first; + while (x != last) { + data_[x - first] = *x; + ++x; + } +} + + Option::~Option() { } diff --git a/src/lib/dhcp/option.h b/src/lib/dhcp/option.h index c7f5d10856..4e086e5f78 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(OptionBufferConstIter first, + OptionBufferConstIter last); + /// just to force that every option has virtual dtor virtual ~Option(); diff --git a/tests/tools/perfdhcp/perf_pkt6.cc b/tests/tools/perfdhcp/perf_pkt6.cc index 6a149e81fe..950eb146ba 100644 --- a/tests/tools/perfdhcp/perf_pkt6.cc +++ b/tests/tools/perfdhcp/perf_pkt6.cc @@ -12,8 +12,7 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#include - +#include #include #include @@ -26,24 +25,39 @@ namespace isc { namespace perfdhcp { +PerfPkt6::PositionedOption::PositionedOption(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& data) : + dhcp::Option(u, type, data), + position_(0) { +} + PerfPkt6::PositionedOption::PositionedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data, - const OptionPosition& position) : + const size_t position) : dhcp::Option(u, type, data), position_(position) { } +PerfPkt6::PositionedOption::PositionedOption(dhcp::Option::Universe u, + uint16_t type, + dhcp::OptionBufferConstIter first, + dhcp::OptionBufferConstIter last) : + dhcp::Option(u, type, first, last), + position_(0) { +} + PerfPkt6::PositionedOption::PositionedOption(dhcp::Option::Universe u, uint16_t type, dhcp::OptionBufferConstIter first, dhcp::OptionBufferConstIter last, - const OptionPosition& position) : + const size_t position) : dhcp::Option(u, type, first, last), position_(position) { } -PerfPkt6::PerfPkt6(const uint8_t* buf, uint32_t len, const OptionPosition& transid_offset) : +PerfPkt6::PerfPkt6(const uint8_t* buf, uint32_t len, size_t transid_offset) : Pkt6(buf, len, dhcp::Pkt6::UDP), transid_offset_(transid_offset) { @@ -98,17 +112,18 @@ PerfPkt6::stampedRawUnpack() { // Update packet timestamp for RTT calculations but do not include unpack time. updateTimestamp(); - // Get transaction id offset. - int transid_offset = transid_offset_.get(); // Read transaction id from the packet at the given offset. - transid_ = ( (data_[transid_offset]) << 16 ) + - ((data_[transid_offset + 1]) << 8) + (data_[transid_offset + 2]); + transid_ = ( (data_[transid_offset_]) << 16 ) + + ((data_[transid_offset_ + 1]) << 8) + (data_[transid_offset_ + 2]); transid_ = transid_ & 0xffffff; // Get message type - assume it is first octet in the packet. msg_type_ = data_[0]; + + rawUnpackOptions(); } + void PerfPkt6::updateOptions() { try { @@ -133,6 +148,36 @@ PerfPkt6::updateOptions() { } } +void +PerfPkt6::rawUnpackOptions() { + for (dhcp::Option::OptionCollection::const_iterator it = options_.begin(); + it != options_.end(); ++it) { + + PositionedOptionPtr option = boost::dynamic_pointer_cast(it->second); + size_t opt_pos = option->getOptionPosition(); + + if (opt_pos == 0) { + isc_throw(isc::BadValue, "Failed to unpack packet from raw buffer " + "(Option position not specified)"); + } else if (opt_pos + 4 > data_.size()) { + isc_throw(isc::BadValue, "Failed to unpack options from from raw buffer " + "(Option position out of bounds)"); + } + + size_t offset = opt_pos; + // uint16_t opt_type = data_[offset] * 256 + data_[offset + 1]; + offset += 2; + uint16_t opt_len = data_[offset] * 256 + data_[offset + 1]; + offset += 2; + + if (offset + opt_len > data_.size()) { + isc_throw(isc::BadValue, + "Failed to unpack option from raw buffer (Option truncated)"); + } + option->setData(data_.begin() + offset, data_.begin() + offset + opt_len); + } +} + void PerfPkt6::updateTimestamp() { if (clock_gettime(CLOCK_REALTIME, &time_stamp_) < 0) { diff --git a/tests/tools/perfdhcp/perf_pkt6.h b/tests/tools/perfdhcp/perf_pkt6.h index f3118d2818..4a4bb58068 100644 --- a/tests/tools/perfdhcp/perf_pkt6.h +++ b/tests/tools/perfdhcp/perf_pkt6.h @@ -16,7 +16,7 @@ #define __PERF_PKT6_H #include - +#include #include namespace isc { @@ -55,28 +55,6 @@ namespace perfdhcp { class PerfPkt6 : public dhcp::Pkt6 { public: - /// \brief Class represents position of DHCP option in a packet - class OptionPosition { - public: - /// \brief Class constructor - /// - /// Applies default value of option position - explicit OptionPosition() : position_(0) { }; - - /// \brief Class constructor - /// - /// \param position position of option in DHCP packet - explicit OptionPosition(size_t position) : position_(position) { }; - - /// \brief Returns position of DHCP option in a packet - /// - /// \return position of DHCP option in packet - size_t get() const { return position_; } - private: - size_t position_; ///< position of option in a packet - ///< Zero is default position - }; - /// \brief DHCPv6 option at specific offset /// /// This class represents DHCPv6 option at specified @@ -84,6 +62,16 @@ public: /// class PositionedOption : public dhcp::Option { public: + + /// \brief Constructor, sets default (0) option position + /// + /// + /// \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 + PositionedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data); + + /// \brief Constructor, used to create positioned option from buffer /// /// @@ -92,7 +80,21 @@ public: /// \param data content of the option /// \param position absolute position of option in a packet (zero is default) PositionedOption(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer& data, - const OptionPosition& position); + const size_t position); + + /// \brief Constructor, sets default (0) option position + /// + /// This contructor is similar to the previous one, but it does not take + /// the whole vector, 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 position absolute position of option in a packet (zero is default) + PositionedOption(dhcp::Option::Universe u, uint16_t type, dhcp::OptionBufferConstIter first, + dhcp::OptionBufferConstIter last); /// \brief Constructor, used to create positioned option from buffer iterators /// @@ -106,19 +108,21 @@ public: /// to be copied. /// \param position absolute position of option in a packet (zero is default) PositionedOption(dhcp::Option::Universe u, uint16_t type, dhcp::OptionBufferConstIter first, - dhcp::OptionBufferConstIter last, const OptionPosition& position); + dhcp::OptionBufferConstIter last, const size_t position); /// \brief Returns absolute position (offset) of an option in a /// DHCPv6 packet. /// /// \return absolute position (offset) of an option in a packet - size_t getOptionPosition() const { return position_.get(); }; + size_t getOptionPosition() const { return position_; }; private: - OptionPosition position_; ///< Absolute position of DHCPv6 option in a packet + size_t position_; ///< Absolute position of DHCPv6 option in a packet }; + typedef boost::shared_ptr PositionedOptionPtr; + /// Constructor, used in message transmission /// /// Creates new DHCPv6 message using provided buffer. New object @@ -143,7 +147,17 @@ public: /// \param xid_off transaction id offset in a packet PerfPkt6(const uint8_t* buf, uint32_t len, - const OptionPosition& transid_offset); + size_t transid_offset_); + + /// \brief Returns transaction id offset in packet buffer + /// + /// return transaction id offset in packet buffer + size_t getTransIdOffset() const { return transid_offset_; }; + + /// \brief Returns current packet timestamp + /// + /// \return current packet timestamp + timespec getTimestamp() const { return time_stamp_; } /// \brief Prepare on-wire packet format and record timestamp /// @@ -208,13 +222,15 @@ private: /// throw isc::Unexpected if options update fails void updateOptions(); + void rawUnpackOptions(); + /// \brief Update packet timestamp with current time /// /// \throw isc::Unexpected if timestamp update failed void updateTimestamp(); - OptionPosition transid_offset_; ///< transaction id offset - timespec time_stamp_; ///< time stamp of last pack or unpack + size_t transid_offset_; ///< transaction id offset + timespec time_stamp_; ///< time stamp of last pack or unpack }; diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index c94ecba36c..ed5348d394 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -4,8 +4,9 @@ AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(B10_CXXFLAGS) +AM_LDFLAGS = $(CLOCK_GETTIME_LDFLAGS) if USE_STATIC_LINK -AM_LDFLAGS = -static +AM_LDFLAGS += -static endif CLEANFILES = *.gcno *.gcda @@ -15,7 +16,9 @@ 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 += $(top_builddir)/tests/tools/perfdhcp/command_options.cc +run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) @@ -23,6 +26,7 @@ run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) 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/dhcp/libdhcp++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la endif 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 0000000000..b9a7c97ecf --- /dev/null +++ b/tests/tools/perfdhcp/tests/perf_pkt6_unittest.cc @@ -0,0 +1,140 @@ +// Copyright (C) 2011-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 +#include +#include +#include +#include + +#include +#include +#include + +#include "../perf_pkt6.h" + +using namespace std; +using namespace isc; +using namespace isc::dhcp; +using namespace isc::perfdhcp; + +typedef PerfPkt6::PositionedOptionPtr PositionedOptionPtr; +typedef PerfPkt6::PositionedOption PositionedOption; + +namespace { +class PerfPkt6Test : public ::testing::Test { +public: + PerfPkt6Test() { + } + +/// @brief returns captured actual 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* capture1() { + 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), 0); + + /* pkt->setRemotePort(546); + pkt->setRemoteAddr(IOAddress("fe80::21e:8cff:fe9b:7349")); + pkt->setLocalPort(0); + pkt->setLocalAddr(IOAddress("ff02::1:2")); + pkt->setIndex(2); + pkt->setIface("eth0");*/ + + return (pkt); +} + +}; + +TEST_F(PerfPkt6Test, constructor) { + uint8_t data[] = { 0, 1, 2, 3, 4, 5 }; + timespec zero_t_spec; + memset(&zero_t_spec, 0, sizeof(zero_t_spec)); + + boost::scoped_ptr pkt1(new PerfPkt6(data, sizeof(data), 1)); + timespec packet_t_spec = pkt1->getTimestamp(); + + EXPECT_EQ(6, pkt1->getData().size()); + EXPECT_EQ(0, memcmp( &pkt1->getData()[0], data, sizeof(data))); + EXPECT_EQ(1, pkt1->getTransIdOffset()); + EXPECT_EQ(0, memcmp(&packet_t_spec, &zero_t_spec, sizeof(zero_t_spec))); +} + +TEST_F(PerfPkt6Test, UnpackRawSolicit) { + // Create packetPositionedOptionPtr + boost::scoped_ptr pkt1(capture1()); + // Create some input buffer to initialize options. + uint8_t buf_elapsed_time[] = { 8, 2, 4, 5 }; + // Create options. + dhcp::OptionBuffer buf(buf_elapsed_time, buf_elapsed_time + 4); + PositionedOptionPtr pkt1_elapsed_time(new PositionedOption(Option::V6, 8, buf, 86)); + + // 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->stampedRawPack()); + + // Let's create another packet using packed data stored in first packet. + // We will try to unpack data into option objects. + buf.clear(); + OptionBuffer pkt1_output = pkt1->getData(); + boost::scoped_ptr pkt2(new PerfPkt6(&pkt1_output[0], pkt1_output.size(), 1)); + + // Create partial options to specify offsets of certain options in packet. + PositionedOptionPtr pkt2_elapsed_time(new PositionedOption(Option::V6, D6O_ELAPSED_TIME, buf, 86)); + // Add partial options. + pkt2->addOption(pkt2_elapsed_time); + // Get relevant parts of buffer data into option objects. + ASSERT_NO_THROW(pkt2->stampedRawUnpack()); + // Once option data is updated we pull it out. + pkt2_elapsed_time = boost::dynamic_pointer_cast(pkt2->getOption(D6O_ELAPSED_TIME)); + ASSERT_TRUE(pkt2_elapsed_time); + // Expecting option contents be the same as original. + OptionBuffer pkt2_elapsed_time_data = pkt2_elapsed_time->getData(); + ASSERT_EQ(100, pkt2_elapsed_time->getUint16()); +} + +}