1 // Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef TEST_CONTROL_H 8 #define TEST_CONTROL_H 9 10 #include <perfdhcp/packet_storage.h> 11 #include <perfdhcp/rate_control.h> 12 #include <perfdhcp/stats_mgr.h> 13 #include <perfdhcp/receiver.h> 14 #include <perfdhcp/command_options.h> 15 #include <perfdhcp/perf_socket.h> 16 #include <perfdhcp/random_number_generator.h> 17 18 #include <dhcp/iface_mgr.h> 19 #include <dhcp/dhcp4.h> 20 #include <dhcp/dhcp6.h> 21 #include <dhcp/pkt4.h> 22 #include <dhcp/pkt6.h> 23 24 #include <boost/noncopyable.hpp> 25 #include <boost/shared_ptr.hpp> 26 #include <boost/date_time/posix_time/posix_time.hpp> 27 28 #include <string> 29 #include <vector> 30 #include <unordered_map> 31 32 namespace isc { 33 namespace perfdhcp { 34 35 /// Default transaction id offset in the packet template. 36 static const size_t DHCPV4_TRANSID_OFFSET = 4; 37 /// Default offset of MAC's last octet in the packet template.. 38 static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35; 39 /// Default elapsed time offset in the packet template. 40 static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8; 41 /// Default server id offset in the packet template. 42 static const size_t DHCPV4_SERVERID_OFFSET = 54; 43 /// Default requested ip offset in the packet template. 44 static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240; 45 /// Default DHCPV6 transaction id offset in t the packet template. 46 static const size_t DHCPV6_TRANSID_OFFSET = 1; 47 /// Default DHCPV6 randomization offset (last octet of DUID) 48 /// in the packet template. 49 static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21; 50 /// Default DHCPV6 elapsed time offset in the packet template. 51 static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84; 52 /// Default DHCPV6 server id offset in the packet template. 53 static const size_t DHCPV6_SERVERID_OFFSET = 22; 54 /// Default DHCPV6 IA_NA offset in the packet template. 55 static const size_t DHCPV6_IA_NA_OFFSET = 40; 56 57 /// \brief Test Control class. 58 /// 59 /// This class is used to run the performance test with 60 /// with \ref TestControl::runWrapped function. This function can be executed 61 /// multiple times if desired because it resets TestControl's internal 62 /// state every time it is executed. Prior to running \ref TestControl::runWrapped, 63 /// one must make sure to parse command line options by calling 64 /// \ref CommandOptions::parse. Failing to do this will result in an exception. 65 /// 66 /// The following major stages of the test are performed by this class: 67 /// - set default transaction id and MAC address generators - the generator 68 /// is an object of \ref TestControl::NumberGenerator type and it provides 69 /// the custom randomization algorithms, 70 /// - print command line arguments, 71 /// - register option factory functions which are used to generate DHCP options 72 /// being sent to a server, 73 /// - create the socket for communication with a server, 74 /// - read packet templates if user specified template files with '-T' command 75 /// line option, 76 /// - set the interrupt handler (invoked when ^C is pressed) which makes 77 /// perfdhcp stop gracefully and print the test results before exiting, 78 /// - executes an external command (if specified '-w' option), e.g. if user 79 /// specified -w ./foo in the command line then program will execute 80 /// "./foo start" at the beginning of the test and "./foo stop" when the test 81 /// ends, 82 /// - initialize the Statistics Manager, 83 /// - executes the main loop: 84 /// - calculate how many packets must be send to satisfy desired rate, 85 /// - receive incoming packets from the server, 86 /// - check the exit conditions - terminate the program if the exit criteria 87 /// are fulfilled, e.g. reached maximum number of packet drops, 88 /// - send the number of packets appropriate to satisfy the desired rate, 89 /// - optionally print intermediate reports, 90 /// - print statistics, e.g. achieved rate, 91 /// - optionally print some diagnostics. 92 /// 93 /// With the '-w' command line option user may specify the external application 94 /// or script to be executed. This is executed twice, first when the test starts 95 /// and second time when the test ends. This external script or application must 96 /// accept 'start' and 'stop' arguments. The first time it is called, it is 97 /// called with the argument 'start' and the second time with the argument 98 /// 'stop'. 99 /// 100 /// The application is executed by calling fork() to fork the current perfdhcp 101 /// process and then call execlp() to replace the current process image with 102 /// the new one. 103 /// 104 /// Option factory functions are registered using 105 /// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions 106 /// provide a way to create options of the same type in the same way. 107 /// When a new option instance is needed, the corresponding factory 108 /// function is called to create it. This is done by calling 109 /// \ref dhcp::Option::factory with DHCP message type specified as one of 110 /// parameters. Some of the parameters passed to factory function 111 /// may be ignored (e.g. option buffer). 112 /// Please note that naming convention for factory functions within this 113 /// class is as follows: 114 /// - factoryABC4 - factory function for DHCPv4 option, 115 /// - factoryDEF6 - factory function for DHCPv6 option, 116 /// - factoryGHI - factory function that can be used to create either 117 /// DHCPv4 or DHCPv6 option. 118 class TestControl : public boost::noncopyable { 119 public: 120 /// \brief Default constructor. 121 TestControl(CommandOptions& options, BasePerfSocket& socket); 122 123 /// Packet template buffer. 124 typedef std::vector<uint8_t> TemplateBuffer; 125 /// Packet template buffers list. 126 typedef std::vector<TemplateBuffer> TemplateBufferCollection; 127 128 /// @brief Delay the exit by a fixed given time to catch up to all exchanges 129 /// that were already started. 130 /// @return true if need to wait, false = ok to exit now 131 bool waitToExit(); 132 133 /// @brief Checks if all expected packets were already received 134 bool haveAllPacketsBeenReceived() const; 135 136 /// \brief Number generator class. 137 /// 138 /// This is default numbers generator class. The member function is 139 /// used to generate uint32_t values. Other generator classes should 140 /// derive from this one to implement generation algorithms 141 /// (e.g. sequential or based on random function). 142 class NumberGenerator { 143 public: 144 145 /// \brief Destructor. ~NumberGenerator()146 virtual ~NumberGenerator() { } 147 148 /// \brief Generate number. 149 /// 150 /// \return Generate number. 151 virtual uint32_t generate() = 0; 152 }; 153 154 /// The default generator pointer. 155 typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr; 156 157 /// \brief Sequential numbers generator class. 158 class SequentialGenerator : public NumberGenerator { 159 public: 160 /// \brief Constructor. 161 /// 162 /// \param range maximum number generated. If 0 is given then 163 /// range defaults to maximum uint32_t value. 164 SequentialGenerator(uint32_t range = 0xFFFFFFFF) : NumberGenerator()165 NumberGenerator(), 166 num_(0), 167 range_(range) { 168 if (range_ == 0) { 169 range_ = 0xFFFFFFFF; 170 } 171 } 172 173 /// \brief Generate number sequentially. 174 /// 175 /// \return generated number. generate()176 virtual uint32_t generate() { 177 uint32_t num = num_; 178 num_ = (num_ + 1) % range_; 179 return (num); 180 } 181 private: 182 uint32_t num_; ///< Current number. 183 uint32_t range_; ///< Number of unique numbers generated. 184 }; 185 186 /// \brief Length of the Ethernet HW address (MAC) in bytes. 187 /// 188 /// \todo Make this variable length as there are cases when HW 189 /// address is longer than this (e.g. 20 bytes). 190 static const uint8_t HW_ETHER_LEN = 6; 191 192 /// \brief Set new transaction id generator. 193 /// 194 /// \param generator generator object to be used. setTransidGenerator(const NumberGeneratorPtr & generator)195 void setTransidGenerator(const NumberGeneratorPtr& generator) { 196 transid_gen_.reset(); 197 transid_gen_ = generator; 198 } 199 200 /// \brief Set new MAC address generator. 201 /// 202 /// Set numbers generator that will be used to generate various 203 /// MAC addresses to simulate number of clients. 204 /// 205 /// \param generator object to be used. setMacAddrGenerator(const NumberGeneratorPtr & generator)206 void setMacAddrGenerator(const NumberGeneratorPtr& generator) { 207 macaddr_gen_.reset(); 208 macaddr_gen_ = generator; 209 } 210 211 /// \brief Removes cached DHCPv6 Reply packets every second. 212 /// 213 /// This function wipes cached Reply packets from the storage. 214 /// The number of packets left in the storage after the call 215 /// to this function should guarantee that the Renew packets 216 /// can be sent at the given rate. Note that the Renew packets 217 /// are generated for the existing leases, represented here as 218 /// replies from the server. 219 /// @todo Instead of cleaning packets periodically we could 220 /// just stop adding new packets when the certain threshold 221 /// has been reached. 222 void cleanCachedPackets(); 223 224 /// \brief Get interrupted flag. interrupted()225 bool interrupted() const { return interrupted_; } 226 227 /// \brief Get stats manager. getStatsMgr()228 StatsMgr& getStatsMgr() { return stats_mgr_; }; 229 230 /// \brief Start receiver. start()231 void start() { receiver_.start(); } 232 233 /// \brief Stop receiver. stop()234 void stop() { receiver_.stop(); } 235 236 /// \brief Run wrapped command. 237 /// 238 /// \param do_stop execute wrapped command with "stop" argument. 239 void runWrapped(bool do_stop = false) const; 240 241 /// \brief Get received server id flag. serverIdReceived()242 bool serverIdReceived() const { return first_packet_serverid_.size() > 0; } 243 244 /// \brief Get received server id. getServerId()245 std::string getServerId() const { return vector2Hex(first_packet_serverid_); } 246 247 /// \brief Send number of packets to initiate new exchanges. 248 /// 249 /// Method initiates the new DHCP exchanges by sending number 250 /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload 251 /// mode was requested sent packets will not be counted in 252 /// the statistics. The responses from the server will be 253 /// received and counted as orphans because corresponding sent 254 /// packets are not included in StatsMgr for match. 255 /// When preload mode is disabled and diagnostics flag 'i' is 256 /// specified then function will be trying to receive late packets 257 /// before new packets are sent to the server. Statistics of 258 /// late received packets is updated accordingly. 259 /// 260 /// \todo do not count responses in preload mode as orphans. 261 /// 262 /// \param packets_num number of packets to be sent. 263 /// \param preload preload mode, packets not included in statistics. 264 /// \throw isc::Unexpected if thrown by packet sending method. 265 /// \throw isc::InvalidOperation if thrown by packet sending method. 266 /// \throw isc::OutOfRange if thrown by packet sending method. 267 void sendPackets(const uint64_t packets_num, 268 const bool preload = false); 269 270 /// \brief Send number of DHCPREQUEST (renew) messages to a server. 271 /// 272 /// \param msg_type A type of the messages to be sent (DHCPREQUEST or 273 /// DHCPRELEASE). 274 /// \param msg_num A number of messages to be sent. 275 /// 276 /// \return A number of messages actually sent. 277 uint64_t sendMultipleMessages4(const uint32_t msg_type, 278 const uint64_t msg_num); 279 280 /// \brief Send number of DHCPv6 Renew or Release messages to the server. 281 /// 282 /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or 283 /// DHCPV6_RELEASE). 284 /// \param msg_num A number of messages to be sent. 285 /// 286 /// \return A number of messages actually sent. 287 uint64_t sendMultipleMessages6(const uint32_t msg_type, 288 const uint64_t msg_num); 289 290 /// \brief Pull packets from receiver and process them. 291 /// 292 /// It runs in a loop until there are no packets in receiver. 293 unsigned int consumeReceivedPackets(); 294 295 /// \brief Print intermediate statistics. 296 /// 297 /// Print brief statistics regarding number of sent packets, 298 /// received packets and dropped packets so far. 299 void printIntermediateStats(); 300 301 /// \brief Print performance statistics. 302 /// 303 /// Method prints performance statistics. 304 /// \throws isc::InvalidOperation if Statistics Manager was 305 /// not initialized. 306 void printStats() const; 307 308 /// \brief Print templates information. 309 /// 310 /// Method prints information about data offsets 311 /// in packet templates and their contents. 312 void printTemplates() const; 313 314 /// \brief Get set of unique replied addresses. getAllUniqueAddrReply()315 std::set<std::string>& getAllUniqueAddrReply() { 316 return unique_reply_address_; 317 } 318 319 /// \brief Get set of unique advertised addresses. getAllUniqueAddrAdvert()320 std::set<std::string>& getAllUniqueAddrAdvert() { 321 return unique_address_; 322 } 323 324 /// \brief Convert binary value to hex string. 325 /// 326 /// \todo Consider moving this function to src/lib/util. 327 /// 328 /// \param b byte to convert. 329 /// \return hex string. 330 static std::string byte2Hex(const uint8_t b); 331 332 /// \brief Convert vector in hexadecimal string. 333 /// 334 /// \todo Consider moving this function to src/lib/util. 335 /// 336 /// \param vec vector to be converted. 337 /// \param separator separator. 338 static std::string vector2Hex(const std::vector<uint8_t>& vec, 339 const std::string& separator = ""); 340 341 /// \brief Initialized at first exit condition with the time perfdhcp 342 /// should exit 343 boost::posix_time::ptime exit_time_; 344 345 // We would really like following methods and members to be private but 346 // they have to be accessible for unit-testing. Another, possibly better, 347 // solution is to make this class friend of test class but this is not 348 // what's followed in other classes. 349 protected: 350 /// Generate uniformly distributed integers in range of [min, max] 351 UniformRandomIntegerGenerator number_generator_; 352 353 /// \brief Creates DHCPREQUEST from a DHCPACK message. 354 /// 355 /// \param ack An instance of the DHCPACK message to be used to 356 /// create a new message. 357 /// 358 /// \return Pointer to the created message. 359 dhcp::Pkt4Ptr createMessageFromAck(const uint16_t msg_type, 360 const dhcp::Pkt4Ptr& ack); 361 362 /// \brief Creates DHCPv6 message from the Reply packet. 363 /// 364 /// This function creates DHCPv6 Renew or Release message using the 365 /// data from the Reply message by copying options from the Reply 366 /// message. 367 /// 368 /// \param msg_type A type of the message to be created. 369 /// \param reply An instance of the Reply packet which contents should 370 /// be used to create an instance of the new message. 371 /// 372 /// \return created Release or Renew message 373 /// \throw isc::BadValue if the msg_type is neither DHCPV6_RENEW nor 374 /// DHCPV6_RELEASE or if the reply is NULL. 375 /// \throw isc::Unexpected if mandatory options are missing in the 376 /// Reply message. 377 dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type, 378 const dhcp::Pkt6Ptr& reply); 379 380 /// \brief Factory function to create DHCPv6 ELAPSED_TIME option. 381 /// 382 /// This factory function creates DHCPv6 ELAPSED_TIME option instance. 383 /// If empty buffer is passed the option buffer will be initialized 384 /// to length 2 and values will be initialized to zeros. Otherwise 385 /// function will initialize option buffer with values in passed buffer. 386 /// 387 /// \param u universe (ignored) 388 /// \param type option-type (ignored). 389 /// \param buf option-buffer containing option content (2 bytes) or 390 /// empty buffer if option content has to be set to default (0) value. 391 /// \throw if elapsed time buffer size is neither 2 nor 0. 392 /// \return instance o the option. 393 static dhcp::OptionPtr 394 factoryElapsedTime6(dhcp::Option::Universe u, 395 uint16_t type, 396 const dhcp::OptionBuffer& buf); 397 398 /// \brief Factory function to create generic option. 399 /// 400 /// This factory function creates option with specified universe, 401 /// type and buf. It does not have any additional logic validating 402 /// the buffer contents, size etc. 403 /// 404 /// \param u universe (V6 or V4). 405 /// \param type option-type (ignored). 406 /// \param buf option-buffer. 407 /// \return instance o the option. 408 static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, 409 uint16_t type, 410 const dhcp::OptionBuffer& buf); 411 412 /// \brief Factory function to create IA_NA option. 413 /// 414 /// This factory function creates DHCPv6 IA_NA option instance. 415 /// 416 /// \todo add support for IA Address options. 417 /// 418 /// \param u universe (ignored). 419 /// \param type option-type (ignored). 420 /// \param buf option-buffer carrying IANA suboptions. 421 /// \return instance of IA_NA option. 422 static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, 423 uint16_t type, 424 const dhcp::OptionBuffer& buf); 425 426 /// \brief Factory function to create IA_PD option. 427 /// 428 /// this factory function creates DHCPv6 IA_PD option instance. 429 /// 430 /// \param u universe (ignored). 431 /// \param type option-type (ignored). 432 /// \param buf option-buffer carrying sub-options. 433 static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u, 434 uint16_t type, 435 const dhcp::OptionBuffer& buf); 436 437 /// \brief Factory function to create DHCPv6 ORO option. 438 /// 439 /// This factory function creates DHCPv6 Option Request Option instance. 440 /// The created option will contain the following set of requested options: 441 /// - D6O_NAME_SERVERS 442 /// - D6O_DOMAIN_SEARCH 443 /// 444 /// \param u universe (ignored). 445 /// \param type option-type (ignored). 446 /// \param buf option-buffer (ignored). 447 /// \return instance of ORO option. 448 static dhcp::OptionPtr 449 factoryOptionRequestOption6(dhcp::Option::Universe u, 450 uint16_t type, 451 const dhcp::OptionBuffer& buf); 452 453 /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance. 454 /// 455 /// This factory function creates DHCPv6 RAPID_COMMIT option instance. 456 /// The buffer passed to this option must be empty because option does 457 /// not have any payload. 458 /// 459 /// \param u universe (ignored). 460 /// \param type option-type (ignored). 461 /// \param buf option-buffer (ignored). 462 /// \return instance of RAPID_COMMIT option.. 463 static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, 464 uint16_t type, 465 const dhcp::OptionBuffer& buf); 466 467 468 /// \brief Factory function to create DHCPv4 Request List option. 469 /// 470 /// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option 471 /// instance with the following set of requested options: 472 /// - DHO_SUBNET_MASK, 473 /// - DHO_BROADCAST_ADDRESS, 474 /// - DHO_TIME_OFFSET, 475 /// - DHO_ROUTERS, 476 /// - DHO_DOMAIN_NAME, 477 /// - DHO_DOMAIN_NAME_SERVERS, 478 /// - DHO_HOST_NAME. 479 /// 480 /// \param u universe (ignored). 481 /// \param type option-type (ignored). 482 /// \param buf option-buffer (ignored). 483 /// \return instance o the generic option. 484 static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, 485 uint16_t type, 486 const dhcp::OptionBuffer& buf); 487 488 /// \brief Generate DHCPv4 client identifier from HW address. 489 /// 490 /// This method generates DHCPv4 client identifier option from a 491 /// HW address. 492 /// 493 /// \param hwaddr HW address. 494 /// 495 /// \return Pointer to an instance of the generated option. 496 dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr& hwaddr) const; 497 498 /// \brief Generate DUID. 499 /// 500 /// Method generates unique DUID. The number of DUIDs it can generate 501 /// depends on the number of simulated clients, which is specified 502 /// from the command line. It uses \ref CommandOptions object to retrieve 503 /// number of clients. Since the last six octets of DUID are constructed 504 /// from the MAC address, this function uses \ref generateMacAddress 505 /// internally to randomize the DUID. 506 /// 507 /// \todo add support for other types of DUID. 508 /// 509 /// \param [out] randomized number of bytes randomized (initial value 510 /// is ignored). 511 /// \throw isc::BadValue if \ref generateMacAddress throws. 512 /// \return vector representing DUID. 513 std::vector<uint8_t> generateDuid(uint8_t& randomized); 514 515 /// \brief Generate MAC address. 516 /// 517 /// This method generates MAC address. The number of unique 518 /// MAC addresses it can generate is determined by the number 519 /// simulated DHCP clients specified from command line. It uses 520 /// \ref CommandOptions object to retrieve number of clients. 521 /// Based on this the random value is generated and added to 522 /// the MAC address template (default MAC address). 523 /// 524 /// \param [out] randomized number of bytes randomized (initial 525 /// value is ignored). 526 /// \throw isc::BadValue if MAC address template (default or specified 527 /// from the command line) has invalid size (expected 6 octets). 528 /// \return generated MAC address. 529 std::vector<uint8_t> generateMacAddress(uint8_t& randomized); 530 531 /// \brief generate transaction id. 532 /// 533 /// Generate transaction id value (32-bit for DHCPv4, 534 /// 24-bit for DHCPv6). 535 /// 536 /// \return generated transaction id. generateTransid()537 uint32_t generateTransid() { 538 return (transid_gen_->generate()); 539 } 540 541 /// \brief Return template buffer. 542 /// 543 /// Method returns template buffer at specified index. 544 /// 545 /// \param idx index of template buffer. 546 /// \throw isc::OutOfRange if buffer index out of bounds. 547 /// \return reference to template buffer. 548 TemplateBuffer getTemplateBuffer(const size_t idx) const; 549 550 /// \brief Reads packet templates from files. 551 /// 552 /// Method iterates through all specified template files, reads 553 /// their content and stores it in class internal buffers. Template 554 /// file names are specified from the command line with -T option. 555 /// 556 /// \throw isc::BadValue if any of the template files does not exist, 557 /// contains characters other than hexadecimal digits or spaces. 558 /// \throw OutOfRange if any of the template files is empty or has 559 /// odd number of hexadecimal digits. 560 void initPacketTemplates(); 561 562 /// \brief Print rate statistics. 563 /// 564 /// Method print packet exchange rate statistics. 565 void printRate() const; 566 567 /// \brief Process received DHCPv4 packet. 568 /// 569 /// Method performs processing of the received DHCPv4 packet, 570 /// updates statistics and responds to the server if required, 571 /// e.g. when OFFER packet arrives, this function will initiate 572 /// REQUEST message to the server. 573 /// 574 /// \warning this method does not check if provided socket is 575 /// valid (specifically if v4 socket for received v4 packet). 576 /// 577 /// \param [in] pkt4 object representing DHCPv4 packet received. 578 /// \throw isc::BadValue if unknown message type received. 579 /// \throw isc::Unexpected if unexpected error occurred. 580 void processReceivedPacket4(const dhcp::Pkt4Ptr& pkt4); 581 582 /// \brief Process IA in received DHCPv6 packet. 583 /// 584 /// Process IA in received message to check if it contain proper 585 /// address and/or prefix 586 /// 587 /// \param [in] pkt6 object representing DHCPv6 packet received. 588 /// \return true if the message include correct IA, false otherwise. 589 bool validateIA(const dhcp::Pkt6Ptr& pkt6); 590 591 /// \brief Process received v6 addresses uniqueness. 592 /// 593 /// Generate list of addresses and check for uniqueness. 594 /// 595 /// \param pkt6 object representing received DHCPv6 packet 596 /// \param xchg_type ExchangeType enum value. 597 void address6Uniqueness(const dhcp::Pkt6Ptr& pkt6, ExchangeType xchg_type); 598 599 /// \brief Process received v4 addresses uniqueness. 600 /// 601 /// Generate list of addresses and check for uniqueness. 602 /// 603 /// \param pkt4 object representing received DHCPv4 packet 604 /// \param xchg_type ExchangeType enum value. 605 void address4Uniqueness(const dhcp::Pkt4Ptr& pkt4, ExchangeType xchg_type); 606 607 /// \brief add unique address to already assigned list. 608 /// 609 /// Add address and/or prefix to unique set if it's not already there, 610 /// otherwise increment the number of non unique addresses. 611 /// 612 /// \param current set of addresses that should be added to unique list 613 /// \param xchg_type ExchangeType enum value. addUniqeAddr(const std::set<std::string> & current,ExchangeType xchg_type)614 void addUniqeAddr(const std::set<std::string>& current, ExchangeType xchg_type) { 615 switch(xchg_type) { 616 case ExchangeType::SA: { 617 for (auto current_it = current.begin(); 618 current_it != current.end(); ++current_it) { 619 // addresses should be unique cross packets 620 auto ret = unique_address_.emplace(*current_it); 621 if (!ret.second) { 622 stats_mgr_.updateNonUniqueAddrNum(ExchangeType::SA); 623 } 624 } 625 break; 626 } 627 case ExchangeType::RR: { 628 for (auto current_it = current.begin(); 629 current_it != current.end(); ++current_it) { 630 // addresses should be unique cross packets 631 auto ret = unique_reply_address_.emplace(*current_it); 632 if (!ret.second) { 633 stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RR); 634 } 635 } 636 break; 637 } 638 case ExchangeType::RLA: 639 case ExchangeType::RL: { 640 removeUniqueAddr(current); 641 break; 642 } 643 case ExchangeType::DO: { 644 for (auto current_it = current.begin(); 645 current_it != current.end(); ++current_it) { 646 // addresses should be unique cross packets 647 auto ret = unique_address_.emplace(*current_it); 648 if (!ret.second) { 649 stats_mgr_.updateNonUniqueAddrNum(ExchangeType::DO); 650 } 651 } 652 break; 653 } 654 case ExchangeType::RA: { 655 for (auto current_it = current.begin(); 656 current_it != current.end(); ++current_it) { 657 // addresses should be unique cross packets 658 auto ret = unique_reply_address_.emplace(*current_it); 659 if (!ret.second) { 660 stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RA); 661 } 662 } 663 break; 664 } 665 case ExchangeType::RNA: 666 case ExchangeType::RN: 667 default: 668 break; 669 } 670 } 671 672 /// \brief remove unique address from list. 673 /// 674 /// If address is released we should remove it from both 675 /// advertised (offered) and assigned sets. 676 /// 677 /// \param addr holding value of unique address. removeUniqueAddr(const std::set<std::string> & addr)678 void removeUniqueAddr(const std::set<std::string>& addr) { 679 for (auto addr_it = addr.begin(); addr_it != addr.end(); ++addr_it) { 680 auto it = unique_address_.find(*addr_it); 681 if (it != unique_address_.end()) { 682 unique_address_.erase(it); 683 } 684 685 auto it2 = unique_reply_address_.find(*addr_it); 686 if (it2 != unique_reply_address_.end()) { 687 unique_reply_address_.erase(it2); 688 } 689 } 690 } 691 692 /// \brief Process received DHCPv6 packet. 693 /// 694 /// Method performs processing of the received DHCPv6 packet, 695 /// updates statistics and responds to the server if required, 696 /// e.g. when ADVERTISE packet arrives, this function will initiate 697 /// REQUEST message to the server. 698 /// 699 /// \param [in] pkt6 object representing DHCPv6 packet received. 700 /// \throw isc::BadValue if unknown message type received. 701 /// \throw isc::Unexpected if unexpected error occurred. 702 void processReceivedPacket6(const dhcp::Pkt6Ptr& pkt6); 703 704 /// \brief Register option factory functions for DHCPv4. 705 /// 706 /// Method registers option factory functions for DHCPv4. 707 /// These functions are called to create instances of DHCPv4 708 /// options. Call \ref dhcp::Option::factory to invoke factory 709 /// function for particular option. Don't use this function directly. 710 /// Use \ref registerOptionFactories instead. 711 void registerOptionFactories4() const; 712 713 /// \brief Register option factory functions for DHCPv6. 714 /// 715 /// Method registers option factory functions for DHCPv6. 716 /// These functions are called to create instances of DHCPv6 717 /// options. Call \ref dhcp::Option::factory to invoke factory 718 /// function for particular option. Don't use this function directly. 719 /// Use \ref registerOptionFactories instead. 720 void registerOptionFactories6() const; 721 722 /// \brief Register option factory functions for DHCPv4 or DHCPv6. 723 /// 724 /// Method registers option factory functions for DHCPv4 or DHCPv6, 725 /// depending in which mode test is currently running. 726 void registerOptionFactories() const; 727 728 /// \brief Resets internal state of the object. 729 /// 730 /// Method resets internal state of the object. It has to be 731 /// called before new test is started. 732 void reset(); 733 734 /// \brief Save the first DHCPv4 sent packet of the specified type. 735 /// 736 /// This method saves first packet of the specified being sent 737 /// to the server if user requested diagnostics flag 'T'. In 738 /// such case program has to print contents of selected packets 739 /// being sent to the server. It collects first packets of each 740 /// type and keeps them around until test finishes. Then they 741 /// are printed to the user. If packet of specified type has 742 /// been already stored this function performs no operation. 743 /// This function does not perform sanity check if packet 744 /// pointer is valid. Make sure it is before calling it. 745 /// 746 /// \param pkt packet to be stored. 747 inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt); 748 749 /// \brief Save the first DHCPv6 sent packet of the specified type. 750 /// 751 /// This method saves first packet of the specified being sent 752 /// to the server if user requested diagnostics flag 'T'. In 753 /// such case program has to print contents of selected packets 754 /// being sent to the server. It collects first packets of each 755 /// type and keeps them around until test finishes. Then they 756 /// are printed to the user. If packet of specified type has 757 /// been already stored this function performs no operation. 758 /// This function does not perform sanity check if packet 759 /// pointer is valid. Make sure it is before calling it. 760 /// 761 /// \param pkt packet to be stored. 762 inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt); 763 764 /// \brief Send DHCPv4 DISCOVER message. 765 /// 766 /// Method creates and sends DHCPv4 DISCOVER message to the server 767 /// with the following options: 768 /// - MESSAGE_TYPE set to DHCPDISCOVER 769 /// - PARAMETER_REQUEST_LIST with the same list of requested options 770 /// as described in \ref factoryRequestList4. 771 /// The transaction id and MAC address are randomly generated for 772 /// the message. Range of unique MAC addresses generated depends 773 /// on the number of clients specified from the command line. 774 /// Copy of sent packet is stored in the stats_mgr_ object to 775 /// update statistics. 776 /// 777 /// \param preload preload mode, packets not included in statistics. 778 /// 779 /// \throw isc::Unexpected if failed to create new packet instance. 780 /// \throw isc::BadValue if MAC address has invalid length. 781 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 782 void sendDiscover4(const bool preload = false); 783 784 /// \brief Send DHCPv4 DISCOVER message from template. 785 /// 786 /// Method sends DHCPv4 DISCOVER message from template. The 787 /// template data is expected to be in binary format. Provided 788 /// buffer is copied and parts of it are replaced with actual 789 /// data (e.g. MAC address, transaction id etc.). 790 /// Copy of sent packet is stored in the stats_mgr_ object to 791 /// update statistics. 792 /// 793 /// \param template_buf buffer holding template packet. 794 /// \param preload preload mode, packets not included in statistics. 795 /// 796 /// \throw isc::OutOfRange if randomization offset is out of bounds. 797 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 798 void sendDiscover4(const std::vector<uint8_t>& template_buf, 799 const bool preload = false); 800 801 /// \brief Send DHCPv4 renew (DHCPREQUEST). 802 /// 803 /// \param msg_type A type of the message to be sent (DHCPREQUEST or 804 /// DHCPRELEASE). 805 /// 806 /// \return true if the message has been sent, false otherwise. 807 bool sendMessageFromAck(const uint16_t msg_type); 808 809 /// \brief Send DHCPv6 Renew or Release message. 810 /// 811 /// This method will select an existing lease from the Reply packet cache 812 /// If there is no lease that can be renewed or released this method will 813 /// return false. 814 /// 815 /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or 816 /// DHCPV6_RELEASE). 817 /// 818 /// \return true if the message has been sent, false otherwise. 819 bool sendMessageFromReply(const uint16_t msg_type); 820 821 /// \brief Send DHCPv4 REQUEST message. 822 /// 823 /// Method creates and sends DHCPv4 REQUEST message to the server. 824 /// Copy of sent packet is stored in the stats_mgr_ object to 825 /// update statistics. 826 /// 827 /// \param discover_pkt4 DISCOVER packet sent. 828 /// \param offer_pkt4 OFFER packet object. 829 /// 830 /// \throw isc::Unexpected if unexpected error occurred. 831 /// \throw isc::InvalidOperation if Statistics Manager has not been 832 /// initialized. 833 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 834 void sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4, 835 const dhcp::Pkt4Ptr& offer_pkt4); 836 837 /// \brief Send DHCPv4 REQUEST message from template. 838 /// 839 /// Method sends DHCPv4 REQUEST message from template. 840 /// Copy of sent packet is stored in the stats_mgr_ object to 841 /// update statistics. 842 /// 843 /// \param template_buf buffer holding template packet. 844 /// \param discover_pkt4 DISCOVER packet sent. 845 /// \param offer_pkt4 OFFER packet received. 846 /// 847 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 848 void sendRequest4(const std::vector<uint8_t>& template_buf, 849 const dhcp::Pkt4Ptr& discover_pkt4, 850 const dhcp::Pkt4Ptr& offer_pkt4); 851 852 /// \brief Send DHCPv6 REQUEST message. 853 /// 854 /// Method creates and sends DHCPv6 REQUEST message to the server 855 /// with the following options: 856 /// - D6O_ELAPSED_TIME 857 /// - D6O_CLIENTID 858 /// - D6O_SERVERID 859 /// Copy of sent packet is stored in the stats_mgr_ object to 860 /// update statistics. 861 /// 862 /// \param advertise_pkt6 ADVERTISE packet object. 863 /// \throw isc::Unexpected if unexpected error occurred. 864 /// \throw isc::InvalidOperation if Statistics Manager has not been 865 /// initialized. 866 /// 867 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 868 void sendRequest6(const dhcp::Pkt6Ptr& advertise_pkt6); 869 870 /// \brief Send DHCPv6 REQUEST message from template. 871 /// 872 /// Method sends DHCPv6 REQUEST message from template. 873 /// Copy of sent packet is stored in the stats_mgr_ object to 874 /// update statistics. 875 /// 876 /// \param template_buf packet template buffer. 877 /// \param advertise_pkt6 ADVERTISE packet object. 878 /// 879 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 880 void sendRequest6(const std::vector<uint8_t>& template_buf, 881 const dhcp::Pkt6Ptr& advertise_pkt6); 882 883 /// \brief Send DHCPv6 SOLICIT message. 884 /// 885 /// Method creates and sends DHCPv6 SOLICIT message to the server 886 /// with the following options: 887 /// - D6O_ELAPSED_TIME, 888 /// - D6O_RAPID_COMMIT if rapid commit is requested in command line, 889 /// - D6O_CLIENTID, 890 /// - D6O_ORO (Option Request Option), 891 /// - D6O_IA_NA. 892 /// Copy of sent packet is stored in the stats_mgr_ object to 893 /// update statistics. 894 /// 895 /// \param preload mode, packets not included in statistics. 896 /// 897 /// \throw isc::Unexpected if failed to create new packet instance. 898 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 899 void sendSolicit6(const bool preload = false); 900 901 /// \brief Send DHCPv6 SOLICIT message from template. 902 /// 903 /// Method sends DHCPv6 SOLICIT message from template. 904 /// Copy of sent packet is stored in the stats_mgr_ object to 905 /// update statistics. 906 /// 907 /// \param template_buf packet template buffer. 908 /// \param preload mode, packets not included in statistics. 909 /// 910 /// \throw isc::dhcp::SocketWriteError if failed to send the packet. 911 void sendSolicit6(const std::vector<uint8_t>& template_buf, 912 const bool preload = false); 913 914 /// \brief Set default DHCPv4 packet parameters. 915 /// 916 /// This method sets default parameters on the DHCPv4 packet: 917 /// - interface name, 918 /// - local port = 68 (DHCP client port), 919 /// - remote port = 67 (DHCP server port), 920 /// - server's address, 921 /// - GIADDR = local address where socket is bound to, 922 /// - hops = 1 (pretending that we are a relay) 923 /// 924 /// \param pkt reference to packet to be configured. 925 void setDefaults4(const dhcp::Pkt4Ptr& pkt); 926 927 /// \brief Set default DHCPv6 packet parameters. 928 /// 929 /// This method sets default parameters on the DHCPv6 packet: 930 /// - interface name, 931 /// - interface index, 932 /// - local port, 933 /// - remote port, 934 /// - local address, 935 /// - remote address (server). 936 /// 937 /// \param pkt reference to packet to be configured. 938 void setDefaults6(const dhcp::Pkt6Ptr& pkt); 939 940 /// @brief Inserts extra options specified by user. 941 /// 942 /// Note: addExtraOpts for v4 and v6 could easily be turned into a template. 943 /// However, this would require putting code here that uses CommandOptions, 944 /// and that would create dependency between test_control.h and 945 /// command_options.h. 946 /// 947 /// @param pkt4 options will be added here. 948 void addExtraOpts(const dhcp::Pkt4Ptr& pkt4); 949 950 /// @brief Inserts extra options specified by user. 951 /// 952 /// Note: addExtraOpts for v4 and v6 could easily be turned into a template. 953 /// However, this would require putting code here that uses CommandOptions, 954 /// and that would create dependency between test_control.h and 955 /// command_options.h. 956 /// 957 /// @param pkt6 options will be added here. 958 void addExtraOpts(const dhcp::Pkt6Ptr& pkt6); 959 960 /// \brief Copies IA_NA or IA_PD option from one packet to another. 961 /// 962 /// This function checks the lease-type specified in the command line 963 /// with option -e<lease-type>. If 'address-only' value has been specified 964 /// this function expects that IA_NA option is present in the packet 965 /// encapsulated by pkt_from object. If 'prefix-only' value has been 966 /// specified, this function expects that IA_PD option is present in the 967 /// packet encapsulated by pkt_to object. 968 /// 969 /// \param [in] pkt_from A packet from which options should be copied. 970 /// \param [out] pkt_to A packet to which options should be copied. 971 /// 972 /// \throw isc::NotFound if a required option is not found in the 973 /// packet from which options should be copied. 974 /// \throw isc::BadValue if any of the specified pointers to packets 975 /// is NULL. 976 void copyIaOptions(const dhcp::Pkt6Ptr& pkt_from, dhcp::Pkt6Ptr& pkt_to); 977 978 /// \brief Calculate elapsed time between two packets. 979 /// 980 /// This function calculates the time elapsed between two packets. If 981 /// the timestamp of the pkt2 is greater than timestamp of the pkt1, 982 /// the positive value is returned. If the pkt2 timestamp is equal or 983 /// less than pkt1 timestamp, 0 is returned. 984 /// 985 /// \tparam T Pkt4Ptr or Pkt6Ptr class. 986 /// \param pkt1 first packet. 987 /// \param pkt2 second packet. 988 /// \throw InvalidOperation if packet timestamps are invalid. 989 /// \return elapsed time in milliseconds between pkt1 and pkt2. 990 template<class T> 991 uint32_t getElapsedTime(const T& pkt1, const T& pkt2); 992 993 /// \brief Return elapsed time offset in a packet. 994 /// 995 /// \return elapsed time offset in packet. 996 int getElapsedTimeOffset() const; 997 998 /// \brief Return randomization offset in a packet. 999 /// 1000 /// \return randomization offset in packet. 1001 int getRandomOffset(const int arg_idx) const; 1002 1003 /// \brief Return requested ip offset in a packet. 1004 /// 1005 /// \return randomization offset in a packet. 1006 int getRequestedIpOffset() const; 1007 1008 /// \brief Return server id offset in a packet. 1009 /// 1010 /// \return server id offset in packet. 1011 int getServerIdOffset() const; 1012 1013 /// \brief Return transaction id offset in a packet. 1014 /// 1015 /// \param arg_idx command line argument index to be used. 1016 /// If multiple -X parameters specified it points to the 1017 /// one to be used. 1018 /// \return transaction id offset in packet. 1019 int getTransactionIdOffset(const int arg_idx) const; 1020 1021 /// \brief Handle child signal. 1022 /// 1023 /// Function handles child signal by waiting for 1024 /// the process to complete. 1025 /// 1026 /// \param sig signal (ignored). 1027 static void handleChild(int sig); 1028 1029 /// \brief Handle interrupt signal. 1030 /// 1031 /// Function sets flag indicating that program has been 1032 /// interrupted. 1033 /// 1034 /// \param sig signal (ignored). 1035 static void handleInterrupt(int sig); 1036 1037 /// \brief Print main diagnostics data. 1038 /// 1039 /// Method prints main diagnostics data. 1040 void printDiagnostics() const; 1041 1042 /// \brief Print template information. 1043 /// 1044 /// \param packet_type packet type. 1045 void printTemplate(const uint8_t packet_type) const; 1046 1047 /// \brief Read DHCP message template from file. 1048 /// 1049 /// Method reads DHCP message template from file and 1050 /// converts it to binary format. Read data is appended 1051 /// to template_buffers_ vector. 1052 /// 1053 /// \param file_name name of the packet template file. 1054 /// \throw isc::OutOfRange if file is empty or has odd number 1055 /// of hexadecimal digits. 1056 /// \throw isc::BadValue if file contains characters other than 1057 /// spaces or hexadecimal digits. 1058 void readPacketTemplate(const std::string& file_name); 1059 1060 /// \brief Keep addresses and prefixes from advertise msg for uniqueness checks. 1061 std::set<std::string> unique_address_; 1062 1063 /// \brief Keep addresses and prefixes from reply msg for uniqueness checks. 1064 std::set<std::string> unique_reply_address_; 1065 1066 /// \brief Socket used for DHCP traffic. 1067 BasePerfSocket &socket_; 1068 1069 /// \brief Receiver used to receive DHCP traffic. 1070 Receiver receiver_; 1071 1072 /// \brief Last intermediate report time. 1073 boost::posix_time::ptime last_report_; 1074 1075 /// \brief Statistics Manager. 1076 StatsMgr stats_mgr_; 1077 1078 /// \brief Storage for DHCPACK messages. 1079 PacketStorage<dhcp::Pkt4> ack_storage_; 1080 1081 /// \brief Storage for reply messages. 1082 PacketStorage<dhcp::Pkt6> reply_storage_; 1083 1084 /// \brief Transaction id generator. 1085 NumberGeneratorPtr transid_gen_; 1086 1087 /// \brief Numbers generator for MAC address. 1088 NumberGeneratorPtr macaddr_gen_; 1089 1090 /// \brief Buffer holding server id received in first packet 1091 dhcp::OptionBuffer first_packet_serverid_; 1092 1093 /// \brief Packet template buffers. 1094 TemplateBufferCollection template_buffers_; 1095 1096 /// First packets send. They are used at the end of the test 1097 /// to print packet templates when diagnostics flag T is specified. 1098 1099 /// \brief Template for v4. 1100 std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_; 1101 1102 /// \brief Template for v6. 1103 std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_; 1104 1105 /// \brief Program interrupted flag. 1106 static bool interrupted_; 1107 1108 /// \brief Command options. 1109 CommandOptions& options_; 1110 }; 1111 1112 } // namespace perfdhcp 1113 } // namespace isc 1114 1115 #endif // TEST_CONTROL_H 1116