test_control_unittest.cc 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <cstddef>
  15. #include <stdint.h>
  16. #include <string>
  17. #include <fstream>
  18. #include <gtest/gtest.h>
  19. #include <boost/date_time/posix_time/posix_time.hpp>
  20. #include <exceptions/exceptions.h>
  21. #include <asiolink/io_address.h>
  22. #include <dhcp/dhcp4.h>
  23. #include <dhcp/iface_mgr.h>
  24. #include "command_options_helper.h"
  25. #include "../test_control.h"
  26. using namespace std;
  27. using namespace boost::posix_time;
  28. using namespace isc;
  29. using namespace isc::dhcp;
  30. using namespace isc::perfdhcp;
  31. /// \brief Test Control class with protected members made public.
  32. ///
  33. /// This class makes protected TestControl class'es member public
  34. /// to allow unit testing.
  35. class NakedTestControl: public TestControl {
  36. public:
  37. /// \brief Incremental transaction id generaator.
  38. ///
  39. /// This is incremental transaction id generator. It overrides
  40. /// the default transaction id generator that generates transaction
  41. /// ids using random function. This generator will generate values
  42. /// like: 1,2,3 etc.
  43. class IncrementalGenerator : public TestControl::NumberGenerator {
  44. public:
  45. /// \brief Default constructor.
  46. IncrementalGenerator() :
  47. NumberGenerator(),
  48. transid_(0) {
  49. }
  50. /// \brief Generate unique transaction id.
  51. ///
  52. /// Generate unique transaction ids incrementally:
  53. /// 1,2,3,4 etc.
  54. ///
  55. /// \return generated transaction id.
  56. virtual uint32_t generate() {
  57. return (++transid_);
  58. }
  59. private:
  60. uint32_t transid_; ///< Last generated transaction id.
  61. };
  62. using TestControl::checkExitConditions;
  63. using TestControl::factoryElapsedTime6;
  64. using TestControl::factoryGeneric;
  65. using TestControl::factoryIana6;
  66. using TestControl::factoryOptionRequestOption6;
  67. using TestControl::factoryRapidCommit6;
  68. using TestControl::factoryRequestList4;
  69. using TestControl::generateDuid;
  70. using TestControl::generateMacAddress;
  71. using TestControl::getNextExchangesNum;
  72. using TestControl::getTemplateBuffer;
  73. using TestControl::initPacketTemplates;
  74. using TestControl::initializeStatsMgr;
  75. using TestControl::openSocket;
  76. using TestControl::processReceivedPacket4;
  77. using TestControl::processReceivedPacket6;
  78. using TestControl::registerOptionFactories;
  79. using TestControl::sendDiscover4;
  80. using TestControl::sendSolicit6;
  81. using TestControl::setDefaults4;
  82. using TestControl::setDefaults6;
  83. NakedTestControl() : TestControl() {
  84. uint32_t clients_num = CommandOptions::instance().getClientsNum() == 0 ?
  85. 1 : CommandOptions::instance().getClientsNum();
  86. setMacAddrGenerator(NumberGeneratorPtr(new TestControl::SequencialGenerator(clients_num)));
  87. };
  88. };
  89. /// \brief Test Fixture Class
  90. ///
  91. /// This test fixture class is used to perform
  92. /// unit tests on perfdhcp TestControl class.
  93. class TestControlTest : public virtual ::testing::Test
  94. {
  95. public:
  96. typedef std::vector<uint8_t> MacAddress;
  97. typedef MacAddress::iterator MacAddressIterator;
  98. typedef std::vector<uint8_t> Duid;
  99. typedef Duid::iterator DuidIterator;
  100. /// \brief Default Constructor
  101. TestControlTest() { }
  102. /// \brief Create packet template file from binary data.
  103. ///
  104. /// Function creates file containing data from the provided buffer
  105. /// in hexadecimal format.
  106. /// \param filename template file to be created.
  107. /// \param buffer with binary datato be stored in file.
  108. /// \return true if file creation successful.
  109. bool createTemplateFile(const std::string& filename,
  110. const std::vector<uint8_t>& buf) const {
  111. std::ofstream temp_file;
  112. temp_file.open(filename.c_str(), ios::out | ios::trunc);
  113. if (!temp_file.is_open()) {
  114. return (false);
  115. }
  116. for (int i = 0; i < buf.size(); ++i) {
  117. int first_digit = buf[i] / 16;
  118. int second_digit = buf[i] % 16;
  119. temp_file << std::hex << first_digit << second_digit << std::dec;
  120. }
  121. temp_file.close();
  122. return (true);
  123. }
  124. /// \brief Get local loopback interface name.
  125. ///
  126. /// Scan available network interfaces for local loopback
  127. /// interface and get its name. On Linux this interface is
  128. /// usually called 'lo' but on other systems, e.g. BSD
  129. /// it will have slightly different name. Local loopback
  130. /// interface is required for unit tests that require
  131. /// socket creation.
  132. ///
  133. /// \return local loopback interface name.
  134. std::string getLocalLoopback() const {
  135. const IfaceMgr::IfaceCollection& ifaces =
  136. IfaceMgr::instance().getIfaces();
  137. for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
  138. iface != ifaces.end();
  139. ++iface) {
  140. if (iface->flag_loopback_) {
  141. return (iface->getName());
  142. }
  143. }
  144. return ("");
  145. }
  146. /// \brief Match requested options in the buffer with given list.
  147. ///
  148. /// This method iterates through options provided in the buffer
  149. /// and matches them with the options specified with first parameter.
  150. /// Options in both vectors may be laid in different order.
  151. ///
  152. /// \param requested_options reference buffer with options.
  153. /// \param buf test buffer with options that will be matched.
  154. /// \return number of options from the buffer matched with options
  155. /// in the reference buffer.
  156. int matchRequestedOptions(const dhcp::OptionBuffer& requested_options,
  157. const dhcp::OptionBuffer& buf) const {
  158. size_t matched_num = 0;
  159. for (size_t i = 0; i < buf.size(); ++i) {
  160. for (int j = 0; j < requested_options.size(); ++j) {
  161. if (requested_options[j] == buf[i]) {
  162. // Requested option has been found.
  163. ++matched_num;
  164. }
  165. }
  166. }
  167. return (matched_num);
  168. }
  169. /// \brief Match requested DHCPv6 options in the buffer with given list.
  170. ///
  171. /// This method iterates through options provided in the buffer and
  172. /// matches them with the options specified with first parameter.
  173. /// Options in both vectors ma be laid in different order.
  174. ///
  175. /// \param requested_options reference buffer with options.
  176. /// \param buf test buffer with options that will be matched.
  177. /// \return number of options from the buffer matched with options in
  178. /// the reference buffer or -1 if error occured.
  179. int matchRequestedOptions6(const dhcp::OptionBuffer& requested_options,
  180. const dhcp::OptionBuffer& buf) const {
  181. // Sanity check.
  182. if ((requested_options.size() % 2 != 0) ||
  183. (buf.size() % 2 != 0)) {
  184. return -1;
  185. }
  186. size_t matched_num = 0;
  187. for (size_t i = 0; i < buf.size(); i += 2) {
  188. for (int j = 0; j < requested_options.size(); j += 2) {
  189. uint16_t opt_i = buf[i + 1] << 8 + buf[i] & 0xFF;
  190. uint16_t opt_j = requested_options[j + 1] << 8 + requested_options[j] & 0xFF;
  191. if (opt_i == opt_j) {
  192. // Requested option has been found.
  193. ++matched_num;
  194. }
  195. }
  196. }
  197. return (matched_num);
  198. }
  199. /// \brief Calculate the maximum vectors' mismatch position.
  200. ///
  201. /// This helper function calculates the maximum mismatch position
  202. /// between two vectors (two different DUIDs or MAC addresses).
  203. /// Calculated position is counted from the end of vectors.
  204. /// Calculation is based on number of simulated clients. When number
  205. /// of clients is less than 256 different DUIDs or MAC addresses can
  206. /// can be coded in such a way that they differ on last vector element.
  207. /// If number of clients is between 257 and 65536 they can differ
  208. /// on two last positions so the returned value will be 2 and so on.
  209. ///
  210. /// \param clients_num number of simulated clinets
  211. /// \return maximum mismatch position
  212. int unequalOctetPosition(int clients_num) const {
  213. if (!clients_num) {
  214. return (0);
  215. }
  216. clients_num--;
  217. int cnt = 0;
  218. while (clients_num) {
  219. clients_num >>= 8;
  220. ++cnt;
  221. }
  222. return (cnt);
  223. }
  224. /// brief Test generation of mulitple DUIDs
  225. ///
  226. /// Thie method checks the generation of multiple DUIDs. Number
  227. /// of iterations depends on the number of simulated clients.
  228. /// It is expected that DUID's size is 14 (consists of DUID-LLT
  229. /// HW type field, 4 octets of time value and MAC address). The
  230. /// MAC address can be randomized depending on the number of
  231. /// simulated clients. The DUID-LLT and HW type are expected to
  232. /// be constant. The time value has to be properly calculated
  233. /// as the number of seconds since DUID time epoch. The parts
  234. /// of MAC address has to change if multiple clients are simulated
  235. /// and do not change if single client is simulated.
  236. void testDuid() const {
  237. int clients_num = CommandOptions::instance().getClientsNum();
  238. // Initialize Test Control class.
  239. NakedTestControl tc;
  240. // The old duid will be holding the previously generated DUID.
  241. // It will be used to compare against the new one. If we have
  242. // multiple clients we want to make sure that duids differ.
  243. uint8_t randomized = 0;
  244. Duid old_duid(tc.generateDuid(randomized));
  245. Duid new_duid(0);
  246. // total_dist shows the total difference between generated duid.
  247. // It has to be greater than zero if multiple clients are simulated.
  248. size_t total_dist = 0;
  249. // Number of unique DUIDs.
  250. size_t unique_duids = 0;
  251. // Holds the position if the octet on which two DUIDS can be different.
  252. // If number of clients is 256 or less it is last DUID octet (except for
  253. // single client when subsequent DUIDs have to be equal). If number of
  254. // clients is between 257 and 65536 the last two octets can differ etc.
  255. int unequal_pos = unequalOctetPosition(clients_num);
  256. // Keep generated DUIDs in this container.
  257. std::list<std::vector<uint8_t> > duids;
  258. // Perform number of iterations to generate number of DUIDs.
  259. for (int i = 0; i < 10 * clients_num; ++i) {
  260. if (new_duid.empty()) {
  261. new_duid = old_duid;
  262. } else {
  263. std::swap(old_duid, new_duid);
  264. new_duid = tc.generateDuid(randomized);
  265. }
  266. // The DUID-LLT is expected to start with DUID_LLT value
  267. // of 1 and hardware ethernet type equal to 1 (HWETHER_TYPE).
  268. const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
  269. // We assume DUID-LLT length 14. This includes 4 octets of
  270. // DUID_LLT value, two octets of hardware type, 4 octets
  271. // of time value and 6 octets of variable link layer (MAC)
  272. // address.
  273. const int duid_llt_size = 14;
  274. ASSERT_EQ(duid_llt_size, new_duid.size());
  275. // The first four octets do not change.
  276. EXPECT_TRUE(std::equal(new_duid.begin(), new_duid.begin() + 4,
  277. duid_llt_and_hw));
  278. // As described in RFC3315: 'the time value is the time
  279. // that the DUID is generated represented in seconds
  280. // since midnight (UTC), January 1, 2000, modulo 2^32.'
  281. uint32_t duid_time = 0;
  282. // Pick 4 bytes of the time from generated DUID and put them
  283. // in reverse order (in DUID they are stored in network order).
  284. for (int j = 4; j < 8; ++j) {
  285. duid_time |= new_duid[j] << (j - 4);
  286. }
  287. // Calculate the duration since epoch time.
  288. ptime now = microsec_clock::universal_time();
  289. ptime duid_epoch(from_iso_string("20000101T000000"));
  290. time_period period(duid_epoch, now);
  291. // Current time is the same or later than time from the DUID because
  292. // DUID had been generated before reference duration was calculated.
  293. EXPECT_GE(period.length().total_seconds(), duid_time);
  294. // Get the mismatch position (counting from the end) of
  295. // mismatched octet between previously generated DUID
  296. // and current.
  297. std::pair<DuidIterator, DuidIterator> mismatch_pos =
  298. std::mismatch(old_duid.begin(), old_duid.end(),
  299. new_duid.begin());
  300. size_t mismatch_dist =
  301. std::distance(mismatch_pos.first, old_duid.end());
  302. // For single client total_dist is expected to be 0 because
  303. // old_duid and new_duid should always match. If we have
  304. // more clients then duids have to differ except the case
  305. // if randomization algorithm generates the same values but
  306. // this would be an error in randomization algorithm.
  307. total_dist += mismatch_dist;
  308. // Mismatch may have occured on the DUID octet position
  309. // up to calculated earlier unequal_pos.
  310. ASSERT_LE(mismatch_dist, unequal_pos);
  311. // unique will inform if tested DUID is unique.
  312. bool unique = true;
  313. for (std::list<std::vector<uint8_t> >::const_iterator it =
  314. duids.begin();
  315. it != duids.end(); ++it) {
  316. // DUIDs should be of the same size if we want to compare them.
  317. ASSERT_EQ(new_duid.size(), it->size());
  318. // Check if DUID is unique.
  319. if (std::equal(new_duid.begin(), new_duid.end(), it->begin())) {
  320. unique = false;
  321. }
  322. }
  323. // Expecting that DUIDs will be unique only when
  324. // first clients-num iterations is performed.
  325. // After that, DUIDs become non unique.
  326. if (unique) {
  327. ++unique_duids;
  328. }
  329. // For number of iterations equal to clients_num,2*clients_num
  330. // 3*clients_num ... we have to have number of unique duids
  331. // equal to clients_num.
  332. if ((i != 0) && (i % clients_num == 0)) {
  333. ASSERT_EQ(clients_num, unique_duids);
  334. }
  335. // Remember generated DUID.
  336. duids.push_back(new_duid);
  337. }
  338. // If we have more than one client at least one mismatch occured.
  339. if (clients_num < 2) {
  340. EXPECT_EQ(0, total_dist);
  341. }
  342. }
  343. /// \brief Test DHCPv4 exchanges.
  344. ///
  345. /// Function simulates DHCPv4 exchanges. Function caller specifies
  346. /// number of exchanges to be simulated and number of simulated
  347. /// responses. When number of responses is lower than number of
  348. /// iterations than the difference between them is the number
  349. /// of simulated packet drops. This is useful to test if program
  350. /// exit conditions are handled properly (maximum number of packet
  351. /// drops specified as -D<max-drops> is taken into account).
  352. ///
  353. /// \param iterations_num number of exchanges to simulate.
  354. /// \param receive_num number of received OFFER packets.
  355. /// \param iterations_performed actual number of iterations.
  356. void testPkt4Exchange(int iterations_num,
  357. int receive_num,
  358. bool use_templates,
  359. int& iterations_performed) const {
  360. int sock_handle = 0;
  361. NakedTestControl tc;
  362. tc.initializeStatsMgr();
  363. // Use templates files to crate packets.
  364. if (use_templates) {
  365. tc.initPacketTemplates();
  366. ASSERT_NO_THROW(tc.getTemplateBuffer(0));
  367. ASSERT_NO_THROW(tc.getTemplateBuffer(1));
  368. }
  369. // Incremental transaction id generator will generate
  370. // predictable values of transaction id for each iteration.
  371. // This is important because we need to simulate responses
  372. // from the server and use the same transaction ids as in
  373. // packets sent by client.
  374. TestControl::NumberGeneratorPtr
  375. generator(new NakedTestControl::IncrementalGenerator());
  376. tc.setTransidGenerator(generator);
  377. // Socket is needed to send packets through the interface.
  378. ASSERT_NO_THROW(sock_handle = tc.openSocket());
  379. TestControl::TestControlSocket sock(sock_handle);
  380. uint32_t transid = 0;
  381. for (int i = 0; i < iterations_num; ++i) {
  382. if (use_templates) {
  383. ASSERT_NO_THROW(tc.sendDiscover4(sock, tc.getTemplateBuffer(0)));
  384. } else {
  385. ASSERT_NO_THROW(tc.sendDiscover4(sock));
  386. }
  387. ++transid;
  388. // Do not simulate responses for packets later
  389. // that specified as receive_num. This simulates
  390. // packet drops.
  391. if (i < receive_num) {
  392. boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(transid));
  393. ASSERT_NO_THROW(tc.processReceivedPacket4(sock, offer_pkt4));
  394. ++transid;
  395. }
  396. if (tc.checkExitConditions()) {
  397. iterations_performed = i + 1;
  398. break;
  399. }
  400. iterations_performed = i + 1;
  401. }
  402. }
  403. /// \brief Test DHCPv6 exchanges.
  404. ///
  405. /// Function simulates DHCPv6 exchanges. Function caller specifies
  406. /// number of exchanges to be simulated and number of simulated
  407. /// responses. When number of responses is lower than number of
  408. /// iterations than the difference between them is the number
  409. /// of simulated packet drops. This is useful to test if program
  410. /// exit conditions are handled properly (maximum number of packet
  411. /// drops specified as -D<max-drops> is taken into account).
  412. ///
  413. /// \param iterations_num number of exchanges to simulate.
  414. /// \param receive_num number of received OFFER packets.
  415. /// \param iterations_performed actual number of iterations.
  416. void testPkt6Exchange(int iterations_num,
  417. int receive_num,
  418. bool use_templates,
  419. int& iterations_performed) const {
  420. int sock_handle = 0;
  421. NakedTestControl tc;
  422. tc.initializeStatsMgr();
  423. // Use templates files to crate packets.
  424. if (use_templates) {
  425. tc.initPacketTemplates();
  426. ASSERT_NO_THROW(tc.getTemplateBuffer(0));
  427. ASSERT_NO_THROW(tc.getTemplateBuffer(1));
  428. }
  429. // Incremental transaction id generator will generate
  430. // predictable values of transaction id for each iteration.
  431. // This is important because we need to simulate reponses
  432. // from the server and use the same transaction ids as in
  433. // packets sent by client.
  434. TestControl::NumberGeneratorPtr
  435. generator(new NakedTestControl::IncrementalGenerator());
  436. tc.setTransidGenerator(generator);
  437. // Socket is needed to send packets through the interface.
  438. ASSERT_NO_THROW(sock_handle = tc.openSocket());
  439. TestControl::TestControlSocket sock(sock_handle);
  440. uint32_t transid = 0;
  441. for (int i = 0; i < iterations_num; ++i) {
  442. // Do not simulate responses for packets later
  443. // that specified as receive_num. This simulates
  444. // packet drops.
  445. if (use_templates) {
  446. ASSERT_NO_THROW(tc.sendSolicit6(sock, tc.getTemplateBuffer(0)));
  447. } else {
  448. ASSERT_NO_THROW(tc.sendSolicit6(sock));
  449. }
  450. ++transid;
  451. if (i < receive_num) {
  452. boost::shared_ptr<Pkt6>
  453. advertise_pkt6(createAdvertisePkt6(transid));
  454. // Receive ADVERTISE and send REQUEST.
  455. ASSERT_NO_THROW(tc.processReceivedPacket6(sock, advertise_pkt6));
  456. ++transid;
  457. }
  458. if (tc.checkExitConditions()) {
  459. iterations_performed = i + 1;
  460. break;
  461. }
  462. iterations_performed = i + 1;
  463. }
  464. }
  465. /// \brief Test generation of multiple MAC addresses.
  466. ///
  467. /// This method validates generation of multiple MAC addresses.
  468. /// The MAC address can be randomized depending on the number
  469. /// of simulated clients. This test checks if different MAC
  470. /// addresses are generated if number of simulated clients is
  471. /// greater than 1. It also checks if the same MAC addresses is
  472. /// generated if only 1 client is simulated.
  473. void testMacAddress() const {
  474. int clients_num = CommandOptions::instance().getClientsNum();
  475. // The old_mac will be holding the value of previously generated
  476. // MAC address. We will be comparing the newly generated one with it
  477. // to see if it changes when mulitple clients are simulated or if it
  478. // does not change when single client is simulated.
  479. MacAddress old_mac(CommandOptions::instance().getMacTemplate());
  480. // Holds the position if the octet on which two MAC addresses can
  481. // be different. If number of clients is 256 or less it is last MAC
  482. // octet (except for single client when subsequent MAC addresses
  483. // have to be equal). If number of clients is between 257 and 65536
  484. // the last two octets can differ etc.
  485. int unequal_pos = unequalOctetPosition(clients_num);
  486. // Number of unique MACs.
  487. size_t unique_macs = 0;
  488. // Initialize Test Controller.
  489. NakedTestControl tc;
  490. size_t total_dist = 0;
  491. // Keep generated MACs in this container.
  492. std::list<std::vector<uint8_t> > macs;
  493. // Do many iterations to generate and test MAC address values.
  494. for (int i = 0; i < clients_num * 10; ++i) {
  495. // Generate new MAC address.
  496. uint8_t randomized = 0;
  497. MacAddress new_mac(tc.generateMacAddress(randomized));
  498. // Get the mismatch position (counting from the end) of
  499. // mismatched octet between previously generated MAC address
  500. // and current.
  501. std::pair<MacAddressIterator, MacAddressIterator> mismatch_pos =
  502. std::mismatch(old_mac.begin(), old_mac.end(), new_mac.begin());
  503. size_t mismatch_dist =
  504. std::distance(mismatch_pos.first, old_mac.end());
  505. // For single client total_dist is expected to be 0 because
  506. // old_mac and new_mac should always match. If we have
  507. // more clients then MAC addresses have to differ except
  508. // the case if randomization algorithm generates the same
  509. // values but this would be an error in randomization algorithm.
  510. total_dist += mismatch_dist;
  511. // Mismatch may have occured on the MAC address'es octet position
  512. // up to calculated earlier unequal_pos.
  513. ASSERT_LE(mismatch_dist, unequal_pos);
  514. // unique will inform if tested DUID is unique.
  515. bool unique = true;
  516. for (std::list<std::vector<uint8_t> >::const_iterator it =
  517. macs.begin();
  518. it != macs.end(); ++it) {
  519. // MACs should be of the same size if we want to compare them.
  520. ASSERT_EQ(new_mac.size(), it->size());
  521. // Check if MAC is unique.
  522. if (std::equal(new_mac.begin(), new_mac.end(), it->begin())) {
  523. unique = false;
  524. }
  525. }
  526. // Expecting that MACs will be unique only when
  527. // first clients-num iterations is performed.
  528. // After that, MACs become non unique.
  529. if (unique) {
  530. ++unique_macs;
  531. }
  532. // For number of iterations equal to clients_num,2*clients_num
  533. // 3*clients_num ... we have to have number of unique MACs
  534. // equal to clients_num.
  535. if ((i != 0) && (i % clients_num == 0)) {
  536. ASSERT_EQ(clients_num, unique_macs);
  537. }
  538. // Remember generated MAC.
  539. macs.push_back(new_mac);
  540. }
  541. if (clients_num < 2) {
  542. EXPECT_EQ(total_dist, 0);
  543. }
  544. }
  545. /// \brief Parse command line string with CommandOptions.
  546. ///
  547. /// \param cmdline command line string to be parsed.
  548. /// \throw isc::Unexpected if unexpected error occured.
  549. /// \throw isc::InvalidParameter if command line is invalid.
  550. void processCmdLine(const std::string& cmdline) const {
  551. CommandOptionsHelper::process(cmdline);
  552. }
  553. private:
  554. /// \brief Create DHCPv4 OFFER packet.
  555. ///
  556. /// \param transid transaction id.
  557. /// \return instance of the packet.
  558. boost::shared_ptr<Pkt4>
  559. createOfferPkt4(uint32_t transid) const {
  560. boost::shared_ptr<Pkt4> offer(new Pkt4(DHCPOFFER, transid));
  561. OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
  562. OptionBuffer(DHCPOFFER));
  563. OptionPtr opt_serverid = Option::factory(Option::V4,
  564. DHO_DHCP_SERVER_IDENTIFIER,
  565. OptionBuffer(4, 1));
  566. offer->setYiaddr(asiolink::IOAddress("127.0.0.1"));
  567. offer->addOption(opt_msg_type);
  568. offer->addOption(opt_serverid);
  569. offer->updateTimestamp();
  570. return (offer);
  571. }
  572. /// \brief Create DHCPv6 ADVERTISE packet.
  573. ///
  574. /// \param transid transaction id.
  575. /// \return instance of the packet.
  576. boost::shared_ptr<Pkt6>
  577. createAdvertisePkt6(uint32_t transid) const {
  578. OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
  579. OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
  580. NakedTestControl tc;
  581. uint8_t randomized = 0;
  582. std::vector<uint8_t> duid(tc.generateDuid(randomized));
  583. OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
  584. boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
  585. advertise->addOption(opt_ia_na);
  586. advertise->addOption(opt_serverid);
  587. advertise->addOption(opt_clientid);
  588. advertise->updateTimestamp();
  589. return (advertise);
  590. }
  591. };
  592. TEST_F(TestControlTest, GenerateDuid) {
  593. // Simple command line that simulates one client only. Always the
  594. // same DUID will be generated.
  595. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
  596. testDuid();
  597. // Simulate 50 clients. Different DUID will be generated.
  598. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
  599. testDuid();
  600. }
  601. TEST_F(TestControlTest, GenerateMacAddress) {
  602. // Simulate one client only. Always the same MAC address will be
  603. // generated.
  604. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
  605. testMacAddress();
  606. // Simulate 50 clients. Different MAC addresses will be generated.
  607. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -R 50 all"));
  608. testMacAddress();
  609. }
  610. TEST_F(TestControlTest, Options4) {
  611. using namespace isc::dhcp;
  612. NakedTestControl tc;
  613. // By default the IP version mode is V4 so there is no need to
  614. // parse command line to override the IP version. Note that
  615. // registerOptionFactories is used for both V4 and V6.
  616. tc.registerOptionFactories();
  617. // Create option with buffer size equal to 1 and holding DHCPDISCOVER
  618. // message type.
  619. OptionPtr opt_msg_type(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
  620. OptionBuffer(1, DHCPDISCOVER)));
  621. // Validate the option type and universe.
  622. EXPECT_EQ(Option::V4, opt_msg_type->getUniverse());
  623. EXPECT_EQ(DHO_DHCP_MESSAGE_TYPE, opt_msg_type->getType());
  624. // Validate the message type from the option we have now created.
  625. uint8_t msg_type = 0;
  626. ASSERT_NO_THROW(msg_type = opt_msg_type->getUint8());
  627. EXPECT_EQ(DHCPDISCOVER, msg_type);
  628. // Create another option: DHCP_PARAMETER_REQUEST_LIST
  629. OptionPtr
  630. opt_requested_options(Option::factory(Option::V4,
  631. DHO_DHCP_PARAMETER_REQUEST_LIST));
  632. // Here is a list of options that we are requesting in the
  633. // server's response.
  634. const uint8_t requested_options[] = {
  635. DHO_SUBNET_MASK,
  636. DHO_BROADCAST_ADDRESS,
  637. DHO_TIME_OFFSET,
  638. DHO_ROUTERS,
  639. DHO_DOMAIN_NAME,
  640. DHO_DOMAIN_NAME_SERVERS,
  641. DHO_HOST_NAME
  642. };
  643. OptionBuffer
  644. requested_options_ref(requested_options,
  645. requested_options + sizeof(requested_options));
  646. // Get the option buffer. It should hold the combination of values
  647. // listed in requested_options array. However their order can be
  648. // different in general so we need to search each value separatelly.
  649. const OptionBuffer& requested_options_buf =
  650. opt_requested_options->getData();
  651. EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size());
  652. size_t matched_num = matchRequestedOptions(requested_options_ref,
  653. requested_options_buf);
  654. // We want exactly the same requested options as listed in
  655. // requested_options array - nothing more or less.
  656. EXPECT_EQ(requested_options_ref.size(), matched_num);
  657. }
  658. TEST_F(TestControlTest, Options6) {
  659. using namespace isc::dhcp;
  660. // Lets override the IP version to test V6 options (-6 parameter)
  661. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 -6 all"));
  662. NakedTestControl tc;
  663. tc.registerOptionFactories();
  664. // Validate the D6O_ELAPSED_TIME option.
  665. OptionPtr opt_elapsed_time(Option::factory(Option::V6, D6O_ELAPSED_TIME));
  666. // Validate the option type and universe.
  667. EXPECT_EQ(Option::V6, opt_elapsed_time->getUniverse());
  668. EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time->getType());
  669. // The default value of elapsed time is zero.
  670. uint16_t elapsed_time;
  671. ASSERT_NO_THROW(elapsed_time = opt_elapsed_time->getUint16());
  672. EXPECT_EQ(0, elapsed_time);
  673. // With the factory function we may also specify the actual
  674. // value of elapsed time. Let's make use of std::vector
  675. // constructor to create the option buffer, 2 octets long
  676. // with each octet initialized to 0x1.
  677. size_t elapsed_time_buf_size = 2;
  678. uint8_t elapsed_time_pattern = 0x1;
  679. OptionPtr
  680. opt_elapsed_time2(Option::factory(Option::V6, D6O_ELAPSED_TIME,
  681. OptionBuffer(elapsed_time_buf_size,
  682. elapsed_time_pattern)));
  683. // Any buffer that has size neither equal to 0 nor 2 is considered invalid.
  684. elapsed_time_buf_size = 1;
  685. EXPECT_THROW(
  686. Option::factory(Option::V6, D6O_ELAPSED_TIME,
  687. OptionBuffer(elapsed_time_buf_size, elapsed_time_pattern)),
  688. isc::BadValue
  689. );
  690. // Validate the option type and universe.
  691. EXPECT_EQ(Option::V6, opt_elapsed_time2->getUniverse());
  692. EXPECT_EQ(D6O_ELAPSED_TIME, opt_elapsed_time2->getType());
  693. // Make sure the getUint16 does not throw exception. It wile throw
  694. // buffer is shorter than 2 octets.
  695. ASSERT_NO_THROW(elapsed_time = opt_elapsed_time2->getUint16());
  696. // Check the expected value of elapsed time.
  697. EXPECT_EQ(0x0101, elapsed_time);
  698. // Validate the D6O_RAPID_COMMIT option.
  699. OptionPtr opt_rapid_commit(Option::factory(Option::V6, D6O_RAPID_COMMIT));
  700. // Validate the option type and universe.
  701. EXPECT_EQ(Option::V6, opt_rapid_commit->getUniverse());
  702. EXPECT_EQ(D6O_RAPID_COMMIT, opt_rapid_commit->getType());
  703. // Rapid commit has no data payload.
  704. EXPECT_THROW(opt_rapid_commit->getUint8(), isc::OutOfRange);
  705. // Validate the D6O_CLIENTID option.
  706. OptionBuffer duid(CommandOptions::instance().getDuidTemplate());
  707. OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
  708. EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
  709. EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
  710. const OptionBuffer& duid2 = opt_clientid->getData();
  711. ASSERT_EQ(duid.size(), duid2.size());
  712. // The Duid we set for option is the same we get.
  713. EXPECT_TRUE(std::equal(duid.begin(), duid.end(), duid2.begin()));
  714. // Validate the D6O_ORO (Option Request Option).
  715. OptionPtr opt_oro(Option::factory(Option::V6, D6O_ORO));
  716. // Prepare the reference buffer with requested options.
  717. const uint8_t requested_options[] = {
  718. 0, D6O_NAME_SERVERS,
  719. 0, D6O_DOMAIN_SEARCH,
  720. };
  721. int requested_options_num =
  722. sizeof(requested_options) / sizeof(requested_options[0]);
  723. OptionBuffer
  724. requested_options_ref(requested_options,
  725. requested_options + sizeof(requested_options));
  726. // Get the buffer from option.
  727. const OptionBuffer& requested_options_buf = opt_oro->getData();
  728. // Size of reference buffer and option buffer have to be
  729. // the same for comparison.
  730. EXPECT_EQ(requested_options_ref.size(), requested_options_buf.size());
  731. // Check if all options in the buffer are matched with reference buffer.
  732. size_t matched_num = matchRequestedOptions6(requested_options_ref,
  733. requested_options_buf);
  734. EXPECT_EQ(requested_options_num, matched_num);
  735. // Validate the D6O_IA_NA option.
  736. OptionPtr opt_ia_na(Option::factory(Option::V6, D6O_IA_NA));
  737. EXPECT_EQ(Option::V6, opt_ia_na->getUniverse());
  738. EXPECT_EQ(D6O_IA_NA, opt_ia_na->getType());
  739. // Every IA_NA option is expected to start with this sequence.
  740. const uint8_t opt_ia_na_array[] = {
  741. 0, 0, 0, 1, // IAID = 1
  742. 0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
  743. 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
  744. };
  745. OptionBuffer opt_ia_na_ref(opt_ia_na_array,
  746. opt_ia_na_array + sizeof(opt_ia_na_array));
  747. const OptionBuffer& opt_ia_na_buf = opt_ia_na->getData();
  748. ASSERT_EQ(opt_ia_na_buf.size(), opt_ia_na_ref.size());
  749. EXPECT_TRUE(std::equal(opt_ia_na_ref.begin(), opt_ia_na_ref.end(),
  750. opt_ia_na_buf.begin()));
  751. // @todo Add more tests for IA address options.
  752. }
  753. TEST_F(TestControlTest, Packet4) {
  754. // Use Interface Manager to get the local loopback interface.
  755. // If interface can't be found we don't want to fail test.
  756. std::string loopback_iface(getLocalLoopback());
  757. if (!loopback_iface.empty()) {
  758. ASSERT_NO_THROW(processCmdLine("perfdhcp -l " + loopback_iface +
  759. " -L 10547 all"));
  760. NakedTestControl tc;
  761. int sock_handle = 0;
  762. // We have to create the socket to setup some parameters of
  763. // outgoing packet.
  764. ASSERT_NO_THROW(sock_handle = tc.openSocket());
  765. TestControl::TestControlSocket sock(sock_handle);
  766. uint32_t transid = 123;
  767. boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
  768. // Set parameters on outgoing packet.
  769. ASSERT_NO_THROW(tc.setDefaults4(sock, pkt4));
  770. // Validate that packet has been setup correctly.
  771. EXPECT_EQ(loopback_iface, pkt4->getIface());
  772. EXPECT_EQ(sock.ifindex_, pkt4->getIndex());
  773. EXPECT_EQ(DHCP4_CLIENT_PORT, pkt4->getLocalPort());
  774. EXPECT_EQ(DHCP4_SERVER_PORT, pkt4->getRemotePort());
  775. EXPECT_EQ(1, pkt4->getHops());
  776. EXPECT_EQ(asiolink::IOAddress("255.255.255.255"),
  777. pkt4->getRemoteAddr());
  778. EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getLocalAddr());
  779. EXPECT_EQ(asiolink::IOAddress(sock.addr_), pkt4->getGiaddr());
  780. } else {
  781. std::cout << "Unable to find the loopback interface. Skip test. "
  782. << std::endl;
  783. }
  784. }
  785. TEST_F(TestControlTest, Packet6) {
  786. // Use Interface Manager to get the local loopback interface.
  787. // If the interface can't be found we don't want to fail test.
  788. std::string loopback_iface(getLocalLoopback());
  789. if (!loopback_iface.empty()) {
  790. ASSERT_NO_THROW(processCmdLine("perfdhcp -6 -l " + loopback_iface +
  791. " -L 10547 servers"));
  792. NakedTestControl tc;
  793. int sock_handle = 0;
  794. // Create the socket. It will be needed to set packet's
  795. // parameters.
  796. ASSERT_NO_THROW(sock_handle = tc.openSocket());
  797. TestControl::TestControlSocket sock(sock_handle);
  798. uint32_t transid = 123;
  799. boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
  800. // Set packet's parameters.
  801. ASSERT_NO_THROW(tc.setDefaults6(sock, pkt6));
  802. // Validate if parameters have been set correctly.
  803. EXPECT_EQ(loopback_iface, pkt6->getIface());
  804. EXPECT_EQ(sock.ifindex_, pkt6->getIndex());
  805. EXPECT_EQ(DHCP6_CLIENT_PORT, pkt6->getLocalPort());
  806. EXPECT_EQ(DHCP6_SERVER_PORT, pkt6->getRemotePort());
  807. EXPECT_EQ(sock.addr_, pkt6->getLocalAddr());
  808. EXPECT_EQ(asiolink::IOAddress("FF05::1:3"), pkt6->getRemoteAddr());
  809. } else {
  810. std::cout << "Unable to find the loopback interface. Skip test. "
  811. << std::endl;
  812. }
  813. }
  814. TEST_F(TestControlTest, Packet4Exchange) {
  815. // Get the local loopback interface to open socket on
  816. // it and test packets exchanges. We don't want to fail
  817. // the test if interface is not available.
  818. std::string loopback_iface(getLocalLoopback());
  819. if (loopback_iface.empty()) {
  820. std::cout << "Unable to find the loopback interface. Skip test."
  821. << std::endl;
  822. return;
  823. }
  824. // Set number of iterations to some high value.
  825. const int iterations_num = 100;
  826. processCmdLine("perfdhcp -l " + loopback_iface
  827. + " -r 100 -n 10 -R 20 -L 10547 127.0.0.1");
  828. // The actual number of iterations will be stored in the
  829. // following variable.
  830. int iterations_performed = 0;
  831. bool use_templates = false;
  832. testPkt4Exchange(iterations_num, iterations_num, use_templates, iterations_performed);
  833. // The command line restricts the number of iterations to 10
  834. // with -n 10 parameter.
  835. EXPECT_EQ(10, iterations_performed);
  836. // With the following command line we restrict the maximum
  837. // number of dropped packets to 20% of all.
  838. // Use templates for this test.
  839. processCmdLine("perfdhcp -l " + loopback_iface
  840. + " -r 100 -R 20 -n 20 -D 10% -L 10547"
  841. + " -T ../templates/discover-example.hex"
  842. + " -T ../templates/request4-example.hex"
  843. + " 127.0.0.1");
  844. // The number iterations is restricted by the percentage of
  845. // dropped packets (-D 10%). We also have to bump up the number
  846. // of iterations because the percentage limitation checks starts
  847. // at packet #10. We expect that at packet #12 the 10% threshold
  848. // will be reached.
  849. const int received_num = 10;
  850. use_templates = true;
  851. testPkt4Exchange(iterations_num, received_num, use_templates, iterations_performed);
  852. EXPECT_EQ(12, iterations_performed);
  853. }
  854. TEST_F(TestControlTest, Packet6Exchange) {
  855. // Get the local loopback interface to open socket on
  856. // it and test packets exchanges. We don't want to fail
  857. // the test if interface is not available.
  858. std::string loopback_iface(getLocalLoopback());
  859. if (loopback_iface.empty()) {
  860. std::cout << "Unable to find the loopback interface. Skip test."
  861. << std::endl;
  862. return;
  863. }
  864. const int iterations_num = 100;
  865. // Set number of iterations to 10.
  866. processCmdLine("perfdhcp -l " + loopback_iface
  867. + " -6 -r 100 -n 10 -R 20 -L 10547 ::1");
  868. int iterations_performed = 0;
  869. // Set number of received packets equal to number of iterations.
  870. // This simulates no packet drops.
  871. bool use_templates = false;
  872. testPkt6Exchange(iterations_num, iterations_num, use_templates,
  873. iterations_performed);
  874. // Actual number of iterations should be 10.
  875. EXPECT_EQ(10, iterations_performed);
  876. // The maximum number of dropped packets is 3 (because of -D 3).
  877. use_templates = true;
  878. processCmdLine("perfdhcp -l " + loopback_iface
  879. + " -6 -r 100 -n 10 -R 20 -D 3 -L 10547"
  880. + " -T ../templates/solicit-example.hex"
  881. + " -T ../templates/request6-example.hex ::1");
  882. // For the first 3 packets we are simulating responses from server.
  883. // For other packets we don't so packet as 4,5,6 will be dropped and
  884. // then test should be interrupted and actual number of iterations will
  885. // be 6.
  886. const int received_num = 3;
  887. testPkt6Exchange(iterations_num, received_num, use_templates,
  888. iterations_performed);
  889. EXPECT_EQ(6, iterations_performed);
  890. }
  891. TEST_F(TestControlTest, PacketTemplates) {
  892. std::vector<uint8_t> template1(256);
  893. std::string file1("../templates/test1.hex");
  894. std::vector<uint8_t> template2(233);
  895. std::string file2("../templates/test2.hex");
  896. for (int i = 0; i < template1.size(); ++i) {
  897. template1[i] = static_cast<uint8_t>(random() % 256);
  898. }
  899. for (int i = 0; i < template2.size(); ++i) {
  900. template2[i] = static_cast<uint8_t>(random() % 256);
  901. }
  902. ASSERT_TRUE(createTemplateFile(file1, template1));
  903. ASSERT_TRUE(createTemplateFile(file2, template2));
  904. CommandOptions& options = CommandOptions::instance();
  905. NakedTestControl tc;
  906. ASSERT_NO_THROW(
  907. processCmdLine("perfdhcp -l 127.0.0.1"
  908. " -T " + file1 + " -T " + file2 + " all")
  909. );
  910. ASSERT_NO_THROW(tc.initPacketTemplates());
  911. TestControl::TemplateBuffer buf1;
  912. TestControl::TemplateBuffer buf2;
  913. ASSERT_NO_THROW(buf1 = tc.getTemplateBuffer(0));
  914. ASSERT_NO_THROW(buf2 = tc.getTemplateBuffer(1));
  915. ASSERT_EQ(template1.size(), buf1.size());
  916. ASSERT_EQ(template2.size(), buf2.size());
  917. EXPECT_TRUE(std::equal(template1.begin(), template1.end(), buf1.begin()));
  918. EXPECT_TRUE(std::equal(template2.begin(), template2.end(), buf2.begin()));
  919. }
  920. TEST_F(TestControlTest, RateControl) {
  921. // We don't specify the exchange rate here so the aggressivity
  922. // value will determine how many packets are to be send each
  923. // time we query the getNextExchangesNum.
  924. ASSERT_NO_THROW(processCmdLine("perfdhcp -l 127.0.0.1 all"));
  925. CommandOptions& options = CommandOptions::instance();
  926. NakedTestControl tc1;
  927. uint64_t xchgs_num = tc1.getNextExchangesNum();
  928. EXPECT_EQ(options.getAggressivity(), xchgs_num);
  929. // The exchange rate is now 1 per second. We don't know how many
  930. // exchanges have to initiated exactly but for sure it has to be
  931. // non-zero value. Also, since aggressivity is very high we expect
  932. // that it will not be restricted by aggressivity.
  933. ASSERT_NO_THROW(
  934. processCmdLine("perfdhcp -l 127.0.0.1 -a 1000000 -r 1 all")
  935. );
  936. NakedTestControl tc2;
  937. xchgs_num = tc2.getNextExchangesNum();
  938. EXPECT_GT(xchgs_num, 0);
  939. EXPECT_LT(xchgs_num, options.getAggressivity());
  940. // @todo add more thorough checks for rate values.
  941. }