test_control.h 42 KB

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