test_control.h 45 KB

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