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