diff --git a/tests/tools/perfdhcp/Makefile.am b/tests/tools/perfdhcp/Makefile.am index 31a75e14a66f80e21129fd8aa7cacfb7a0332274..301ad2a7453c4a800b3017b7a8eafccbee7af09d 100644 --- a/tests/tools/perfdhcp/Makefile.am +++ b/tests/tools/perfdhcp/Makefile.am @@ -28,6 +28,8 @@ perfdhcp_SOURCES = perfdhcp.cc perfdhcp_SOURCES += localized_option.h perfdhcp_SOURCES += command_options.cc command_options.h perfdhcp_SOURCES += perf_pkt6.cc perf_pkt6.h +perfdhcp_SOURCES += perf_pkt4.cc perf_pkt4.h +perfdhcp_SOURCES += pkt_transform.cc pkt_transform.h perfdhcp_LDADD = $(top_builddir)/src/lib/exceptions/libexceptions.la perfdhcp_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la diff --git a/tests/tools/perfdhcp/perf_pkt4.cc b/tests/tools/perfdhcp/perf_pkt4.cc new file mode 100644 index 0000000000000000000000000000000000000000..0f48557a7fcceac2a2107432f5b6225abf7d4171 --- /dev/null +++ b/tests/tools/perfdhcp/perf_pkt4.cc @@ -0,0 +1,61 @@ +// 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 +#include +#include +#include + +#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, uint32_t len, size_t transid_offset, uint32_t transid) : + Pkt4(buf, len), + transid_offset_(transid_offset) { + transid_ = transid; +} + +PerfPkt4::PerfPkt4(const uint8_t* buf, uint32_t len, size_t transid_offset) : + Pkt4(buf, len), + transid_offset_(transid_offset) { +} + +bool +PerfPkt4::rawPack() { + return (PktTransform::pack(dhcp::Option::V4, + data_, + options_, + transid_offset_, + transid_, + bufferOut_)); +} + +bool +PerfPkt4::rawUnpack() { + return (PktTransform::unpack(dhcp::Option::V4, + data_, + options_, + transid_offset_, + transid_)); +} + +} // namespace perfdhcp +} // namespace isc diff --git a/tests/tools/perfdhcp/perf_pkt4.h b/tests/tools/perfdhcp/perf_pkt4.h new file mode 100644 index 0000000000000000000000000000000000000000..88128992a19fd123ff51d54b30088e6f23f923e4 --- /dev/null +++ b/tests/tools/perfdhcp/perf_pkt4.h @@ -0,0 +1,149 @@ +// 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. + +#ifndef __PERF_PKT4_H +#define __PERF_PKT4_H + +#include +#include +#include + +#include "localized_option.h" + +namespace isc { +namespace perfdhcp { + +/// \brief PerfPkt4 (DHCPv4 packet) +/// +/// This class extends functionality of \ref isc::dhcp::Pkt4 by +/// adding ability to specify options offset in DHCP message +/// and override options' contents with new option. +/// This approach is useful when we create paket object from +/// raw template buffer from file and we want to use it as +/// a base to create test packets to be sent to DHCP server. +/// +/// Some of the contents of such a template packets always +/// have to be replaced e.g. transaction id, IA_NA. Other +/// contents (options) may be changed e.g. elapsed time, +/// server id. +/// +/// In order to create packet from raw template buffer +/// we have to pass this buffer along with transaction id +/// offset. Class will read transaction id from the buffer. +/// Next, in order to replace contents of selected options +/// in a template packet, we need to add these selected options +/// to packet object using addOption() method. Please note +/// that options must be of the +/// \ref isc::perfdhcp::LocalizedOption type. +/// +/// \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 LocalizedOptionPtr; + + /// \brief Constructor, used for outgoing DHCP messages. + /// + /// Creates new DHCPv4 message using provided buffer. + /// Transaction id and its offset are specified through this + /// constructor so as they are stored in outgoing message + /// when client class calls \ref PerfPkt4::rawPack. + /// + /// \note This constructor should be used only for outgoing + /// messages that are created from raw buffer (e.g. read from + /// template files). + /// + /// \param buf buffer holiding 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 outgoing message. + /// \param transid transaction id to be stored in outgoing message. + PerfPkt4(const uint8_t* buf, + uint32_t len, + size_t transid_offset, + uint32_t transid); + + /// Constructor, used for incoming DHCP messages. + /// + /// Creates new DHCPv6 message using provided buffer. New object + /// will keep copy of contents of provided buffer. If buffer contains + /// options at custom offsets (e.g. if packet was read from + /// template file) additional information about options' + /// offsets has to be provided - see + /// \ref isc::perfdhcp::LocalizedOption for details. + /// + /// Transaction id offset point to location of raw data where + /// transaction id field is stored. The transaction id will + /// be read from this location when PerfPkt4::rawUnpack is + /// called. The transid_ class member will be updated accordingly. + /// + /// \note use this constructor only in case you want to create + /// incoming DHCPv6 object from the raw buffer + /// and you know options offsets. Options offsets are + /// specified from perfdhcp command line by the user. + /// + /// \param buf pointer to a buffer of received packet content. + /// \param len size of buffer of packet content. + /// \param transid_offset transaction id offset in a message. + PerfPkt4(const uint8_t* buf, + uint32_t len, + 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 Prepares on-wire format from raw buffer + /// + /// The method copies user buffer to output buffer and + /// extracts transaction id from it based on transaction id + /// offset provided in constructor. + /// + /// \note: Use this method to prepare on-wire DHCPv6 message + /// when you use template packets that require replacement + /// of selected options' contents before sending. + /// + /// \retrun false, id pack operation failed. + bool rawPack(); + + /// \brief Handles limited binary packet parsing for packets with + /// custom offsets of options and transaction id + /// + /// Function handles reception of packets that have non-default values + /// of options or transaction id offsets. Use + /// \ref isc::dhcp::Pkt4::addOption to specify which options to parse. + /// Each option should be of the: isc::perfdhcp::LocalizedOption + /// type with offset value indicated. + /// + /// \return false, if unpack operation failed. + bool rawUnpack(); + + /// \brief Update packet timestamp with current time + /// + /// \throw isc::Unexpected if timestamp update failed + void updateTimestamp(); + +private: + size_t transid_offset_; ///< transaction id offset + +}; + +} // namespace perfdhcp +} // namespace isc + +#endif // __PERF_PKT4_H diff --git a/tests/tools/perfdhcp/pkt_transform.cc b/tests/tools/perfdhcp/pkt_transform.cc new file mode 100644 index 0000000000000000000000000000000000000000..0a6a00c30b1b85308b8e95e2bfb20b00cfcf0a75 --- /dev/null +++ b/tests/tools/perfdhcp/pkt_transform.cc @@ -0,0 +1,220 @@ +// 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 + +#include +#include +#include +#include + +#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 + 1 > 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); + } + + // Seek to the transaction id position in output buffer. + out_buffer.clear(); + out_buffer.skip(transid_offset); + + try { + if (universe == Option::V6) { + out_buffer.writeUint8(transid >> 24 & 0xFF); + } + out_buffer.writeUint8(transid >> 16 & 0xFF); + out_buffer.writeUint8(transid >> 8 & 0xFF); + out_buffer.writeUint8(transid & 0xFF); + + // Buffer pointer is at the end of transaction id. + // We have to seek to the end of buffer so as data don't + // get truncated. + out_buffer.skip(in_buffer.size() - out_buffer.getLength()); + + // 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, out_buffer, options); + } 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); + } + + if (universe == Option::V6) { + transid = ((in_buffer[transid_offset] << 16) + + (in_buffer[transid_offset + 1] << 8) + + (in_buffer[transid_offset + 2])) + & 0xFFFFFF; + } else { + transid = ((in_buffer[transid_offset] << 24) + + (in_buffer[transid_offset +1 ] << 16) + + (in_buffer[transid_offset + 2] << 8) + + (in_buffer[transid_offset + 3])); + } + + try { + PktTransform::unpackOptions(universe, 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, + util::OutputBuffer& out_buffer, + const Option::OptionCollection& options) { + 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 option = + boost::dynamic_pointer_cast(it->second); + 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() << ")"); + } + out_buffer.clear(); + out_buffer.skip(offset); + + // Replace existing option with new value. + option->pack(out_buffer); + } + // Seek to the end of the buffer to make sure its size is correct. + out_buffer.skip(in_buffer.size() - out_buffer.getLength()); + } + catch (const Exception&) { + isc_throw(isc::BadValue, "failed to pack options into buffer."); + } +} + +void +PktTransform::unpackOptions(const Option::Universe universe, + const OptionBuffer& in_buffer, + const Option::OptionCollection& options) { + for (Option::OptionCollection::const_iterator it = options.begin(); + it != options.end(); ++it) { + + boost::shared_ptr option = + boost::dynamic_pointer_cast(it->second); + 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 + 4 > 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 (universe == 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 (universe == 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 + opt_len > in_buffer.size()) { + isc_throw(isc::BadValue, + "failed to unpack option from raw buffer " + "(option truncated)"); + } + + // Seek to actual option data and replace it. + offset += offset_step; + option->setData(in_buffer.begin() + offset, + in_buffer.begin() + offset + opt_len); + } +} + + +} // namespace perfdhcp +} // namespace isc diff --git a/tests/tools/perfdhcp/pkt_transform.h b/tests/tools/perfdhcp/pkt_transform.h new file mode 100644 index 0000000000000000000000000000000000000000..8a2b96565d2354db29d156f04c3a7e4f58e908c9 --- /dev/null +++ b/tests/tools/perfdhcp/pkt_transform.h @@ -0,0 +1,150 @@ +// 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. + +#ifndef __PKT_TRANSFORM_H +#define __PKT_TRANSFORM_H + +#include +#include + +#include "localized_option.h" + +namespace isc { +namespace perfdhcp { + +/// \brief Read and write raw data to DHCP packets. +/// +/// This class provides static functions to read raw +/// data from packet buffer and write raw data to packet +/// buffer. When reading data with unpack() method, the +/// corresponding options objects are updated. +/// When writting to the packet buffer with pack() nethod, +/// 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 +/// server. Offset of specific options are provided from +/// command line by perfdhcp tool user and passed in +/// options collection. +/// This class also provide mechanism to read some data +/// from incoming packet buffer and update options +/// in options collection with these data. +/// It is assumed either in case of writting or reading +/// that all options have to be added to options collection +/// and their offset have to be specified in buffer +/// (\see LocalizedOption). +class PktTransform { +public: + + /// \brief Prepares on-wire format from raw buffer + /// + /// The method copies input buffer and options contents + /// to output buffer. Input buffer must contain whole + /// initial packet data. Parts of this data will be + /// overriden by options data specified in options + /// collection. Such options must have their offsets in + /// a packet specified (\see LocalizedOption to find out how + /// to specify options offset). + /// + /// \note Specified options must fit into size of the + /// initial packet data. Call to this function will fail + /// if option's offset + its size is out of bounds. + /// + /// \param universe universe used, V4 or V6 + /// \param in_buffer input buffer holiding 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, + /// transatcion id will be written to output buffer at this + /// offset + /// \param transid transaction id value + /// \param out_buffer output buffer holding "packed" data + /// + /// \retrun 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 limited binary packet parsing for packets with + /// custom offsets of options and transaction id. + /// + /// Function handles parsing of packet that have non-default values + /// of options or transaction id offsets. Use + /// \ref isc::dhcp::Pkt6::addOption to specify which options to parse. + /// Each option should be of the \ref isc::perfdhcp::LocalizedOption + /// type with offset value indicated. + /// Transaction id offset is specified as separate argument and + /// is used to read transaction id value from buffer. + /// + /// \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 options contents of options in a buffer + /// + /// The method uses localized options collection added to + /// replace parts of initial packet data (e.g. 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, + util::OutputBuffer& out_buffer, + const dhcp::Option::OptionCollection& options); + + /// \brief Reads contents of specified options from buffer + /// + /// The method reads options data from the input buffer + /// and stores read data in options objects. Options + /// must have their offsets in a buffer specified + /// (\see LocalizedOption to find out how to specify + /// option offset). + /// This private method is called by \ref PktTransform::unpack. + /// + /// \note This method iterates through all options in + /// options collection, checks offset of the option + /// in input buffer and reads data from the buffer to + /// update option's buffer. If provided options collection + /// is empty, call to this function 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::Option::Universe universe, + const dhcp::OptionBuffer& in_buffer, + const dhcp::Option::OptionCollection& options); +}; + +} // namespace perfdhcp +} // namespace isc + +#endif // __PKT_TRANSFORM_H diff --git a/tests/tools/perfdhcp/tests/Makefile.am b/tests/tools/perfdhcp/tests/Makefile.am index a16abd6e1b664f5c9acf7b91621988e530941abe..c4f36b5ef977f4b274c55d7a117574899cf7a8de 100644 --- a/tests/tools/perfdhcp/tests/Makefile.am +++ b/tests/tools/perfdhcp/tests/Makefile.am @@ -17,9 +17,12 @@ 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) @@ -27,6 +30,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/asiolink/libasiolink.la run_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libdhcp++.la run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la endif diff --git a/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc new file mode 100644 index 0000000000000000000000000000000000000000..4adeccff649124cc439bce072520b8aadb7d33df --- /dev/null +++ b/tests/tools/perfdhcp/tests/perf_pkt4_unittest.cc @@ -0,0 +1,344 @@ +// 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 "../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 sample data +const uint8_t dummyOp = BOOTREQUEST; +const uint8_t dummyHtype = 6; +const uint8_t dummyHlen = 6; +const uint8_t dummyHops = 13; +const uint32_t dummyTransid = 0x12345678; +const uint16_t dummySecs = 42; +const uint16_t dummyFlags = BOOTP_BROADCAST; + +const IOAddress dummyCiaddr("192.0.2.1"); +const IOAddress dummyYiaddr("1.2.3.4"); +const IOAddress dummySiaddr("192.0.2.255"); +const IOAddress dummyGiaddr("255.255.255.255"); + +// a dummy MAC address +const uint8_t dummyMacAddr[] = {0, 1, 2, 3, 4, 5}; + +// 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 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 + std::vector 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[] = { + 12, 3, 0, 1, 2, // Host name option. + 13, 3, 10, 11, 12, // Boot file size option + 14, 3, 20, 21, 22, // Merit dump file + 53, 1, 1, // DHCP message type. + 128, 3, 30, 31, 32, + 254, 3, 40, 41, 42, + }; + + // Initialize the vector with the header fields defined above. + vector buf(hdr, hdr + 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. + boost::scoped_ptr pkt1(new PerfPkt4(data, sizeof(data), 1)); + EXPECT_EQ(1, pkt1->getTransIdOffset()); + + // Test constructor to be used for outgoing messages. + // Use non-zero offset and specify transaction id. + boost::scoped_ptr pkt2(new PerfPkt4(data, sizeof(data), 10, 0x010203)); + EXPECT_EQ(0x010203, pkt2->getTransid()); + EXPECT_EQ(10, pkt2->getTransIdOffset()); +} + +TEST_F(PerfPkt4Test, RawPack) { + // Create new packet. + std::vector buf = capture(); + boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1)); + + // Initialize options data. + uint8_t buf_hostname[] = { 12, 3, 4, 5, 6 }; + uint8_t buf_boot_filesize[] = { 13, 3, 1, 2, 3 }; + OptionBuffer vec_hostname(buf_hostname + 2, buf_hostname + 5); + OptionBuffer vec_boot_filesize(buf_boot_filesize + 2, buf_boot_filesize + 5); + + // Create options objects. + LocalizedOptionPtr pkt_hostname(new LocalizedOption(Option::V4, + DHO_HOST_NAME, + vec_hostname, + 240)); + LocalizedOptionPtr pkt_boot_filesize(new LocalizedOption(Option::V4, + DHO_BOOT_SIZE, + vec_boot_filesize, + 245)); + + // 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(pkt_output.getData()); + + // Check if options we read from buffer is valid. + EXPECT_EQ(0, memcmp(buf_hostname, out_buf_data + 240, 5)); + EXPECT_EQ(0, memcmp(buf_boot_filesize, out_buf_data + 245, 5)); +} + +TEST_F(PerfPkt4Test, RawUnpack) { + // Create new packet. + std::vector buf = capture(); + boost::scoped_ptr pkt(new PerfPkt4(&buf[0], buf.size(), 0x1, 1)); + + // Create options (existing in the packet) and specify their offsets. + LocalizedOptionPtr opt_merit(new LocalizedOption(Option::V4, + DHO_MERIT_DUMP, + OptionBuffer(), + 250)); + + LocalizedOptionPtr opt_msg_type(new LocalizedOption(Option::V4, + DHO_DHCP_MESSAGE_TYPE, + OptionBuffer(), + 255)); + // 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(pkt->getOption(DHO_MERIT_DUMP)); + opt_msg_type = boost::dynamic_pointer_cast(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 packet. + boost::scoped_ptr pkt1(capture()); + OptionBuffer vec_server_id; + vec_server_id.resize(10); + // Testing invalid offset of the option (greater than packet size) + LocalizedOptionPtr pkt1_serverid(new LocalizedOption(Option::V6, + D6O_SERVERID, + vec_server_id, + 150)); + pkt1->addOption(pkt1_serverid); + // Pack has to fail due to invalid offset. + EXPECT_FALSE(pkt1->rawPack()); + + // Create packet. + boost::scoped_ptr 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, + 85)); + pkt2->addOption(pkt2_serverid); + // Pack must fail due to invalid offset. + EXPECT_FALSE(pkt2->rawPack()); +} + + +TEST_F(PerfPkt4Test, 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 pkt1(captureTruncated()); + OptionBuffer vec_duid; + vec_duid.resize(30); + LocalizedOptionPtr pkt1_duid(new LocalizedOption(Option::V6, + D6O_CLIENTID, + vec_duid, + 4)); + 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(PerfPkt4Test, PackTransactionId) { + uint8_t data[100] = { 0 }; + + // Create dummy packet that is simply filled with zeros. + boost::scoped_ptr pkt1(new PerfPkt4(data, + sizeof(data), + 0x010203, + PerfPkt4::Offset(50))); + + // 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 + (out_buf.getData()); + + // Validate transaction id. + EXPECT_EQ(0, memcmp(out_buf_data + 50, ref_data, 3)); + + // Out of bounds transaction id offset. + boost::scoped_ptr pkt2(new PerfPkt4(data, + sizeof(data), + 0x010202, + PerfPkt4::Offset(100))); + cout << "Testing out of bounds offset. " + "This may produce spurious errors ..." << endl; + EXPECT_FALSE(pkt2->rawPack()); +} + +TEST_F(PerfPkt4Test, 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. + boost::scoped_ptr pkt1(new PerfPkt4(data, + sizeof(data), + PerfPkt4::Offset(50))); + + // 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 pkt2(new PerfPkt4(data, + sizeof(data), + PerfPkt4::Offset(300))); + cout << "Testing out of bounds offset. " + "This may produce spurious errors ..." << endl; + EXPECT_FALSE(pkt2->rawUnpack()); + + } */ + +}