test_control.h 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef __TEST_CONTROL_H
  15. #define __TEST_CONTROL_H
  16. #include <string>
  17. #include <vector>
  18. #include <boost/noncopyable.hpp>
  19. #include <boost/shared_ptr.hpp>
  20. #include <boost/function.hpp>
  21. #include <boost/date_time/posix_time/posix_time.hpp>
  22. #include <dhcp/dhcp6.h>
  23. #include <dhcp/pkt4.h>
  24. #include <dhcp/pkt6.h>
  25. #include "stats_mgr.h"
  26. namespace isc {
  27. namespace perfdhcp {
  28. /// \brief Test Control class.
  29. ///
  30. /// This class is responsible for executing DHCP performance
  31. /// test end to end.
  32. ///
  33. /// Option factory functions are registered using
  34. /// \ref LibDHCP::OptionFactoryRegister. Registered factory functions
  35. /// provide a way to create options of the same type in the same way.
  36. /// When new option instance is needed the corresponding factory
  37. /// function is called to create it. This is done by calling
  38. /// \ref Option::factory with DHCP message type specified as one of
  39. /// parameters. Some of the parameters passed to factory function
  40. /// may be ignored (e.g. option buffer).
  41. class TestControl : public boost::noncopyable {
  42. public:
  43. // Statistics Manager for DHCPv4.
  44. typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
  45. // Pointer to Statistics Manager for DHCPv4;
  46. typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
  47. // Statictics Manager for DHCPv6.
  48. typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
  49. // Pointer to Statistics Manager for DHCPv6.
  50. typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
  51. // Packet exchange type.
  52. typedef StatsMgr<>::ExchangeType ExchangeType;
  53. /// \brief Socket wrapper class.
  54. ///
  55. /// This is wrapper class that holds descriptor of the socket
  56. /// used to run DHCP test. All sockets created with \ref IfaceMgr
  57. /// are closed in the destructor. This ensures that socket is
  58. /// closed when the function that created the socket ends
  59. /// (normally or when exception occurs).
  60. class TestControlSocket {
  61. public:
  62. /// \brief Constructor of socket wrapper class.
  63. ///
  64. /// This constructor uses provided socket descriptor to
  65. /// find the name of the interface where socket has been
  66. /// bound to.
  67. ///
  68. /// \param socket socket descriptor.
  69. /// \throw isc::BadValue if interface for specified
  70. /// socket descriptor does not exist.
  71. TestControlSocket(const int socket);
  72. /// \brief Destriuctor of the socket wrapper class.
  73. ///
  74. /// Destructor closes all open sockets on all interfaces.
  75. /// TODO: close only the socket being wrapped by this class.
  76. ~TestControlSocket();
  77. /// \brief Return name of the interface where socket is bound to.
  78. ///
  79. /// \return name of the interface where socket is bound to.
  80. const std::string& getIface() const { return(iface_); }
  81. /// \brief Return interface index where socket is bound to.
  82. ///
  83. /// \return index fo the interface where sockert is bound to.
  84. int getIfIndex() const { return(ifindex_); }
  85. /// \brief Return address where socket is bound to.
  86. ///
  87. /// \return address where socket is bound to.
  88. const asiolink::IOAddress& getAddress() const { return(addr_); }
  89. private:
  90. /// \brief Private default constructor.
  91. ///
  92. /// Default constructor is private to make sure that valid
  93. /// socket descriptor is passed to create object.
  94. TestControlSocket();
  95. /// \brief Initialize socket data.
  96. ///
  97. /// This method initializes members of the class that Interface
  98. /// Manager holds: interface name, local address.
  99. ///
  100. /// \throw isc::BadValue if interface for specified socket
  101. /// descriptor does not exist.
  102. void initSocketData();
  103. int socket_; ///< Socket descirptor.
  104. std::string iface_; ///< Name of the interface.
  105. int ifindex_; ///< Index of the interface.
  106. asiolink::IOAddress addr_; ///< Address bound.
  107. };
  108. /// \brief Default transaction id generator class.
  109. ///
  110. /// This is default transaction id generator class. The member
  111. /// function is used to generate unique transaction id value.
  112. /// Other generator classes should derive from this one to
  113. /// override the standard generation algorithm (e.g. unit tests
  114. /// override this class wih algorithm that produces more predictable
  115. /// transaction id values).
  116. class TransidGenerator {
  117. public:
  118. /// \brief generate transaction id.
  119. ///
  120. /// \return generated transazction id value.
  121. virtual uint32_t generate() {
  122. return static_cast<uint32_t>(random() % 0x00FFFFFF);
  123. }
  124. };
  125. typedef boost::shared_ptr<TransidGenerator> TransidGeneratorPtr;
  126. /// \brief Length of the Ethernet HW address (MAC) in bytes.
  127. static const uint8_t HW_ETHER_LEN = 6;
  128. /// TestControl is a singleton class. This method returns reference
  129. /// to its sole instance.
  130. ///
  131. /// \return the only existing instance of test control
  132. static TestControl& instance();
  133. /// brief\ Run performance test.
  134. ///
  135. /// Method runs whole performance test. Command line options must
  136. /// be parsed prior to running this function. Othewise function will
  137. /// throw exception.
  138. ///
  139. /// \throw isc::InvalidOperation if command line options are not parsed.
  140. /// \throw isc::Unexpected if internal Test Controler error occured.
  141. void run();
  142. /// \brief Set new transaction id generator.
  143. ///
  144. /// \param generator generator object to be used.
  145. void setTransidGenerator(TransidGeneratorPtr& generator) {
  146. transid_gen_.reset();
  147. transid_gen_ = generator;
  148. }
  149. protected:
  150. // We would really like these methods and members to be private but
  151. // they have to be accessible for unit-testing. Another, possibly better,
  152. // solution is to make this class friend of test class but this is not
  153. // what's followed in other classes.
  154. /// \brief Default constructor.
  155. ///
  156. /// Default constructor is protected as the object can be created
  157. /// only via \ref instance method.
  158. TestControl();
  159. /// \brief Check if test exit condtitions fulfiled.
  160. ///
  161. /// Method checks if test exit conditions are fulfiled.
  162. /// Exit conditions are checked periodically from the
  163. /// main loop. Program should break the main loop when
  164. /// this method returns true. It is calling function
  165. /// responsibility to break main loop gracefully and
  166. /// cleanup after test execution.
  167. ///
  168. /// \return true if any of the exit conditions is fulfiled.
  169. bool checkExitConditions() const;
  170. /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
  171. ///
  172. /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
  173. /// If empty buffer is passed the option buffer will be initialized
  174. /// to length 2 and values will be initialized to zeros. Otherwise
  175. /// function will initialize option buffer with values in passed buffer.
  176. ///
  177. /// \param u universe (V6 or V4).
  178. /// \param type option-type.
  179. /// \param buf option-buffer.
  180. /// \throw if elapsed time buffer size is neither 2 nor 0.
  181. /// \return instance o the option.
  182. static dhcp::OptionPtr
  183. factoryElapsedTime6(dhcp::Option::Universe u,
  184. uint16_t type,
  185. const dhcp::OptionBuffer& buf);
  186. /// \brief Factory function to create generic option.
  187. ///
  188. /// This factory function creates option with specified universe,
  189. /// type and buf. It does not have any additional logic validating
  190. /// the buffer contents, size etc.
  191. ///
  192. /// \param u universe (V6 or V4).
  193. /// \param type option-type.
  194. /// \param buf option-buffer.
  195. /// \return instance o the option.
  196. static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u,
  197. uint16_t type,
  198. const dhcp::OptionBuffer& buf);
  199. /// \brief Factory function to create IA_NA option.
  200. ///
  201. /// This factory function creates DHCPv6 IA_NA option instance.
  202. /// \TODO: add support for IA Address options.
  203. /// \param u universe (V6 or V4).
  204. /// \param type option-type.
  205. /// \param buf option-buffer.
  206. /// \return instance of IA_NA option.
  207. static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u,
  208. uint16_t type,
  209. const dhcp::OptionBuffer& buf);
  210. /// \brief Factory function to create DHCPv6 ORO option.
  211. ///
  212. /// This factory function creates DHCPv6 Option Request Option instance.
  213. /// The created option will contain the following set of requested options:
  214. /// - D6O_NAME_SERVERS
  215. /// - D6O_DOMAIN_SEARCH
  216. ///
  217. /// \param u universe (V6 or V4).
  218. /// \param type option-type.
  219. /// \param buf option-buffer (ignored and should be empty).
  220. /// \return instance of ORO option.
  221. static dhcp::OptionPtr
  222. factoryOptionRequestOption6(dhcp::Option::Universe u,
  223. uint16_t type,
  224. const dhcp::OptionBuffer& buf);
  225. /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance.
  226. ///
  227. /// This factory function creates DHCPv6 RAPID_COMMIT option instance.
  228. /// The buffer passed to this option must be empty because option does
  229. /// not have any payload.
  230. ///
  231. /// \param u universe (V6 or V4).
  232. /// \param type option-type.
  233. /// \param buf option-buffer (ignored and should be empty).
  234. /// \return instance of RAPID_COMMIT option..
  235. static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u,
  236. uint16_t type,
  237. const dhcp::OptionBuffer& buf);
  238. /// \brief Factory function to create DHCPv4 Request List option.
  239. ///
  240. /// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option
  241. /// instance with the following set of requested options:
  242. /// - DHO_SUBNET_MASK,
  243. /// - DHO_BROADCAST_ADDRESS,
  244. /// - DHO_TIME_OFFSET,
  245. /// - DHO_ROUTERS,
  246. /// - DHO_DOMAIN_NAME,
  247. /// - DHO_DOMAIN_NAME_SERVERS,
  248. /// - DHO_HOST_NAME.
  249. ///
  250. /// \param u universe (V6 or V4).
  251. /// \param type option-type.
  252. /// \param buf option-buffer (ignored and should be empty).
  253. /// \return instance o the generic option.
  254. static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
  255. uint16_t type,
  256. const dhcp::OptionBuffer& buf);
  257. /// \brief Generate DUID.
  258. ///
  259. /// Method generates unique DUID. The number of DUIDs it can generate
  260. /// depends on the number of simulated clinets, which is specified
  261. /// from the command line. It uses \ref CommandOptions object to retrieve
  262. /// number of clinets. Since the last six octets of DUID are constructed
  263. /// from the MAC address, this function uses \ref generateMacAddress
  264. /// internally to randomize the DUID.
  265. ///
  266. /// \throw isc::BadValue if \ref generateMacAddress throws.
  267. /// \return vector representing DUID.
  268. std::vector<uint8_t> generateDuid() const;
  269. /// \brief Generate MAC address.
  270. ///
  271. /// This method generates MAC address. The number of unique
  272. /// MAC addresses it can generate is determined by the number
  273. /// simulated DHCP clients specified from command line. It uses
  274. /// \ref CommandOptions object to retrieve number of clients.
  275. /// Based on this the random value is generated and added to
  276. /// the MAC address prefix (default MAC address).
  277. ///
  278. /// \throw isc::BadValue if MAC address prefix (default or specified
  279. /// from the command line) has invalid size (expected 6 octets).
  280. /// \return generated MAC address.
  281. std::vector<uint8_t> generateMacAddress() const;
  282. /// \brief generate transaction id.
  283. ///
  284. /// \return generated transaction id.
  285. uint32_t generateTransid() {
  286. return(transid_gen_->generate());
  287. }
  288. /// \brief Returns number of exchanges to be started.
  289. ///
  290. /// Method returns number of new exchanges to be started as soon
  291. /// as possible to satisfy expected rate. Calculation used here
  292. /// is based on current time, due time calculated with
  293. /// \ref updateSendTime function and expected rate.
  294. ///
  295. /// \return number of exchanges to be started immediatelly.
  296. uint64_t getNextExchangesNum() const;
  297. /// \brief Initializes Statistics Manager.
  298. ///
  299. /// This function initializes Statistics Manager. If there is
  300. /// the one initialized already it is released.
  301. void initializeStatsMgr();
  302. /// \brief Open socket to communicate with DHCP server.
  303. ///
  304. /// Method opens socket and binds it to local address. Function will
  305. /// can use either interface name, local address or server address
  306. /// to create a socket, depending on what is available (specified
  307. /// from the command line). If socket can't be created for any
  308. /// reason, exception is thrown.
  309. /// If destination address is broadcast (for DHCPv4) or multicast
  310. /// (for DHCPv6) than broadcast or multicast option is set on
  311. /// the socket.
  312. ///
  313. /// \param port port to bound socket to.
  314. /// \throw isc::BadValue if socket can't be created for given
  315. /// interface, local address or remote address.
  316. /// \throw isc::InvalidOperation if broadcast option can't be
  317. /// set for the v4 socket or if multicast option cat't be set
  318. /// for the v6 socket.
  319. /// \throw isc::Unexpected if interal unexpected error occured.
  320. /// \return socket descriptor.
  321. int openSocket(uint16_t port = 0) const;
  322. /// \brief Print performance statistics.
  323. ///
  324. /// Method prints performance statistics.
  325. /// \throws isc::InvalidOperation if Statistics Manager was
  326. /// not initialized.
  327. void printStats() const;
  328. /// \brief Receive DHCPv4 packet.
  329. ///
  330. /// Method performs reception of the DHCPv4 packet, updates
  331. /// statistics and responsds to the server if required, e.g.
  332. /// when OFFER packet arrives, this function will initiate
  333. /// REQUEST message to the server.
  334. ///
  335. /// \param socket socket to be used.
  336. /// \param pkt4 object representing DHCPv4 packet received.
  337. /// \throw isc::BadValue if unknown message type received.
  338. /// \throw isc::Unexpected if unexpected error occured.
  339. void receivePacket4(const TestControlSocket& socket,
  340. const dhcp::Pkt4Ptr& pkt4);
  341. /// \brief Receive DHCPv6 packet.
  342. ///
  343. /// Method performs reception of the DHCPv6 packet, updates
  344. /// statistics and responsds to the server if required, e.g.
  345. /// when ADVERTISE packet arrives, this function will initiate
  346. /// REQUEST message to the server.
  347. ///
  348. /// \param socket socket to be used.
  349. /// \param pkt6 object representing DHCPv6 packet received.
  350. /// \throw isc::BadValue if unknown message type received.
  351. /// \throw isc::Unexpected if unexpected error occured.
  352. void receivePacket6(const TestControlSocket& socket,
  353. const dhcp::Pkt6Ptr& pkt6);
  354. /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
  355. ///
  356. /// Method receives DHCPv4 or DHCPv6 packets from the server.
  357. /// This function will call \ref receivePacket4 or
  358. /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
  359. /// has arrived.
  360. ///
  361. /// \param socket socket to be used.
  362. /// \throw::BadValue if unknown message type received.
  363. /// \throw::Unexpected if unexpected error occured.
  364. void receivePackets(const TestControlSocket& socket);
  365. /// \brief Register option factory functions for DHCPv4
  366. ///
  367. /// Method registers option factory functions for DHCPv4.
  368. /// These functions are called to create instances of DHCPv4
  369. /// options. Call \ref Option::factory to invoke factory
  370. /// function for particular option. Don't use this function directly.
  371. /// Use \ref registerOptionFactories instead.
  372. void registerOptionFactories4() const;
  373. /// \brief Register option factory functions for DHCPv6
  374. ///
  375. /// Method registers option factory functions for DHCPv6.
  376. /// These functions are called to create instances of DHCPv6
  377. /// options. Call \ref Option::factory to invoke factory
  378. /// function for particular option. Don't use this function directly.
  379. /// Use \ref registerOptionFactories instead.
  380. void registerOptionFactories6() const;
  381. /// \brief Register option factory functions for DHCPv4 or DHCPv6.
  382. ///
  383. /// Method registers option factory functions for DHCPv4 or DHCPv6,
  384. /// depending in whch mode test is currently running.
  385. void registerOptionFactories() const;
  386. /// \brief Send DHCPv4 DISCOVER message.
  387. ///
  388. /// Method creates and sends DHCPv4 DISCOVER message to the server
  389. /// with the following options:
  390. /// - MESSAGE_TYPE set to DHCPDISCOVER
  391. /// - PARAMETER_REQUEST_LIST with the same list of requested options
  392. /// as described in \ref factoryRequestList.
  393. /// The transaction id and MAC address are randomly generated for
  394. /// the message. Range of unique MAC addresses generated depends
  395. /// on the number of clients specified from the command line.
  396. ///
  397. /// \param socket socket to be used to send the message.
  398. /// \throw isc::Unexpected if failed to create new packet instance.
  399. /// \throw isc::BadValue if MAC address has invalid length.
  400. void sendDiscover4(const TestControlSocket& socket);
  401. /// \brief Sent DHCPv6 REQUEST message.
  402. ///
  403. /// Method creates and sends DHCPv6 REQUEST message to the server
  404. /// with the following options:
  405. /// - D6O_ELAPSED_TIME
  406. /// - D6O_CLIENTID
  407. /// - D6O_SERVERID
  408. /// The elapsed time is calculated based on the duration between
  409. /// sending a SOLICIT and receiving the ADVERTISE packet prior.
  410. /// For this reason both solicit and advertise packet objects have
  411. /// to be passed when calling this function.
  412. ///
  413. /// \param socket socket to be used to send message.
  414. /// \param solicit_pkt6 SOLICIT packet object.
  415. /// \param advertise_pkt6 ADVERTISE packet object.
  416. /// \throw isc::Unexpected if unexpected error occured.
  417. /// \throw isc::InvalidOperation if Statistics Manager has not been
  418. /// initialized.
  419. void sendRequest6(const TestControlSocket& socket,
  420. const dhcp::Pkt6Ptr& solicit_pkt6,
  421. const dhcp::Pkt6Ptr& advertise_pkt6);
  422. /// \brief Send DHCPv6 SOLICIT message.
  423. ///
  424. /// Method creates and sends DHCPv6 SOLICIT message to the server
  425. /// with the following options:
  426. /// - D6O_ELAPSED_TIME,
  427. /// - D6O_RAPID_COMMIT if rapid commit is requested in command line,
  428. /// - D6O_CLIENTID,
  429. /// - D6O_ORO (Option Request Option),
  430. /// - D6O_IA_NA.
  431. ///
  432. /// \param socket socket to be used to send the message.
  433. /// \throw isc::Unexpected if failed to create new packet instance.
  434. void sendSolicit6(const TestControlSocket& socket);
  435. /// \brief Set default DHCPv4 packet parameters.
  436. ///
  437. /// This method sets default parameters on the DHCPv4 packet:
  438. /// - interface name,
  439. /// - local port = 68 (DHCP client port),
  440. /// - remote port = 67 (DHCP server port),
  441. /// - server's address,
  442. /// - GIADDR = local address where socket is bound to,
  443. /// - hops = 1 (pretending that we are a relay)
  444. ///
  445. /// \param socket socket used to send the packet.
  446. /// \param pkt reference to packet to be configured.
  447. void setDefaults4(const TestControlSocket& socket,
  448. const dhcp::Pkt4Ptr& pkt);
  449. /// \brief Set default DHCPv6 packet parameters.
  450. ///
  451. /// This method sets default parameters on the DHCPv6 packet:
  452. /// - interface name,
  453. /// - interface index,
  454. /// - local port,
  455. /// - remote port,
  456. /// - local address,
  457. /// - remote address (server).
  458. ///
  459. /// \param socket socket used to send the packet.
  460. /// \param pkt reference to packet to be configured.
  461. void setDefaults6(const TestControlSocket& socket,
  462. const dhcp::Pkt6Ptr& pkt);
  463. /// \brief Update due time to initiate next chunk of exchanges.
  464. ///
  465. /// Method updates due time to initiate next chunk of exchanges.
  466. /// Function takes current time, last sent packet's time and
  467. /// expected rate in its calculations.
  468. void updateSendDue();
  469. private:
  470. /// \brief Generate transaction id using random function.
  471. ///
  472. /// \return generated transaction id value.
  473. static uint32_t generateTransidRandom();
  474. /// \brief Get number of received packets.
  475. ///
  476. /// Get the number of received packets from the Statistics Manager.
  477. /// Function may throw if Statistics Manager object is not
  478. /// initialized.
  479. /// \param xchg_type packet exchange type.
  480. /// \return number of received packets.
  481. uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const;
  482. /// \brief Get number of sent packets.
  483. ///
  484. /// Get the number of sent packets from the Statistics Manager.
  485. /// Function may throw if Statistics Manager object is not
  486. /// initialized.
  487. /// \param xchg_type packet exchange type.
  488. /// \return number of sent packets.
  489. uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
  490. boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
  491. ///< of exchanges.
  492. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
  493. /// was initiated.
  494. StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
  495. StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
  496. // Pointers to functions.
  497. TransidGeneratorPtr transid_gen_; ///< Transaction id generator.
  498. uint64_t sent_packets_0_;
  499. uint64_t sent_packets_1_;
  500. };
  501. } // namespace perfdhcp
  502. } // namespace isc
  503. #endif // __COMMAND_OPTIONS_H