test_control.cc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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 <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <unistd.h>
  18. #include <boost/date_time/posix_time/posix_time.hpp>
  19. #include <exceptions/exceptions.h>
  20. #include <asiolink/io_address.h>
  21. #include <dhcp/libdhcp++.h>
  22. #include <dhcp/iface_mgr.h>
  23. #include <dhcp/dhcp4.h>
  24. #include "test_control.h"
  25. #include "command_options.h"
  26. using namespace std;
  27. using namespace boost;
  28. using namespace boost::posix_time;
  29. using namespace isc;
  30. using namespace isc::dhcp;
  31. using namespace isc::asiolink;
  32. namespace isc {
  33. namespace perfdhcp {
  34. TestControl::TestControlSocket::TestControlSocket(int socket) :
  35. socket_(socket),
  36. addr_("127.0.0.1") {
  37. initSocketData();
  38. }
  39. TestControl::TestControlSocket::~TestControlSocket() {
  40. IfaceMgr::instance().closeSockets();
  41. }
  42. void
  43. TestControl::TestControlSocket::initSocketData() {
  44. const IfaceMgr::IfaceCollection& ifaces =
  45. IfaceMgr::instance().getIfaces();
  46. for (IfaceMgr::IfaceCollection::const_iterator it = ifaces.begin();
  47. it != ifaces.end();
  48. ++it) {
  49. const IfaceMgr::SocketCollection& socket_collection =
  50. it->getSockets();
  51. for (IfaceMgr::SocketCollection::const_iterator s =
  52. socket_collection.begin();
  53. s != socket_collection.end();
  54. ++s) {
  55. if (s->sockfd_ == socket_) {
  56. iface_ = it->getName();
  57. addr_ = s->addr_;
  58. return;
  59. }
  60. }
  61. }
  62. isc_throw(BadValue, "interface for for specified socket "
  63. "descriptor not found");
  64. }
  65. TestControl&
  66. TestControl::instance() {
  67. static TestControl test_control;
  68. return (test_control);
  69. }
  70. TestControl::TestControl() :
  71. send_due_(microsec_clock::universal_time()),
  72. last_sent_(send_due_) {
  73. }
  74. bool
  75. TestControl::checkExitConditions() const {
  76. CommandOptions& options = CommandOptions::instance();
  77. if ((options.getNumRequests().size() > 0) &&
  78. (sent_packets_0_ >= options.getNumRequests()[0])) {
  79. return(true);
  80. } else if ((options.getNumRequests().size() == 2) &&
  81. (sent_packets_1_ >= options.getNumRequests()[1])) {
  82. return(true);
  83. }
  84. return(false);
  85. }
  86. OptionPtr
  87. TestControl::factoryElapsedTimeSolicit6(Option::Universe, uint16_t,
  88. const OptionBuffer&) {
  89. return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
  90. OptionBuffer(2, 0)));
  91. }
  92. OptionPtr
  93. TestControl::factoryGeneric(Option::Universe u, uint16_t type,
  94. const OptionBuffer& buf) {
  95. OptionPtr opt(new Option(u, type, buf));
  96. return opt;
  97. }
  98. OptionPtr
  99. TestControl::factoryIana6(Option::Universe, uint16_t,
  100. const OptionBuffer&) {
  101. const uint8_t buf_array[] = {
  102. 0, 0, 0, 1, // IAID = 1
  103. 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600
  104. 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
  105. };
  106. OptionBuffer buf(buf_array, buf_array + sizeof(buf_array));
  107. return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf));
  108. }
  109. OptionPtr
  110. TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
  111. const OptionBuffer&) {
  112. return OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer()));
  113. }
  114. OptionPtr
  115. TestControl::factoryOptionRequestOption6(Option::Universe,
  116. uint16_t,
  117. const OptionBuffer&) {
  118. const uint8_t buf_array[] = {
  119. D6O_NAME_SERVERS,
  120. D6O_DOMAIN_SEARCH
  121. };
  122. OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
  123. return OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options));
  124. }
  125. OptionPtr
  126. TestControl::factoryRequestList4(Option::Universe u,
  127. uint16_t type,
  128. const OptionBuffer& buf) {
  129. const uint8_t buf_array[] = {
  130. DHO_SUBNET_MASK,
  131. DHO_BROADCAST_ADDRESS,
  132. DHO_TIME_OFFSET,
  133. DHO_ROUTERS,
  134. DHO_DOMAIN_NAME,
  135. DHO_DOMAIN_NAME_SERVERS,
  136. DHO_HOST_NAME
  137. };
  138. OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
  139. OptionPtr opt(new Option(u, type, buf));
  140. opt->setData(buf_with_options.begin(), buf_with_options.end());
  141. return opt;
  142. }
  143. std::vector<uint8_t>
  144. TestControl::generateMacAddress() const {
  145. CommandOptions& options = CommandOptions::instance();
  146. uint32_t clients_num = options.getClientsNum();
  147. if ((clients_num == 0) || (clients_num == 1)) {
  148. return options.getMacPrefix();
  149. }
  150. // Get the base MAC address. We are going to randomize part of it.
  151. std::vector<uint8_t> mac_addr(options.getMacPrefix());
  152. if (mac_addr.size() != HW_ETHER_LEN) {
  153. isc_throw(BadValue, "invalid MAC address prefix specified");
  154. }
  155. uint32_t r = random();
  156. // The random number must be in the range 0..clients_num. This
  157. // will guarantee that every client has exactly one random MAC
  158. // address assigned.
  159. r %= clients_num;
  160. // Randomize MAC address octets.
  161. for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
  162. it >= mac_addr.begin();
  163. --it) {
  164. // Add the random value to the current octet.
  165. (*it) += r;
  166. if (r < 256) {
  167. // If we are here it means that there is no sense
  168. // to randomize the remaining octets of MAC address
  169. // because the following bytes of random value
  170. // are zero and it will have no effect.
  171. break;
  172. }
  173. // Randomize the next octet with the following
  174. // byte of random value.
  175. r >>= 8;
  176. }
  177. return mac_addr;
  178. }
  179. std::vector<uint8_t>
  180. TestControl::generateDuid() const {
  181. CommandOptions& options = CommandOptions::instance();
  182. uint32_t clients_num = options.getClientsNum();
  183. if ((clients_num == 0) || (clients_num == 1)) {
  184. return options.getDuidPrefix();
  185. }
  186. // Get the base MAC address. We are going to randomize part of it.
  187. std::vector<uint8_t> duid(options.getDuidPrefix());
  188. return duid;
  189. }
  190. uint64_t
  191. TestControl::getNextExchangesNum() const {
  192. CommandOptions& options = CommandOptions::instance();
  193. // Reset number of exchanges.
  194. uint64_t due_exchanges = 0;
  195. // Get current time.
  196. ptime now(microsec_clock::universal_time());
  197. // The due time indicates when we should start sending next chunk
  198. // of packets. If it is already due time, we should calculate
  199. // how many packets to send.
  200. if (now >= send_due_) {
  201. // If rate is specified from the command line we have to
  202. // synchornize with it.
  203. if (options.getRate() != 0) {
  204. time_period period(send_due_, now);
  205. // Null condition should not occur because we
  206. // have checked it in the first if statement but
  207. // let's keep this check just in case.
  208. if (period.is_null()) {
  209. return (0);
  210. }
  211. time_duration duration = period.length();
  212. // due_factor indicates the number of seconds that
  213. // sending next chunk of packets will take.
  214. double due_factor = duration.fractional_seconds() /
  215. time_duration::ticks_per_second();
  216. due_factor += duration.total_seconds();
  217. // Multiplying due_factor by expected rate gives the number
  218. // of exchanges to be initiated.
  219. due_exchanges = static_cast<uint64_t>(due_factor * options.getRate());
  220. // We want to make sure that at least one packet goes out.
  221. due_exchanges += 1;
  222. // We should not exceed aggressivity as it could have been
  223. // restricted from command line.
  224. if (due_exchanges > options.getAggressivity()) {
  225. due_exchanges = options.getAggressivity();
  226. }
  227. } else {
  228. // Rate is not specified so we rely on aggressivity
  229. // which is the number of packets to be sent in
  230. // one chunk.
  231. due_exchanges = options.getAggressivity();
  232. }
  233. return (due_exchanges);
  234. }
  235. return (0);
  236. }
  237. int
  238. TestControl::openSocket() const {
  239. CommandOptions& options = CommandOptions::instance();
  240. std::string localname = options.getLocalName();
  241. std::string servername = options.getServerName();
  242. uint8_t family = AF_INET;
  243. uint16_t port = 67;
  244. int sock = 0;
  245. if (options.getIpVersion() == 6) {
  246. family = AF_INET6;
  247. port = 547;
  248. }
  249. // Local name is specified along with '-l' option.
  250. // It may point to interface name or local address.
  251. if (!localname.empty()) {
  252. // CommandOptions should be already aware wether local name
  253. // is interface name or address because it uses IfaceMgr to
  254. // scan interfaces and get's their names.
  255. if (options.isInterface()) {
  256. sock = IfaceMgr::instance().openSocketFromIface(localname,
  257. port,
  258. family);
  259. } else {
  260. IOAddress localaddr(localname);
  261. sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
  262. port);
  263. }
  264. } else if (!servername.empty()) {
  265. // If only server name is given we will need to try to resolve
  266. // the local address to bind socket to based on remote address.
  267. IOAddress remoteaddr(servername);
  268. sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
  269. port);
  270. }
  271. if (sock <= 0) {
  272. isc_throw(BadValue, "unable to open socket to communicate with "
  273. "DHCP server");
  274. }
  275. // IfaceMgr does not set broadcast option on the socket. We rely
  276. // on CommandOptions object to find out if socket has to have
  277. // broadcast enabled.
  278. if ((options.getIpVersion() == 4) && options.isBroadcast()) {
  279. int broadcast_enable = 1;
  280. int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
  281. &broadcast_enable, sizeof(broadcast_enable));
  282. if (ret < 0) {
  283. isc_throw(InvalidOperation,
  284. "unable to set broadcast option on the socket");
  285. }
  286. }
  287. return(sock);
  288. }
  289. void
  290. TestControl::receivePackets() {
  291. int timeout = 0;
  292. bool receiving = true;
  293. while (receiving) {
  294. Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout);
  295. if (!pkt4) {
  296. receiving = false;
  297. } else {
  298. // TODO: replace this with use of StatsMgr to increase
  299. // number of received packets. This can be done once
  300. // the 1958 ticket is reviewed and checked-in.
  301. std::cout << "Received packet" << std::endl;
  302. }
  303. }
  304. }
  305. void
  306. TestControl::registerOptionFactories4() const {
  307. static bool factories_registered = false;
  308. if (!factories_registered) {
  309. // DHCP_MESSAGE_TYPE option factory.
  310. LibDHCP::OptionFactoryRegister(Option::V4,
  311. DHO_DHCP_MESSAGE_TYPE,
  312. &TestControl::factoryGeneric);
  313. // DHCP_PARAMETER_REQUEST_LIST option factory.
  314. LibDHCP::OptionFactoryRegister(Option::V4,
  315. DHO_DHCP_PARAMETER_REQUEST_LIST,
  316. &TestControl::factoryRequestList4);
  317. }
  318. factories_registered = true;
  319. }
  320. void
  321. TestControl::registerOptionFactories6() const {
  322. static bool factories_registered = false;
  323. if (!factories_registered) {
  324. LibDHCP::OptionFactoryRegister(Option::V6,
  325. D6O_ELAPSED_TIME,
  326. &TestControl::factoryElapsedTimeSolicit6);
  327. LibDHCP::OptionFactoryRegister(Option::V6,
  328. D6O_RAPID_COMMIT,
  329. &TestControl::factoryRapidCommit6);
  330. LibDHCP::OptionFactoryRegister(Option::V6,
  331. D6O_ORO,
  332. &TestControl::factoryOptionRequestOption6);
  333. LibDHCP::OptionFactoryRegister(Option::V6,
  334. D6O_CLIENTID,
  335. &TestControl::factoryGeneric);
  336. LibDHCP::OptionFactoryRegister(Option::V6,
  337. D6O_IA_NA,
  338. &TestControl::factoryIana6);
  339. }
  340. factories_registered = true;
  341. }
  342. void
  343. TestControl::registerOptionFactories() const {
  344. CommandOptions& options = CommandOptions::instance();
  345. switch(options.getIpVersion()) {
  346. case 4:
  347. registerOptionFactories4();
  348. break;
  349. case 6:
  350. registerOptionFactories6();
  351. break;
  352. default:
  353. isc_throw(InvalidOperation, "command line options have to be parsed "
  354. "before DHCP option factories can be registered");
  355. }
  356. }
  357. void
  358. TestControl::run() {
  359. sent_packets_0_ = 0;
  360. sent_packets_1_ = 0;
  361. CommandOptions& options = CommandOptions::instance();
  362. // Ip version is not set ONLY in case the command options
  363. // were not parsed. This surely means that parse() function
  364. // was not called prior to starting the test. This is fatal
  365. // error.
  366. if (options.getIpVersion() == 0) {
  367. isc_throw(InvalidOperation,
  368. "command options must be parsed before running a test");
  369. }
  370. registerOptionFactories();
  371. TestControlSocket socket(openSocket());
  372. uint64_t packets_sent = 0;
  373. for (;;) {
  374. updateSendDue();
  375. if (checkExitConditions()) {
  376. break;
  377. }
  378. uint64_t packets_due = getNextExchangesNum();
  379. receivePackets();
  380. for (uint64_t i = packets_due; i > 0; --i) {
  381. if (options.getIpVersion() == 4) {
  382. sendDiscover4(socket);
  383. } else {
  384. sendSolicit6(socket);
  385. }
  386. ++packets_sent;
  387. cout << "Packets sent " << packets_sent << endl;
  388. }
  389. }
  390. }
  391. void
  392. TestControl::sendDiscover4(const TestControlSocket& socket) {
  393. ++sent_packets_0_;
  394. last_sent_ = microsec_clock::universal_time();
  395. // Generate the MAC address to be passed in the packet.
  396. std::vector<uint8_t> mac_address = generateMacAddress();
  397. // Generate trasnaction id to be set for the new exchange.
  398. const uint32_t transid = static_cast<uint32_t>(random());
  399. boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
  400. if (!pkt4) {
  401. isc_throw(Unexpected, "failed to create DISCOVER packet");
  402. }
  403. // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
  404. OptionBuffer buf_msg_type;
  405. buf_msg_type.push_back(DHCPDISCOVER);
  406. pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
  407. buf_msg_type));
  408. pkt4->addOption(Option::factory(Option::V4,
  409. DHO_DHCP_PARAMETER_REQUEST_LIST));
  410. // Set client's and server's ports as well as server's address,
  411. // and local (relay) address.
  412. setDefaults4(socket, pkt4);
  413. pkt4->pack();
  414. IfaceMgr::instance().send(pkt4);
  415. }
  416. void
  417. TestControl::sendSolicit6(const TestControlSocket& socket) {
  418. ++sent_packets_0_;
  419. last_sent_ = microsec_clock::universal_time();
  420. // Generate the MAC address to be passed in the packet.
  421. std::vector<uint8_t> mac_address = generateMacAddress();
  422. // Generate DUID to be passed to the packet
  423. std::vector<uint8_t> duid = generateDuid();
  424. // Generate trasnaction id to be set for the new exchange.
  425. const uint32_t transid = static_cast<uint32_t>(random());
  426. boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
  427. if (!pkt6) {
  428. isc_throw(Unexpected, "failed to create SOLICIT packet");
  429. }
  430. pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
  431. pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
  432. pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
  433. pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
  434. pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
  435. setDefaults6(socket, pkt6);
  436. pkt6->pack();
  437. IfaceMgr::instance().send(pkt6);
  438. }
  439. void
  440. TestControl::setDefaults4(const TestControlSocket& socket,
  441. const boost::shared_ptr<Pkt4>& pkt) {
  442. CommandOptions& options = CommandOptions::instance();
  443. // Interface name.
  444. pkt->setIface(socket.getIface());
  445. // Local client's port (68)
  446. pkt->setLocalPort(DHCP4_CLIENT_PORT);
  447. // Server's port (67)
  448. pkt->setRemotePort(DHCP4_SERVER_PORT);
  449. // The remote server's name or IP.
  450. pkt->setRemoteAddr(IOAddress(options.getServerName()));
  451. // Set relay (GIADDR) address to local address.
  452. pkt->setGiaddr(IOAddress(socket.getAddress()));
  453. // Pretend that we have one relay (which is us).
  454. pkt->setHops(1);
  455. }
  456. void
  457. TestControl::setDefaults6(const TestControlSocket& socket,
  458. const boost::shared_ptr<Pkt6>& pkt) {
  459. CommandOptions& options = CommandOptions::instance();
  460. // Interface name.
  461. pkt->setIface(socket.getIface());
  462. // Local client's port (547)
  463. pkt->setLocalPort(DHCP6_CLIENT_PORT);
  464. // Server's port (548)
  465. pkt->setRemotePort(DHCP6_SERVER_PORT);
  466. // The remote server's name or IP.
  467. pkt->setRemoteAddr(IOAddress(options.getServerName()));
  468. }
  469. void
  470. TestControl::updateSendDue() {
  471. // If default constructor was called, this should not happen but
  472. // if somebody has changed default constructor it is better to
  473. // keep this check.
  474. if (last_sent_.is_not_a_date_time()) {
  475. isc_throw(Unexpected, "time of last sent packet not initialized");
  476. }
  477. // Get the expected exchange rate.
  478. CommandOptions& options = CommandOptions::instance();
  479. int rate = options.getRate();
  480. // If rate was not specified we will wait just one clock tick to
  481. // send next packet. This simulates best effort conditions.
  482. long duration = 1;
  483. if (rate != 0) {
  484. // We use number of ticks instead of nanoseconds because
  485. // nanosecond resolution may not be available on some
  486. // machines. Number of ticks guarantees the highest possible
  487. // timer resolution.
  488. duration = time_duration::ticks_per_second() / rate;
  489. }
  490. // Calculate due time to initate next chunk of exchanges.
  491. send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
  492. }
  493. } // namespace perfdhcp
  494. } // namespace isc