test_control.cc 13 KB

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