test_control.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  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/date_time/posix_time/posix_time.hpp>
  21. #include <dhcp/dhcp6.h>
  22. #include <dhcp/pkt4.h>
  23. #include <dhcp/pkt6.h>
  24. #include "stats_mgr.h"
  25. namespace isc {
  26. namespace perfdhcp {
  27. /// \brief Test Control class.
  28. ///
  29. /// This class is responsible for executing DHCP performance
  30. /// test end to end.
  31. ///
  32. /// Option factory functions are registered using
  33. /// \ref LibDHCP::OptionFactoryRegister. Registered factory functions
  34. /// provide a way to create options of the same type in the same way.
  35. /// When new option instance is needed the corresponding factory
  36. /// function is called to create it. This is done by calling
  37. /// \ref Option::factory with DHCP message type specified as one of
  38. /// parameters. Some of the parameters passed to factory function
  39. /// may be ignored (e.g. option buffer).
  40. class TestControl : public boost::noncopyable {
  41. public:
  42. // Statistics Manager for DHCPv4.
  43. typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
  44. // Pointer to Statistics Manager for DHCPv4;
  45. typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
  46. // Statictics Manager for DHCPv6.
  47. typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
  48. // Pointer to Statistics Manager for DHCPv6.
  49. typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
  50. /// \brief Socket wrapper class.
  51. ///
  52. /// This is wrapper class that holds descriptor of the socket
  53. /// used to run DHCP test. All sockets created with \ref IfaceMgr
  54. /// are closed in the destructor. This ensures that socket is
  55. /// closed when the function that created the socket ends
  56. /// (normally or when exception occurs).
  57. class TestControlSocket {
  58. public:
  59. /// \brief Constructor of socket wrapper class.
  60. ///
  61. /// This constructor uses provided socket descriptor to
  62. /// find the name of the interface where socket has been
  63. /// bound to.
  64. ///
  65. /// \param socket socket descriptor.
  66. /// \throw isc::BadValue if interface for specified
  67. /// socket descriptor does not exist.
  68. TestControlSocket(const int socket);
  69. /// \brief Destriuctor of the socket wrapper class.
  70. ///
  71. /// Destructor closes all open sockets on all interfaces.
  72. /// TODO: close only the socket being wrapped by this class.
  73. ~TestControlSocket();
  74. /// \brief Return name of the interface where socket is bound to.
  75. ///
  76. /// \return name of the interface where socket is bound to.
  77. const std::string& getIface() const { return(iface_); }
  78. /// \brief Return interface index where socket is bound to.
  79. ///
  80. /// \return index fo the interface where sockert is bound to.
  81. int getIfIndex() const { return(ifindex_); }
  82. /// \brief Return address where socket is bound to.
  83. ///
  84. /// \return address where socket is bound to.
  85. const asiolink::IOAddress& getAddress() const { return(addr_); }
  86. private:
  87. /// \brief Private default constructor.
  88. ///
  89. /// Default constructor is private to make sure that valid
  90. /// socket descriptor is passed to create object.
  91. TestControlSocket();
  92. /// \brief Initialize socket data.
  93. ///
  94. /// This method initializes members of the class that Interface
  95. /// Manager holds: interface name, local address.
  96. ///
  97. /// \throw isc::BadValue if interface for specified socket
  98. /// descriptor does not exist.
  99. void initSocketData();
  100. int socket_; ///< Socket descirptor.
  101. std::string iface_; ///< Name of the interface.
  102. int ifindex_; ///< Index of the interface.
  103. asiolink::IOAddress addr_; ///< Address bound.
  104. };
  105. /// \brief Length of the Ethernet HW address (MAC) in bytes.
  106. static const uint8_t HW_ETHER_LEN = 6;
  107. /// TestControl is a singleton class. This method returns reference
  108. /// to its sole instance.
  109. ///
  110. /// \return the only existing instance of test control
  111. static TestControl& instance();
  112. /// Run performance test.
  113. ///
  114. /// Method runs whole performance test. Command line options must
  115. /// be parsed prior to running this function. Othewise function will
  116. /// throw exception.
  117. ///
  118. /// \throw isc::InvalidOperation if command line options are not parsed.
  119. /// \throw isc::Unexpected if internal Test Controler error occured.
  120. void run();
  121. protected:
  122. // We would really like these methods and members to be private but
  123. // they have to be accessible for unit-testing. Another, possibly better,
  124. // solution is to make this class friend of test class but this is not
  125. // what's followed in other classes.
  126. /// \brief Default constructor.
  127. ///
  128. /// Default constructor is protected as the object can be created
  129. /// only via \ref instance method.
  130. TestControl();
  131. /// \brief Check if test exit condtitions fulfiled.
  132. ///
  133. /// Method checks if test exit conditions are fulfiled.
  134. /// Exit conditions are checked periodically from the
  135. /// main loop. Program should break the main loop when
  136. /// this method returns true. It is calling function
  137. /// responsibility to break main loop gracefully and
  138. /// cleanup after test execution.
  139. ///
  140. /// \return true if any of the exit conditions is fulfiled.
  141. bool checkExitConditions() const;
  142. /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
  143. ///
  144. /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
  145. /// If empty buffer is passed the option buffer will be initialized
  146. /// to length 2 and values will be initialized to zeros. Otherwise
  147. /// function will initialize option buffer with values in passed buffer.
  148. ///
  149. /// \param u universe (V6 or V4).
  150. /// \param type option-type.
  151. /// \param buf option-buffer.
  152. /// \throw if elapsed time buffer size is neither 2 nor 0.
  153. /// \return instance o the option.
  154. static dhcp::OptionPtr
  155. factoryElapsedTime6(dhcp::Option::Universe u,
  156. uint16_t type,
  157. const dhcp::OptionBuffer& buf);
  158. /// \brief Factory function to create generic option.
  159. ///
  160. /// This factory function creates option with specified universe,
  161. /// type and buf. It does not have any additional logic validating
  162. /// the buffer contents, size etc.
  163. ///
  164. /// \param u universe (V6 or V4).
  165. /// \param type option-type.
  166. /// \param buf option-buffer.
  167. /// \return instance o the option.
  168. static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u,
  169. uint16_t type,
  170. const dhcp::OptionBuffer& buf);
  171. /// \brief Factory function to create IA_NA option.
  172. ///
  173. /// This factory function creates DHCPv6 IA_NA option instance.
  174. /// \TODO: add support for IA Address options.
  175. /// \param u universe (V6 or V4).
  176. /// \param type option-type.
  177. /// \param buf option-buffer.
  178. /// \return instance of IA_NA option.
  179. static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u,
  180. uint16_t type,
  181. const dhcp::OptionBuffer& buf);
  182. /// \brief Factory function to create DHCPv6 ORO option.
  183. ///
  184. /// This factory function creates DHCPv6 Option Request Option instance.
  185. /// The created option will contain the following set of requested options:
  186. /// - D6O_NAME_SERVERS
  187. /// - D6O_DOMAIN_SEARCH
  188. ///
  189. /// \param u universe (V6 or V4).
  190. /// \param type option-type.
  191. /// \param buf option-buffer (ignored and should be empty).
  192. /// \return instance of ORO option.
  193. static dhcp::OptionPtr
  194. factoryOptionRequestOption6(dhcp::Option::Universe u,
  195. uint16_t type,
  196. const dhcp::OptionBuffer& buf);
  197. /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance.
  198. ///
  199. /// This factory function creates DHCPv6 RAPID_COMMIT option instance.
  200. /// The buffer passed to this option must be empty because option does
  201. /// not have any payload.
  202. ///
  203. /// \param u universe (V6 or V4).
  204. /// \param type option-type.
  205. /// \param buf option-buffer (ignored and should be empty).
  206. /// \return instance of RAPID_COMMIT option..
  207. static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u,
  208. uint16_t type,
  209. const dhcp::OptionBuffer& buf);
  210. /// \brief Factory function to create DHCPv4 Request List option.
  211. ///
  212. /// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option
  213. /// instance with the following set of requested options:
  214. /// - DHO_SUBNET_MASK,
  215. /// - DHO_BROADCAST_ADDRESS,
  216. /// - DHO_TIME_OFFSET,
  217. /// - DHO_ROUTERS,
  218. /// - DHO_DOMAIN_NAME,
  219. /// - DHO_DOMAIN_NAME_SERVERS,
  220. /// - DHO_HOST_NAME.
  221. ///
  222. /// \param u universe (V6 or V4).
  223. /// \param type option-type.
  224. /// \param buf option-buffer (ignored and should be empty).
  225. /// \return instance o the generic option.
  226. static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
  227. uint16_t type,
  228. const dhcp::OptionBuffer& buf);
  229. /// \brief Generate DUID.
  230. ///
  231. /// Method generates unique DUID. The number of DUIDs it can generate
  232. /// depends on the number of simulated clinets, which is specified
  233. /// from the command line. It uses \ref CommandOptions object to retrieve
  234. /// number of clinets. Since the last six octets of DUID are constructed
  235. /// from the MAC address, this function uses \ref generateMacAddress
  236. /// internally to randomize the DUID.
  237. ///
  238. /// \throw isc::BadValue if \ref generateMacAddress throws.
  239. /// \return vector representing DUID.
  240. std::vector<uint8_t> generateDuid() const;
  241. /// \brief Generate MAC address.
  242. ///
  243. /// This method generates MAC address. The number of unique
  244. /// MAC addresses it can generate is determined by the number
  245. /// simulated DHCP clients specified from command line. It uses
  246. /// \ref CommandOptions object to retrieve number of clients.
  247. /// Based on this the random value is generated and added to
  248. /// the MAC address prefix (default MAC address).
  249. ///
  250. /// \throw isc::BadValue if MAC address prefix (default or specified
  251. /// from the command line) has invalid size (expected 6 octets).
  252. /// \return generated MAC address.
  253. std::vector<uint8_t> generateMacAddress() const;
  254. /// \brief Returns number of exchanges to be started.
  255. ///
  256. /// Method returns number of new exchanges to be started as soon
  257. /// as possible to satisfy expected rate. Calculation used here
  258. /// is based on current time, due time calculated with
  259. /// \ref updateSendTime function and expected rate.
  260. ///
  261. /// \return number of exchanges to be started immediatelly.
  262. uint64_t getNextExchangesNum() const;
  263. /// \brief Initializes Statistics Manager.
  264. ///
  265. /// This function initializes Statistics Manager. If there is
  266. /// the one initialized already it is released.
  267. void initializeStatsMgr();
  268. /// \brief Open socket to communicate with DHCP server.
  269. ///
  270. /// Method opens socket and binds it to local address. Function will
  271. /// can use either interface name, local address or server address
  272. /// to create a socket, depending on what is available (specified
  273. /// from the command line). If socket can't be created for any
  274. /// reason, exception is thrown.
  275. /// If destination address is broadcast (for DHCPv4) or multicast
  276. /// (for DHCPv6) than broadcast or multicast option is set on
  277. /// the socket.
  278. ///
  279. /// \param port port to bound socket to.
  280. /// \throw isc::BadValue if socket can't be created for given
  281. /// interface, local address or remote address.
  282. /// \throw isc::InvalidOperation if broadcast option can't be
  283. /// set for the v4 socket or if multicast option cat't be set
  284. /// for the v6 socket.
  285. /// \throw isc::Unexpected if interal unexpected error occured.
  286. /// \return socket descriptor.
  287. int openSocket(uint16_t port = 0) const;
  288. /// \brief Print performance statistics.
  289. ///
  290. /// Method prints performance statistics.
  291. /// \throws isc::InvalidOperation if Statistics Manager was
  292. /// not initialized.
  293. void printStats() const;
  294. void receivePacket4(dhcp::Pkt4Ptr& pkt4);
  295. void receivePacket6(dhcp::Pkt6Ptr& pkt4);
  296. /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
  297. ///
  298. /// Method receives DHCPv4 or DHCPv6 packets from the server.
  299. void receivePackets();
  300. /// \brief Register option factory functions for DHCPv4
  301. ///
  302. /// Method registers option factory functions for DHCPv4.
  303. /// These functions are called to create instances of DHCPv4
  304. /// options. Call \ref Option::factory to invoke factory
  305. /// function for particular option. Don't use this function directly.
  306. /// Use \ref registerOptionFactories instead.
  307. void registerOptionFactories4() const;
  308. /// \brief Register option factory functions for DHCPv6
  309. ///
  310. /// Method registers option factory functions for DHCPv6.
  311. /// These functions are called to create instances of DHCPv6
  312. /// options. Call \ref Option::factory to invoke factory
  313. /// function for particular option. Don't use this function directly.
  314. /// Use \ref registerOptionFactories instead.
  315. void registerOptionFactories6() const;
  316. /// \brief Register option factory functions for DHCPv4 or DHCPv6.
  317. ///
  318. /// Method registers option factory functions for DHCPv4 or DHCPv6,
  319. /// depending in whch mode test is currently running.
  320. void registerOptionFactories() const;
  321. /// \brief Send DHCPv4 DISCOVER message.
  322. ///
  323. /// Method creates and sends DHCPv4 DISCOVER message to the server
  324. /// with the following options:
  325. /// - MESSAGE_TYPE set to DHCPDISCOVER
  326. /// - PARAMETER_REQUEST_LIST with the same list of requested options
  327. /// as described in \ref factoryRequestList.
  328. /// The transaction id and MAC address are randomly generated for
  329. /// the message. Range of unique MAC addresses generated depends
  330. /// on the number of clients specified from the command line.
  331. ///
  332. /// \param socket socket to be used to send the message.
  333. /// \throw isc::Unexpected if failed to create new packet instance.
  334. /// \throw isc::BadValue if MAC address has invalid length.
  335. void sendDiscover4(const TestControlSocket& socket);
  336. /// \brief Send DHCPv6 SOLICIT message.
  337. ///
  338. /// Method creates and sends DHCPv6 SOLICIT message to the server
  339. /// with the following options:
  340. /// - D6O_ELAPSED_TIME,
  341. /// - D6O_RAPID_COMMIT if rapid commit is requested in command line,
  342. /// - D6O_CLIENTID,
  343. /// - D6O_ORO (Option Request Option),
  344. /// - D6O_IA_NA.
  345. ///
  346. /// \param socket socket to be used to send the message.
  347. /// \throw isc::Unexpected if failed to create new packet instance.
  348. void sendSolicit6(const TestControlSocket& socket);
  349. /// \brief Set default DHCPv4 packet parameters.
  350. ///
  351. /// This method sets default parameters on the DHCPv4 packet:
  352. /// - interface name,
  353. /// - local port = 68 (DHCP client port),
  354. /// - remote port = 67 (DHCP server port),
  355. /// - server's address,
  356. /// - GIADDR = local address where socket is bound to,
  357. /// - hops = 1 (pretending that we are a relay)
  358. ///
  359. /// \param socket socket used to send the packet.
  360. /// \param pkt reference to packet to be configured.
  361. void setDefaults4(const TestControlSocket& socket,
  362. const boost::shared_ptr<dhcp::Pkt4>& pkt);
  363. /// \brief Set default DHCPv6 packet parameters.
  364. ///
  365. /// This method sets default parameters on the DHCPv6 packet:
  366. /// - interface name,
  367. /// - interface index,
  368. /// - local port,
  369. /// - remote port,
  370. /// - local address,
  371. /// - remote address (server).
  372. ///
  373. /// \param socket socket used to send the packet.
  374. /// \param pkt reference to packet to be configured.
  375. void setDefaults6(const TestControlSocket& socket,
  376. const boost::shared_ptr<dhcp::Pkt6>& pkt);
  377. /// \brief Update due time to initiate next chunk of exchanges.
  378. ///
  379. /// Method updates due time to initiate next chunk of exchanges.
  380. /// Function takes current time, last sent packet's time and
  381. /// expected rate in its calculations.
  382. void updateSendDue();
  383. private:
  384. boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
  385. ///< of exchanges.
  386. boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
  387. /// was initiated.
  388. StatsMgr4Ptr stats_mgr4_; /// Statistics Manager 4.
  389. StatsMgr6Ptr stats_mgr6_; /// Statistics Manager 6.
  390. uint64_t sent_packets_0_;
  391. uint64_t sent_packets_1_;
  392. };
  393. } // namespace perfdhcp
  394. } // namespace isc
  395. #endif // __COMMAND_OPTIONS_H