test_control.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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::factoryGeneric4(Option::Universe u,
  88. uint16_t type,
  89. const OptionBuffer& buf) {
  90. OptionPtr opt(new Option(u, type, buf));
  91. return opt;
  92. }
  93. OptionPtr
  94. TestControl::factoryRequestList4(Option::Universe u,
  95. uint16_t type,
  96. const OptionBuffer& buf) {
  97. const uint8_t buf_array[] = {
  98. DHO_SUBNET_MASK,
  99. DHO_BROADCAST_ADDRESS,
  100. DHO_TIME_OFFSET,
  101. DHO_ROUTERS,
  102. DHO_DOMAIN_NAME,
  103. DHO_DOMAIN_NAME_SERVERS,
  104. DHO_HOST_NAME
  105. };
  106. OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
  107. OptionPtr opt(new Option(u, type, buf));
  108. opt->setData(buf_with_options.begin(), buf_with_options.end());
  109. return opt;
  110. }
  111. std::vector<uint8_t>
  112. TestControl::generateMacAddress() const {
  113. CommandOptions& options = CommandOptions::instance();
  114. uint32_t clients_num = options.getClientsNum();
  115. if ((clients_num == 0) || (clients_num == 1)) {
  116. return options.getMacPrefix();
  117. }
  118. std::vector<uint8_t> mac_addr(options.getMacPrefix());
  119. if (mac_addr.size() != HW_ETHER_LEN) {
  120. isc_throw(BadValue, "invalid MAC address prefix specified");
  121. }
  122. uint32_t r = random();
  123. r %= clients_num;
  124. for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
  125. it >= mac_addr.begin();
  126. --it) {
  127. (*it) += r;
  128. if (r < 256) {
  129. break;
  130. }
  131. r >>= 8;
  132. }
  133. return mac_addr;
  134. }
  135. uint64_t
  136. TestControl::getNextExchangesNum() const {
  137. CommandOptions& options = CommandOptions::instance();
  138. // Reset number of exchanges.
  139. uint64_t due_exchanges = 0;
  140. // Get current time.
  141. ptime now(microsec_clock::universal_time());
  142. // The due time indicates when we should start sending next chunk
  143. // of packets. If it is already due time, we should calculate
  144. // how many packets to send.
  145. if (now >= send_due_) {
  146. // If rate is specified from the command line we have to
  147. // synchornize with it.
  148. if (options.getRate() != 0) {
  149. time_period period(send_due_, now);
  150. // Null condition should not occur because we
  151. // have checked it in the first if statement but
  152. // let's keep this check just in case.
  153. if (period.is_null()) {
  154. return (0);
  155. }
  156. time_duration duration = period.length();
  157. // due_factor indicates the number of seconds that
  158. // sending next chunk of packets will take.
  159. double due_factor = duration.fractional_seconds() /
  160. time_duration::ticks_per_second();
  161. due_factor += duration.total_seconds();
  162. // Multiplying due_factor by expected rate gives the number
  163. // of exchanges to be initiated.
  164. due_exchanges = static_cast<uint64_t>(due_factor * options.getRate());
  165. // We want to make sure that at least one packet goes out.
  166. due_exchanges += 1;
  167. // We should not exceed aggressivity as it could have been
  168. // restricted from command line.
  169. if (due_exchanges > options.getAggressivity()) {
  170. due_exchanges = options.getAggressivity();
  171. }
  172. } else {
  173. // Rate is not specified so we rely on aggressivity
  174. // which is the number of packets to be sent in
  175. // one chunk.
  176. due_exchanges = options.getAggressivity();
  177. }
  178. return (due_exchanges);
  179. }
  180. return (0);
  181. }
  182. int
  183. TestControl::openSocket() const {
  184. CommandOptions& options = CommandOptions::instance();
  185. std::string localname = options.getLocalName();
  186. std::string servername = options.getServerName();
  187. uint8_t family = AF_INET;
  188. uint16_t port = 67;
  189. int sock = 0;
  190. if (options.getIpVersion() == 6) {
  191. family = AF_INET6;
  192. port = 547;
  193. }
  194. if (!localname.empty()) {
  195. bool is_interface = false;;
  196. try {
  197. sock = IfaceMgr::instance().openSocketFromIface(localname,
  198. port,
  199. family);
  200. is_interface = true;
  201. } catch (...) {
  202. // This is not fatal error. It may be the case that
  203. // parameter given from command line is not interface
  204. // name but local IP address.
  205. }
  206. if (!is_interface) {
  207. IOAddress localaddr(localname);
  208. // We don't catch exception here because parameter given
  209. // must be either interface name or local address. If
  210. // both attempts failed, we want exception to be emited.
  211. sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
  212. port);
  213. }
  214. } else if (!servername.empty()) {
  215. IOAddress remoteaddr(servername);
  216. sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
  217. port);
  218. }
  219. if (sock <= 0) {
  220. isc_throw(BadValue, "unable to open socket to communicate with "
  221. "DHCP server");
  222. }
  223. return(sock);
  224. }
  225. void
  226. TestControl::receivePackets() {
  227. int timeout = 0;
  228. bool receiving = true;
  229. while (receiving) {
  230. Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout);
  231. if (!pkt4) {
  232. receiving = false;
  233. } else {
  234. std::cout << "Received packet" << std::endl;
  235. }
  236. }
  237. }
  238. void
  239. TestControl::registerOptionFactories4() const {
  240. static bool factories_registered = false;
  241. if (!factories_registered) {
  242. LibDHCP::OptionFactoryRegister(Option::V4,
  243. DHO_DHCP_MESSAGE_TYPE,
  244. &TestControl::factoryGeneric4);
  245. LibDHCP::OptionFactoryRegister(Option::V4,
  246. DHO_DHCP_PARAMETER_REQUEST_LIST,
  247. &TestControl::factoryRequestList4);
  248. }
  249. factories_registered = true;
  250. }
  251. void
  252. TestControl::registerOptionFactories6() const {
  253. static bool factories_registered = false;
  254. if (!factories_registered) {
  255. }
  256. factories_registered = true;
  257. }
  258. void
  259. TestControl::registerOptionFactories() const {
  260. CommandOptions& options = CommandOptions::instance();
  261. switch(options.getIpVersion()) {
  262. case 4:
  263. registerOptionFactories4();
  264. break;
  265. case 6:
  266. registerOptionFactories6();
  267. break;
  268. default:
  269. isc_throw(InvalidOperation, "command line options have to be parsed "
  270. "before DHCP option factories can be registered");
  271. }
  272. }
  273. void
  274. TestControl::run() {
  275. sent_packets_0_ = 0;
  276. sent_packets_1_ = 0;
  277. CommandOptions& options = CommandOptions::instance();
  278. // Ip version is not set ONLY in case the command options
  279. // were not parsed. This surely means that parse() function
  280. // was not called prior to starting the test. This is fatal
  281. // error.
  282. if (options.getIpVersion() == 0) {
  283. isc_throw(InvalidOperation,
  284. "command options must be parsed before running a test");
  285. }
  286. registerOptionFactories();
  287. TestControlSocket socket(openSocket());
  288. uint64_t packets_sent = 0;
  289. for (;;) {
  290. updateSendDue();
  291. if (checkExitConditions()) {
  292. break;
  293. }
  294. uint64_t packets_due = getNextExchangesNum();
  295. receivePackets();
  296. for (uint64_t i = packets_due; i > 0; --i) {
  297. sendDiscover4(socket);
  298. ++packets_sent;
  299. cout << "Packets sent " << packets_sent << endl;
  300. }
  301. }
  302. }
  303. void
  304. TestControl::sendDiscover4(const TestControlSocket& socket) {
  305. ++sent_packets_0_;
  306. last_sent_ = microsec_clock::universal_time();
  307. std::vector<uint8_t> mac_address = generateMacAddress();
  308. const uint32_t transid = static_cast<uint32_t>(random());
  309. boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
  310. if (!pkt4) {
  311. isc_throw(Unexpected, "failed to create DISCOVER packet");
  312. }
  313. OptionBuffer buf_msg_type;
  314. buf_msg_type.push_back(DHCPDISCOVER);
  315. pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
  316. buf_msg_type));
  317. pkt4->addOption(Option::factory(Option::V4,
  318. DHO_DHCP_PARAMETER_REQUEST_LIST));
  319. setDefaults4(socket, pkt4);
  320. pkt4->pack();
  321. IfaceMgr::instance().send(pkt4);
  322. }
  323. void
  324. TestControl::setDefaults4(const TestControlSocket &socket,
  325. const boost::shared_ptr<Pkt4>& pkt) {
  326. CommandOptions& options = CommandOptions::instance();
  327. pkt->setIface(socket.getIface());
  328. pkt->setLocalPort(DHCP4_CLIENT_PORT);
  329. pkt->setRemotePort(DHCP4_SERVER_PORT);
  330. pkt->setRemoteAddr(IOAddress(options.getServerName()));
  331. pkt->setGiaddr(IOAddress(socket.getAddress()));
  332. pkt->setHops(1);
  333. }
  334. void
  335. TestControl::updateSendDue() {
  336. // If default constructor was called, this should not happen but
  337. // if somebody has cw/e August 3, 2012hanged default constructor it is better to
  338. // keep this check.
  339. if (last_sent_.is_not_a_date_time()) {
  340. isc_throw(Unexpected, "time of last sent packet not initialized");
  341. }
  342. // Get the expected exchange rate.
  343. CommandOptions& options = CommandOptions::instance();
  344. int rate = options.getRate();
  345. // If rate was not specified we will wait just one clock tick to
  346. // send next packet. This simulates best effort conditions.
  347. long duration = 1;
  348. if (rate != 0) {
  349. // We use number of ticks instead of nanoseconds because
  350. // nanosecond resolution may not be available on some
  351. // machines. Number of ticks guarantees the highest possible
  352. // timer resolution.
  353. duration = time_duration::ticks_per_second() / rate;
  354. }
  355. // Calculate due time to initate next chunk of exchanges.
  356. send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
  357. }
  358. } // namespace perfdhcp
  359. } // namespace isc