Kea  1.5.0
test_control.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <asiolink/io_address.h>
11 #include <dhcp/libdhcp++.h>
12 #include <dhcp/iface_mgr.h>
13 #include <dhcp/dhcp4.h>
14 #include <dhcp/option6_ia.h>
16 #include "test_control.h"
17 #include "command_options.h"
18 #include "perf_pkt4.h"
19 #include "perf_pkt6.h"
20 
21 #include <boost/date_time/posix_time/posix_time.hpp>
22 #include <boost/foreach.hpp>
23 
24 #include <algorithm>
25 #include <fstream>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <unistd.h>
30 #include <signal.h>
31 #include <sstream>
32 #include <sys/wait.h>
33 
34 using namespace std;
35 using namespace boost::posix_time;
36 using namespace isc;
37 using namespace isc::dhcp;
38 using namespace isc::asiolink;
39 
40 namespace isc {
41 namespace perfdhcp {
42 
43 bool TestControl::interrupted_ = false;
44 
45 ptime late_exit_target_time_ = ptime(not_a_date_time);
46 
47 bool
48 TestControl::hasLateExitCommenced() const {
49  return !late_exit_target_time_.is_not_a_date_time();
50 }
51 
52 bool
53 TestControl::waitToExit() const {
54  static ptime exit_time = ptime(not_a_date_time);
55  CommandOptions& options = CommandOptions::instance();
56  uint32_t wait_time = options.getExitWaitTime();
57 
58  // If we care and not all packets are in yet
59  if (wait_time && !haveAllPacketsBeenReceived()) {
60  const ptime now = microsec_clock::universal_time();
61 
62  // Init the end time if it hasn't started yet
63  if (exit_time.is_not_a_date_time()) {
64  exit_time = now + time_duration(microseconds(wait_time));
65  }
66 
67  // If we're not at end time yet, return true
68  return (now < exit_time);
69  }
70 
71  // No need to wait, return false;
72  return (false);
73 }
74 
75 bool
76 TestControl::haveAllPacketsBeenReceived() const {
77  const CommandOptions& options = CommandOptions::instance();
78  const uint8_t& ipversion = options.getIpVersion();
79  const std::vector<int>& num_request = options.getNumRequests();
80  const size_t& num_request_size = num_request.size();
81 
82  if (num_request_size == 0) {
83  return false;
84  }
85 
86  uint32_t responses = 0;
87  uint32_t requests = num_request[0];
88  if (num_request_size >= 2) {
89  requests += num_request[1];
90  }
91 
92  if (ipversion == 4) {
93  responses = stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) +
94  stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_RA);
95  } else {
96  responses = stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) +
97  stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_RR);
98  }
99 
100  return (responses == requests);
101 }
102 
103 TestControl::TestControlSocket::TestControlSocket(const int socket) :
104  SocketInfo(asiolink::IOAddress("127.0.0.1"), 0, socket),
105  ifindex_(0), valid_(true) {
106  try {
107  initSocketData();
108  } catch (const Exception&) {
109  valid_ = false;
110  }
111 }
112 
114  IfacePtr iface = IfaceMgr::instance().getIface(ifindex_);
115  if (iface) {
116  iface->delSocket(sockfd_);
117  }
118 }
119 
120 void
121 TestControl::TestControlSocket::initSocketData() {
122  BOOST_FOREACH(IfacePtr iface, IfaceMgr::instance().getIfaces()) {
123  BOOST_FOREACH(SocketInfo s, iface->getSockets()) {
124  if (s.sockfd_ == sockfd_) {
125  ifindex_ = iface->getIndex();
126  addr_ = s.addr_;
127  return;
128  }
129  }
130  }
131  isc_throw(BadValue, "interface for for specified socket "
132  "descriptor not found");
133 }
134 
137  static TestControl test_control;
138  return (test_control);
139 }
140 
142  : number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) {
143  reset();
144 }
145 
146 void
148  // If diagnostics is disabled, there is no need to log late sent messages.
149  // If it is enabled and the rate control object indicates that the last
150  // sent message was late, bump up the counter in Stats Manager.
151  if (rate_control.isLateSent() && testDiags('i')) {
153  if (options.getIpVersion() == 4) {
154  stats_mgr4_->incrementCounter("latesend");
155  } else if (options.getIpVersion() == 6) {
156  stats_mgr6_->incrementCounter("latesend");
157  }
158  }
159 }
160 
161 void
164  // When Renews are not sent, Reply packets are not cached so there
165  // is nothing to do.
166  if (options.getRenewRate() == 0) {
167  return;
168  }
169 
170  static boost::posix_time::ptime last_clean =
171  microsec_clock::universal_time();
172 
173  // Check how much time has passed since last cleanup.
174  time_period time_since_clean(last_clean,
175  microsec_clock::universal_time());
176  // Cleanup every 1 second.
177  if (time_since_clean.length().total_seconds() >= 1) {
178  // Calculate how many cached packets to remove. Actually we could
179  // just leave enough packets to handle Renews for 1 second but
180  // since we want to randomize leases to be renewed so leave 5
181  // times more packets to randomize from.
182  // @todo The cache size might be controlled from the command line.
183  if (reply_storage_.size() > 5 * options.getRenewRate()) {
184  reply_storage_.clear(reply_storage_.size() -
185  5 * options.getRenewRate());
186  }
187  // Remember when we performed a cleanup for the last time.
188  // We want to do the next cleanup not earlier than in one second.
189  last_clean = microsec_clock::universal_time();
190  }
191 }
192 
193 void
194 TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
195  if (!pkt_from || !pkt_to) {
196  isc_throw(BadValue, "NULL pointers must not be specified as arguments"
197  " for the copyIaOptions function");
198  }
199  // IA_NA
200  if (CommandOptions::instance().getLeaseType()
202  OptionPtr option = pkt_from->getOption(D6O_IA_NA);
203  if (!option) {
204  isc_throw(OptionNotFound, "IA_NA option not found in the"
205  " server's response");
206  }
207  pkt_to->addOption(option);
208  }
209  // IA_PD
210  if (CommandOptions::instance().getLeaseType()
212  OptionPtr option = pkt_from->getOption(D6O_IA_PD);
213  if (!option) {
214  isc_throw(OptionNotFound, "IA_PD option not found in the"
215  " server's response");
216  }
217  pkt_to->addOption(option);
218  }
219 
220 
221 }
222 
223 std::string
224 TestControl::byte2Hex(const uint8_t b) const {
225  const int b1 = b / 16;
226  const int b0 = b % 16;
227  ostringstream stream;
228  stream << std::hex << b1 << b0 << std::dec;
229  return (stream.str());
230 }
231 
232 bool
234  if (interrupted_) {
235  return (true);
236  }
238  bool test_period_reached = false;
239  // Check if test period passed.
240  if (options.getPeriod() != 0) {
241  if (options.getIpVersion() == 4) {
242  time_period period(stats_mgr4_->getTestPeriod());
243  if (period.length().total_seconds() >= options.getPeriod()) {
244  test_period_reached = true;
245  }
246  } else if (options.getIpVersion() == 6) {
247  time_period period = stats_mgr6_->getTestPeriod();
248  if (period.length().total_seconds() >= options.getPeriod()) {
249  test_period_reached = true;
250  }
251  }
252  }
253  if (test_period_reached) {
254  if (testDiags('e')) {
255  std::cout << "reached test-period." << std::endl;
256  }
257  if (!waitToExit()) {
258  return true;
259  }
260  }
261 
262  bool max_requests = false;
263  // Check if we reached maximum number of DISCOVER/SOLICIT sent.
264  if (options.getNumRequests().size() > 0) {
265  if (options.getIpVersion() == 4) {
267  options.getNumRequests()[0]) {
268  max_requests = true;
269  }
270  } else if (options.getIpVersion() == 6) {
271  if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
272  options.getNumRequests()[0]) {
273  max_requests = true;
274  }
275  }
276  }
277  // Check if we reached maximum number REQUEST packets.
278  if (options.getNumRequests().size() > 1) {
279  if (options.getIpVersion() == 4) {
280  if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
281  options.getNumRequests()[1]) {
282  max_requests = true;
283  }
284  } else if (options.getIpVersion() == 6) {
285  if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
286  options.getNumRequests()[1]) {
287  max_requests = true;
288  }
289  }
290  }
291  if (max_requests) {
292  if (testDiags('e')) {
293  std::cout << "Reached max requests limit." << std::endl;
294  }
295  if (!waitToExit()) {
296  return true;
297  }
298  }
299 
300  // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
301  bool max_drops = false;
302  if (options.getMaxDrop().size() > 0) {
303  if (options.getIpVersion() == 4) {
304  if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
305  options.getMaxDrop()[0]) {
306  max_drops = true;
307  }
308  } else if (options.getIpVersion() == 6) {
309  if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
310  options.getMaxDrop()[0]) {
311  max_drops = true;
312  }
313  }
314  }
315  // Check if we reached maximum number of drops of ACK/REPLY packets.
316  if (options.getMaxDrop().size() > 1) {
317  if (options.getIpVersion() == 4) {
318  if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
319  options.getMaxDrop()[1]) {
320  max_drops = true;
321  }
322  } else if (options.getIpVersion() == 6) {
323  if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
324  options.getMaxDrop()[1]) {
325  max_drops = true;
326  }
327  }
328  }
329  if (max_drops) {
330  if (testDiags('e')) {
331  std::cout << "Reached maximum drops number." << std::endl;
332  }
333  if (!waitToExit()) {
334  return true;
335  }
336  }
337 
338  // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
339  bool max_pdrops = false;
340  if (options.getMaxDropPercentage().size() > 0) {
341  if (options.getIpVersion() == 4) {
342  if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
343  ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
344  stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
345  options.getMaxDropPercentage()[0])) {
346  max_pdrops = true;
347 
348  }
349  } else if (options.getIpVersion() == 6) {
350  if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
351  ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
352  stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
353  options.getMaxDropPercentage()[0])) {
354  max_pdrops = true;
355  }
356  }
357  }
358  // Check if we reached maximum drops percentage of ACK/REPLY packets.
359  if (options.getMaxDropPercentage().size() > 1) {
360  if (options.getIpVersion() == 4) {
361  if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
362  ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
363  stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
364  options.getMaxDropPercentage()[1])) {
365  max_pdrops = true;
366  }
367  } else if (options.getIpVersion() == 6) {
368  if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
369  ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
370  stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
371  options.getMaxDropPercentage()[1])) {
372  max_pdrops = true;
373  }
374  }
375  }
376  if (max_pdrops) {
377  if (testDiags('e')) {
378  std::cout << "Reached maximum percentage of drops." << std::endl;
379  }
380  if (!waitToExit()) {
381  return true;
382  }
383  }
384  return (false);
385 }
386 
387 Pkt4Ptr
389  if (!ack) {
390  isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
391  " null DHCPACK message");
392  } else if (ack->getYiaddr().isV4Zero()) {
393  isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
394  " DHCPACK message containing yiaddr of 0");
395  }
397  msg->setCiaddr(ack->getYiaddr());
398  msg->setHWAddr(ack->getHWAddr());
399  msg->addOption(generateClientId(msg->getHWAddr()));
400  return (msg);
401 }
402 
403 Pkt6Ptr
404 TestControl::createMessageFromReply(const uint16_t msg_type,
405  const dhcp::Pkt6Ptr& reply) {
406  // Restrict messages to Release and Renew.
407  if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
408  isc_throw(isc::BadValue, "invalid message type " << msg_type
409  << " to be created from Reply, expected DHCPV6_RENEW or"
410  " DHCPV6_RELEASE");
411  }
412  // Get the string representation of the message - to be used for error
413  // logging purposes.
414  const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
415  // Reply message must be specified.
416  if (!reply) {
417  isc_throw(isc::BadValue, "Unable to create " << msg_type_str
418  << " message from the Reply message because the instance of"
419  " the Reply message is NULL");
420  }
421 
422  Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
423  // Client id.
424  OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
425  if (!opt_clientid) {
426  isc_throw(isc::Unexpected, "failed to create " << msg_type_str
427  << " message because client id option has not been found"
428  " in the Reply message");
429  }
430  msg->addOption(opt_clientid);
431  // Server id.
432  OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
433  if (!opt_serverid) {
434  isc_throw(isc::Unexpected, "failed to create " << msg_type_str
435  << " because server id option has not been found in the"
436  " Reply message");
437  }
438  msg->addOption(opt_serverid);
439  copyIaOptions(reply, msg);
440  return (msg);
441 }
442 
443 OptionPtr
445  const OptionBuffer& buf) {
446  if (buf.size() == 2) {
447  return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)));
448  } else if (buf.size() == 0) {
449  return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
450  OptionBuffer(2, 0))));
451  }
453  "elapsed time option buffer size has to be 0 or 2");
454 }
455 
456 OptionPtr
458  const OptionBuffer& buf) {
459  OptionPtr opt(new Option(u, type, buf));
460  return (opt);
461 }
462 
463 OptionPtr
465  const OptionBuffer& buf) {
466  // @todo allow different values of T1, T2 and IAID.
467  const uint8_t buf_array[] = {
468  0, 0, 0, 1, // IAID = 1
469  0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
470  0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
471  };
472  OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array));
473  for (size_t i = 0; i < buf.size(); ++i) {
474  buf_ia_na.push_back(buf[i]);
475  }
476  return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
477 }
478 
479 OptionPtr
481  const OptionBuffer& buf) {
482  // @todo allow different values of T1, T2 and IAID.
483  static const uint8_t buf_array[] = {
484  0, 0, 0, 1, // IAID = 1
485  0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
486  0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
487  };
488  OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
489  // Append sub-options to IA_PD.
490  buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
491  return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
492 }
493 
494 
495 OptionPtr
497  const OptionBuffer&) {
498  return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())));
499 }
500 
501 OptionPtr
503  uint16_t,
504  const OptionBuffer&) {
505  const uint8_t buf_array[] = {
506  0, D6O_NAME_SERVERS,
508  };
509  OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
510  return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)));
511 }
512 
513 
514 OptionPtr
516  uint16_t type,
517  const OptionBuffer& buf) {
518  const uint8_t buf_array[] = {
522  DHO_ROUTERS,
526  };
527 
528  OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
529  OptionPtr opt(new Option(u, type, buf));
530  opt->setData(buf_with_options.begin(), buf_with_options.end());
531  return (opt);
532 }
533 
534 std::vector<uint8_t>
535 TestControl::generateMacAddress(uint8_t& randomized) {
537 
538  const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
539  // if we are using the -M option return a random one from the list...
540  if (macs.size() > 0) {
541  uint16_t r = number_generator_();
542  if (r >= macs.size()) {
543  r = 0;
544  }
545  return macs[r];
546 
547  } else {
548  // ... otherwise use the standard behavior
549  uint32_t clients_num = options.getClientsNum();
550  if (clients_num < 2) {
551  return (options.getMacTemplate());
552  }
553  // Get the base MAC address. We are going to randomize part of it.
554  std::vector<uint8_t> mac_addr(options.getMacTemplate());
555  if (mac_addr.size() != HW_ETHER_LEN) {
556  isc_throw(BadValue, "invalid MAC address template specified");
557  }
558  uint32_t r = macaddr_gen_->generate();
559  randomized = 0;
560  // Randomize MAC address octets.
561  for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
562  it >= mac_addr.begin();
563  --it) {
564  // Add the random value to the current octet.
565  (*it) += r;
566  ++randomized;
567  if (r < 256) {
568  // If we are here it means that there is no sense
569  // to randomize the remaining octets of MAC address
570  // because the following bytes of random value
571  // are zero and it will have no effect.
572  break;
573  }
574  // Randomize the next octet with the following
575  // byte of random value.
576  r >>= 8;
577  }
578  return (mac_addr);
579  }
580 }
581 
582 OptionPtr
584  std::vector<uint8_t> client_id(1, static_cast<uint8_t>(hwaddr->htype_));
585  client_id.insert(client_id.end(), hwaddr->hwaddr_.begin(),
586  hwaddr->hwaddr_.end());
587  return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
588  client_id)));
589 }
590 
591 std::vector<uint8_t>
592 TestControl::generateDuid(uint8_t& randomized) {
594  std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
595  const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
596  // pick a random mac address if we are using option -M..
597  if (macs.size() > 0) {
598  uint16_t r = number_generator_();
599  if (r >= macs.size()) {
600  r = 0;
601  }
602  std::vector<uint8_t> mac = macs[r];
603  // DUID_LL is in this format
604  // 0 1 2 3
605  // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
606  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
607  // | 3 | hardware type (16 bits) |
608  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609  // . .
610  // . link-layer address (variable length) .
611  // . .
612  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
613 
614  // No C++11 so initializer list support, building a vector<uint8_t> is a
615  // pain...
616  uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
617  // copy duid_ll array into the vector
618  std::vector<uint8_t> duid(duid_ll,
619  duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
620  // put the mac address bytes at the end
621  std::copy(mac.begin(), mac.end(), duid.begin() + 4);
622  return (duid);
623  } else {
624  uint32_t clients_num = options.getClientsNum();
625  if ((clients_num == 0) || (clients_num == 1)) {
626  return (options.getDuidTemplate());
627  }
628  // Get the base DUID. We are going to randomize part of it.
629  std::vector<uint8_t> duid(options.getDuidTemplate());
630  // @todo: add support for DUIDs of different sizes.
631  duid.resize(duid.size());
632  std::copy(mac_addr.begin(), mac_addr.end(),
633  duid.begin() + duid.size() - mac_addr.size());
634  return (duid);
635  }
636 }
637 
638 uint32_t
641  ptime now(microsec_clock::universal_time());
642  // Check that we haven't passed the moment to send the next set of
643  // packets.
644  if (now >= basic_rate_control_.getDue() ||
645  (options.getRenewRate() != 0 && now >= renew_rate_control_.getDue()) ||
646  (options.getReleaseRate() != 0 &&
647  now >= release_rate_control_.getDue())) {
648  return (0);
649  }
650 
651  // Let's assume that the due time for Solicit is the soonest.
652  ptime due = basic_rate_control_.getDue();
653  // If we are sending Renews and due time for Renew occurs sooner,
654  // set the due time to Renew due time.
655  if ((options.getRenewRate()) != 0 && (renew_rate_control_.getDue() < due)) {
656  due = renew_rate_control_.getDue();
657  }
658  // If we are sending Releases and the due time for Release occurs
659  // sooner than the current due time, let's use the due for Releases.
660  if ((options.getReleaseRate() != 0) &&
661  (release_rate_control_.getDue() < due)) {
663  }
664  // Return the timeout in microseconds.
665  return (time_period(now, due).length().total_microseconds());
666 }
667 
668 int
670  int elp_offset = CommandOptions::instance().getIpVersion() == 4 ?
671  DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
674  }
675  return (elp_offset);
676 }
677 
678 template<class T>
679 uint32_t
680 TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
681  using namespace boost::posix_time;
682  ptime pkt1_time = pkt1->getTimestamp();
683  ptime pkt2_time = pkt2->getTimestamp();
684  if (pkt1_time.is_not_a_date_time() ||
685  pkt2_time.is_not_a_date_time()) {
686  isc_throw(InvalidOperation, "packet timestamp not set");;
687  }
688  time_period elapsed_period(pkt1_time, pkt2_time);
689  return (elapsed_period.is_null() ? 0 :
690  elapsed_period.length().total_milliseconds());
691 }
692 
693 int
694 TestControl::getRandomOffset(const int arg_idx) const {
695  int rand_offset = CommandOptions::instance().getIpVersion() == 4 ?
696  DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
697  if (CommandOptions::instance().getRandomOffset().size() > arg_idx) {
698  rand_offset = CommandOptions::instance().getRandomOffset()[arg_idx];
699  }
700  return (rand_offset);
701 }
702 
703 int
705  int rip_offset = CommandOptions::instance().getIpVersion() == 4 ?
706  DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
709  }
710  return (rip_offset);
711 }
712 
713 uint64_t
715  uint8_t ip_version = CommandOptions::instance().getIpVersion();
716  if (ip_version == 4) {
717  return (stats_mgr4_->getRcvdPacketsNum(xchg_type));
718  }
719  return (stats_mgr6_->
720  getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
721 }
722 
723 uint64_t
725  uint8_t ip_version = CommandOptions::instance().getIpVersion();
726  if (ip_version == 4) {
727  return (stats_mgr4_->getSentPacketsNum(xchg_type));
728  }
729  return (stats_mgr6_->
730  getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
731 }
732 
733 int
735  int srvid_offset = CommandOptions::instance().getIpVersion() == 4 ?
736  DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
738  srvid_offset = CommandOptions::instance().getServerIdOffset();
739  }
740  return (srvid_offset);
741 }
742 
744 TestControl::getTemplateBuffer(const size_t idx) const {
745  if (template_buffers_.size() > idx) {
746  return (template_buffers_[idx]);
747  }
748  isc_throw(OutOfRange, "invalid buffer index");
749 }
750 
751 int
752 TestControl::getTransactionIdOffset(const int arg_idx) const {
753  int xid_offset = CommandOptions::instance().getIpVersion() == 4 ?
754  DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
755  if (CommandOptions::instance().getTransactionIdOffset().size() > arg_idx) {
756  xid_offset = CommandOptions::instance().getTransactionIdOffset()[arg_idx];
757  }
758  return (xid_offset);
759 }
760 
761 void
763  int status = 0;
764  while (wait3(&status, WNOHANG, NULL) > 0) {
765  // continue
766  }
767 }
768 
769 void
771  interrupted_ = true;
772 }
773 
774 void
776  template_packets_v4_.clear();
777  template_packets_v6_.clear();
778  template_buffers_.clear();
780  std::vector<std::string> template_files = options.getTemplateFiles();
781  for (std::vector<std::string>::const_iterator it = template_files.begin();
782  it != template_files.end(); ++it) {
783  readPacketTemplate(*it);
784  }
785 }
786 
787 void
790  // Check if packet archive mode is required. If user
791  // requested diagnostics option -x t we have to enable
792  // it so as StatsMgr preserves all packets.
793  const bool archive_mode = testDiags('t') ? true : false;
794  if (options.getIpVersion() == 4) {
795  stats_mgr4_.reset();
796  stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4(archive_mode));
797  stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO,
798  options.getDropTime()[0]);
799  if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
800  stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA,
801  options.getDropTime()[1]);
802  }
803  if (options.getRenewRate() != 0) {
804  stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RNA);
805  }
806 
807  } else if (options.getIpVersion() == 6) {
808  stats_mgr6_.reset();
809  stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6(archive_mode));
810  stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA,
811  options.getDropTime()[0]);
812  if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
813  stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR,
814  options.getDropTime()[1]);
815  }
816  if (options.getRenewRate() != 0) {
817  stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RN);
818  }
819  if (options.getReleaseRate() != 0) {
820  stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RL);
821  }
822  }
823  if (testDiags('i')) {
824  if (options.getIpVersion() == 4) {
825  stats_mgr4_->addCustomCounter("latesend", "Late sent packets");
826  stats_mgr4_->addCustomCounter("shortwait", "Short waits for packets");
827  stats_mgr4_->addCustomCounter("multircvd", "Multiple packets receives");
828  stats_mgr4_->addCustomCounter("latercvd", "Late received packets");
829  } else if (options.getIpVersion() == 6) {
830  stats_mgr6_->addCustomCounter("latesend", "Late sent packets");
831  stats_mgr6_->addCustomCounter("shortwait", "Short waits for packets");
832  stats_mgr6_->addCustomCounter("multircvd", "Multiple packets receives");
833  stats_mgr6_->addCustomCounter("latercvd", "Late received packets");
834  }
835  }
836 }
837 
838 int
841  std::string localname = options.getLocalName();
842  std::string servername = options.getServerName();
843  uint16_t port = options.getLocalPort();
844  int sock = 0;
845 
846  uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
847  IOAddress remoteaddr(servername);
848 
849  // Check for mismatch between IP option and server address
850  if (family != remoteaddr.getFamily()) {
852  "Values for IP version: " <<
853  static_cast<unsigned int>(options.getIpVersion()) <<
854  " and server address: " << servername << " are mismatched.");
855  }
856 
857  if (port == 0) {
858  if (family == AF_INET6) {
859  // need server port (547) because the server is acting as a relay agent
860  port = DHCP6_CLIENT_PORT;
861  // if acting as a relay agent change port.
862  if (options.isUseRelayedV6()) {
863  port = DHCP6_SERVER_PORT;
864  }
865  } else if (options.getIpVersion() == 4) {
866  port = 67; // TODO: find out why port 68 is wrong here.
867  }
868  }
869 
870  // Local name is specified along with '-l' option.
871  // It may point to interface name or local address.
872  if (!localname.empty()) {
873  // CommandOptions should be already aware whether local name
874  // is interface name or address because it uses IfaceMgr to
875  // scan interfaces and get's their names.
876  if (options.isInterface()) {
877  sock = IfaceMgr::instance().openSocketFromIface(localname,
878  port,
879  family);
880  } else {
881  IOAddress localaddr(localname);
882  sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
883  port);
884  }
885  } else if (!servername.empty()) {
886  // If only server name is given we will need to try to resolve
887  // the local address to bind socket to based on remote address.
888  sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
889  port);
890  }
891  if (sock <= 0) {
892  isc_throw(BadValue, "unable to open socket to communicate with "
893  "DHCP server");
894  }
895 
896  // IfaceMgr does not set broadcast option on the socket. We rely
897  // on CommandOptions object to find out if socket has to have
898  // broadcast enabled.
899  if ((options.getIpVersion() == 4) && options.isBroadcast()) {
900  int broadcast_enable = 1;
901  int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
902  &broadcast_enable, sizeof(broadcast_enable));
903  if (ret < 0) {
905  "unable to set broadcast option on the socket");
906  }
907  } else if (options.getIpVersion() == 6) {
908  // If remote address is multicast we need to enable it on
909  // the socket that has been created.
910  if (remoteaddr.isV6Multicast()) {
911  int hops = 1;
912  int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
913  &hops, sizeof(hops));
914  // If user specified interface name with '-l' the
915  // IPV6_MULTICAST_IF has to be set.
916  if ((ret >= 0) && options.isInterface()) {
917  IfacePtr iface =
918  IfaceMgr::instance().getIface(options.getLocalName());
919  if (iface == NULL) {
920  isc_throw(Unexpected, "unknown interface "
921  << options.getLocalName());
922  }
923  int idx = iface->getIndex();
924  ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
925  &idx, sizeof(idx));
926  }
927  if (ret < 0) {
929  "unable to enable multicast on socket " << sock
930  << ". errno = " << errno);
931  }
932  }
933  }
934 
935  return (sock);
936 }
937 
938 void
940  const uint64_t packets_num,
941  const bool preload /* = false */) {
943  for (uint64_t i = packets_num; i > 0; --i) {
944  if (options.getIpVersion() == 4) {
945  // No template packets means that no -T option was specified.
946  // We have to build packets ourselves.
947  if (template_buffers_.empty()) {
948  sendDiscover4(socket, preload);
949  } else {
950  // @todo add defines for packet type index that can be
951  // used to access template_buffers_.
952  sendDiscover4(socket, template_buffers_[0], preload);
953  }
954  } else {
955  // No template packets means that no -T option was specified.
956  // We have to build packets ourselves.
957  if (template_buffers_.empty()) {
958  sendSolicit6(socket, preload);
959  } else {
960  // @todo add defines for packet type index that can be
961  // used to access template_buffers_.
962  sendSolicit6(socket, template_buffers_[0], preload);
963  }
964  }
965  // If we preload server we don't want to receive any packets.
966  if (!preload) {
967  uint64_t latercvd = receivePackets(socket);
968  if (testDiags('i')) {
969  if (options.getIpVersion() == 4) {
970  stats_mgr4_->incrementCounter("latercvd", latercvd);
971  } else if (options.getIpVersion() == 6) {
972  stats_mgr6_->incrementCounter("latercvd", latercvd);
973  }
974  }
975  }
976  }
977 }
978 
979 uint64_t
981  const uint64_t msg_num) {
982  for (uint64_t i = 0; i < msg_num; ++i) {
983  if (!sendRequestFromAck(socket)) {
984  return (i);
985  }
986  }
987  return (msg_num);
988 }
989 
990 uint64_t
992  const uint32_t msg_type,
993  const uint64_t msg_num) {
994  for (uint64_t i = 0; i < msg_num; ++i) {
995  if (!sendMessageFromReply(msg_type, socket)) {
996  return (i);
997  }
998  }
999  return (msg_num);
1000 }
1001 
1002 void
1005  if (testDiags('a')) {
1006  // Print all command line parameters.
1007  options.printCommandLine();
1008  // Print MAC and DUID.
1009  std::cout << "Set MAC to " << vector2Hex(options.getMacTemplate(), "::")
1010  << std::endl;
1011  if (options.getDuidTemplate().size() > 0) {
1012  std::cout << "Set DUID to " << vector2Hex(options.getDuidTemplate()) << std::endl;
1013  }
1014  }
1015 }
1016 
1017 void
1018 TestControl::printTemplate(const uint8_t packet_type) const {
1019  std::string hex_buf;
1020  int arg_idx = 0;
1021  if (CommandOptions::instance().getIpVersion() == 4) {
1022  if (packet_type == DHCPREQUEST) {
1023  arg_idx = 1;
1024  }
1025  std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
1026  template_packets_v4_.find(packet_type);
1027  if ((pkt_it != template_packets_v4_.end()) &&
1028  pkt_it->second) {
1029  const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
1030  const char* out_buf_data =
1031  static_cast<const char*>(out_buf.getData());
1032  std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
1033  hex_buf = vector2Hex(buf);
1034  }
1035  } else if (CommandOptions::instance().getIpVersion() == 6) {
1036  if (packet_type == DHCPV6_REQUEST) {
1037  arg_idx = 1;
1038  }
1039  std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
1040  template_packets_v6_.find(packet_type);
1041  if (pkt_it != template_packets_v6_.end() &&
1042  pkt_it->second) {
1043  const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
1044  const char* out_buf_data =
1045  static_cast<const char*>(out_buf.getData());
1046  std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
1047  hex_buf = vector2Hex(buf);
1048  }
1049  }
1050  std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
1051  std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
1052  if (arg_idx > 0) {
1053  std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
1054  std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
1055  std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
1056  }
1057 
1058  std::cout << "contents: " << std::endl;
1059  int line_len = 32;
1060  int i = 0;
1061  while (line_len == 32) {
1062  if (hex_buf.length() - i < 32) {
1063  line_len = hex_buf.length() - i;
1064  };
1065  if (line_len > 0) {
1066  std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
1067  << " " << hex_buf.substr(i, line_len) << std::endl;
1068  }
1069  i += 32;
1070  }
1071  std::cout << std::endl;
1072 }
1073 
1074 void
1077  if (options.getIpVersion() == 4) {
1080  } else if (options.getIpVersion() == 6) {
1083  }
1084 }
1085 
1086 void
1088  double rate = 0;
1090  std::string exchange_name = "4-way exchanges";
1091  if (options.getIpVersion() == 4) {
1092  StatsMgr4::ExchangeType xchg_type =
1093  options.getExchangeMode() == CommandOptions::DO_SA ?
1095  if (xchg_type == StatsMgr4::XCHG_DO) {
1096  exchange_name = "DISCOVER-OFFER";
1097  }
1098  double duration =
1099  stats_mgr4_->getTestPeriod().length().total_nanoseconds() / 1e9;
1100  rate = stats_mgr4_->getRcvdPacketsNum(xchg_type) / duration;
1101  } else if (options.getIpVersion() == 6) {
1102  StatsMgr6::ExchangeType xchg_type =
1103  options.getExchangeMode() == CommandOptions::DO_SA ?
1105  if (xchg_type == StatsMgr6::XCHG_SA) {
1106  exchange_name = options.isRapidCommit() ? "Solicit-Reply" :
1107  "Solicit-Advertise";
1108  }
1109  double duration =
1110  stats_mgr6_->getTestPeriod().length().total_nanoseconds() / 1e9;
1111  rate = stats_mgr6_->getRcvdPacketsNum(xchg_type) / duration;
1112  }
1113  std::ostringstream s;
1114  s << "***Rate statistics***" << std::endl;
1115  s << "Rate: " << rate << " " << exchange_name << "/second";
1116  if (options.getRate() > 0) {
1117  s << ", expected rate: " << options.getRate() << std::endl;
1118  }
1119 
1120  std::cout << s.str() << std::endl;
1121 }
1122 
1123 void
1126  int delay = options.getReportDelay();
1127  ptime now = microsec_clock::universal_time();
1128  time_period time_since_report(last_report_, now);
1129  if (time_since_report.length().total_seconds() >= delay) {
1130  if (options.getIpVersion() == 4) {
1131  stats_mgr4_->printIntermediateStats();
1132  } else if (options.getIpVersion() == 6) {
1133  stats_mgr6_->printIntermediateStats();
1134  }
1135  last_report_ = now;
1136  }
1137 }
1138 
1139 void
1141  printRate();
1143  if (options.getIpVersion() == 4) {
1144  if (!stats_mgr4_) {
1145  isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1146  "hasn't been initialized");
1147  }
1148  stats_mgr4_->printStats();
1149  if (testDiags('i')) {
1150  stats_mgr4_->printCustomCounters();
1151  }
1152  } else if (options.getIpVersion() == 6) {
1153  if (!stats_mgr6_) {
1154  isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
1155  "hasn't been initialized");
1156  }
1157  stats_mgr6_->printStats();
1158  if (testDiags('i')) {
1159  stats_mgr6_->printCustomCounters();
1160  }
1161  }
1162 }
1163 
1164 std::string
1165 TestControl::vector2Hex(const std::vector<uint8_t>& vec,
1166  const std::string& separator /* ="" */) const {
1167  std::ostringstream stream;
1168  for (std::vector<uint8_t>::const_iterator it = vec.begin();
1169  it != vec.end();
1170  ++it) {
1171  if (it == vec.begin()) {
1172  stream << byte2Hex(*it);
1173  } else {
1174  stream << separator << byte2Hex(*it);
1175  }
1176  }
1177  return (stream.str());
1178 }
1179 
1180 void
1181 TestControl::readPacketTemplate(const std::string& file_name) {
1182  std::ifstream temp_file;
1183  temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
1184  if (!temp_file.is_open()) {
1185  isc_throw(BadValue, "unable to open template file " << file_name);
1186  }
1187  // Read template file contents.
1188  std::streampos temp_size = temp_file.tellg();
1189  if (temp_size == std::streampos(0)) {
1190  temp_file.close();
1191  isc_throw(OutOfRange, "the template file " << file_name << " is empty");
1192  }
1193  temp_file.seekg(0, ios::beg);
1194  std::vector<char> file_contents(temp_size);
1195  temp_file.read(&file_contents[0], temp_size);
1196  temp_file.close();
1197  // Spaces are allowed so we have to strip the contents
1198  // from them. In the same time we want to make sure that
1199  // apart from spaces the file contains hexadecimal digits
1200  // only.
1201  std::vector<char> hex_digits;
1202  for (size_t i = 0; i < file_contents.size(); ++i) {
1203  if (isxdigit(file_contents[i])) {
1204  hex_digits.push_back(file_contents[i]);
1205  } else if (!isxdigit(file_contents[i]) &&
1206  !isspace(file_contents[i])) {
1207  isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
1208  " hexadecimal digit");
1209  }
1210  }
1211  // Expect even number of digits.
1212  if (hex_digits.size() % 2 != 0) {
1213  isc_throw(OutOfRange, "odd number of digits in template file");
1214  } else if (hex_digits.empty()) {
1215  isc_throw(OutOfRange, "template file " << file_name << " is empty");
1216  }
1217  std::vector<uint8_t> binary_stream;
1218  for (size_t i = 0; i < hex_digits.size(); i += 2) {
1219  stringstream s;
1220  s << "0x" << hex_digits[i] << hex_digits[i+1];
1221  int b;
1222  s >> std::hex >> b;
1223  binary_stream.push_back(static_cast<uint8_t>(b));
1224  }
1225  template_buffers_.push_back(binary_stream);
1226 }
1227 
1228 void
1230  const Pkt4Ptr& pkt4) {
1231  if (pkt4->getType() == DHCPOFFER) {
1232  Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
1233  pkt4));
1234  CommandOptions::ExchangeMode xchg_mode =
1236  if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
1237  if (template_buffers_.size() < 2) {
1238  sendRequest4(socket, discover_pkt4, pkt4);
1239  } else {
1240  // @todo add defines for packet type index that can be
1241  // used to access template_buffers_.
1242  sendRequest4(socket, template_buffers_[1], discover_pkt4, pkt4);
1243  }
1244  }
1245  } else if (pkt4->getType() == DHCPACK) {
1246  // If received message is DHCPACK, we have to check if this is
1247  // a response to 4-way exchange. We'll match this packet with
1248  // a DHCPREQUEST sent as part of the 4-way exchanges.
1249  if (stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4)) {
1250  // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
1251  // So, we may need to keep this DHCPACK in the storage if renews.
1252  // Note that, DHCPACK messages hold the information about
1253  // leases assigned. We use this information to renew.
1254  if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
1255  // Renew messages are sent, because StatsMgr has the
1256  // specific exchange type specified. Let's append the DHCPACK.
1257  // message to a storage
1258  ack_storage_.append(pkt4);
1259  }
1260  // The DHCPACK message is not a server's response to the DHCPREQUEST
1261  // message sent within the 4-way exchange. It may be a response to a
1262  // renewal. In this case we first check if StatsMgr has exchange type
1263  // for renew specified, and if it has, if there is a corresponding
1264  // renew message for the received DHCPACK.
1265  } else if (stats_mgr4_->hasExchangeStats(StatsMgr4::XCHG_RNA)) {
1266  stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RNA, pkt4);
1267  }
1268  }
1269 }
1270 
1271 void
1273  const Pkt6Ptr& pkt6) {
1274  uint8_t packet_type = pkt6->getType();
1275  if (packet_type == DHCPV6_ADVERTISE) {
1276  Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
1277  pkt6));
1278  CommandOptions::ExchangeMode xchg_mode =
1280  if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
1281  // \todo check whether received ADVERTISE packet is sane.
1282  // We might want to check if STATUS_CODE option is non-zero
1283  // and if there is IAADR option in IA_NA.
1284  if (template_buffers_.size() < 2) {
1285  sendRequest6(socket, pkt6);
1286  } else {
1287  // @todo add defines for packet type index that can be
1288  // used to access template_buffers_.
1289  sendRequest6(socket, template_buffers_[1], pkt6);
1290  }
1291  }
1292  } else if (packet_type == DHCPV6_REPLY) {
1293  // If the received message is Reply, we have to find out which exchange
1294  // type the Reply message belongs to. It is doable by matching the Reply
1295  // transaction id with the transaction id of the sent Request, Renew
1296  // or Release. First we start with the Request.
1297  if (stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6)) {
1298  // The Reply belongs to Request-Reply exchange type. So, we may need
1299  // to keep this Reply in the storage if Renews or/and Releases are
1300  // being sent. Note that, Reply messages hold the information about
1301  // leases assigned. We use this information to construct Renew and
1302  // Release messages.
1303  if (stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) ||
1304  stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
1305  // Renew or Release messages are sent, because StatsMgr has the
1306  // specific exchange type specified. Let's append the Reply
1307  // message to a storage.
1308  reply_storage_.append(pkt6);
1309  }
1310  // The Reply message is not a server's response to the Request message
1311  // sent within the 4-way exchange. It may be a response to the Renew
1312  // or Release message. In the if clause we first check if StatsMgr
1313  // has exchange type for Renew specified, and if it has, if there is
1314  // a corresponding Renew message for the received Reply. If not,
1315  // we check that StatsMgr has exchange type for Release specified,
1316  // as possibly the Reply has been sent in response to Release.
1317  } else if (!(stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RN) &&
1318  stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RN, pkt6)) &&
1319  stats_mgr6_->hasExchangeStats(StatsMgr6::XCHG_RL)) {
1320  // At this point, it is only possible that the Reply has been sent
1321  // in response to a Release. Try to match the Reply with Release.
1322  stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RL, pkt6);
1323  }
1324  }
1325 }
1326 
1327 uint64_t
1329  bool receiving = true;
1330  uint64_t received = 0;
1331  while (receiving) {
1332  if (CommandOptions::instance().getIpVersion() == 4) {
1333  Pkt4Ptr pkt4;
1334  try {
1335  pkt4 = IfaceMgr::instance().receive4(0, getCurrentTimeout());
1336  } catch (const Exception& e) {
1337  std::cerr << "Failed to receive DHCPv4 packet: "
1338  << e.what() << std::endl;
1339  }
1340  if (!pkt4) {
1341  receiving = false;
1342  } else {
1343  ++received;
1344  if ((received > 1) && testDiags('i')) {
1345  stats_mgr4_->incrementCounter("multircvd");
1346  }
1347 
1350  pkt4->unpack();
1351  processReceivedPacket4(socket, pkt4);
1352  }
1353  } else if (CommandOptions::instance().getIpVersion() == 6) {
1354  Pkt6Ptr pkt6;
1355  try {
1356  pkt6 = IfaceMgr::instance().receive6(0, getCurrentTimeout());
1357  } catch (const Exception& e) {
1358  std::cerr << "Failed to receive DHCPv6 packet: "
1359  << e.what() << std::endl;
1360  }
1361  if (!pkt6) {
1362  receiving = false;
1363  } else {
1364  ++received;
1365  if ((received > 1) && testDiags('i')) {
1366  stats_mgr6_->incrementCounter("multircvd");
1367  }
1368 
1371  pkt6->unpack();
1372  processReceivedPacket6(socket, pkt6);
1373  }
1374  }
1375  }
1376  return (received);
1377 }
1378 
1379 void
1381  static bool factories_registered = false;
1382  if (!factories_registered) {
1383  // DHCP_MESSAGE_TYPE option factory.
1384  LibDHCP::OptionFactoryRegister(Option::V4,
1387  // DHCP_SERVER_IDENTIFIER option factory.
1388  LibDHCP::OptionFactoryRegister(Option::V4,
1391  // DHCP_PARAMETER_REQUEST_LIST option factory.
1392  LibDHCP::OptionFactoryRegister(Option::V4,
1395  }
1396  factories_registered = true;
1397 }
1398 
1399 void
1401  static bool factories_registered = false;
1402  if (!factories_registered) {
1403  // D60_ELAPSED_TIME
1404  LibDHCP::OptionFactoryRegister(Option::V6,
1407  // D6O_RAPID_COMMIT
1408  LibDHCP::OptionFactoryRegister(Option::V6,
1411  // D6O_ORO (option request option) factory.
1412  LibDHCP::OptionFactoryRegister(Option::V6,
1413  D6O_ORO,
1415  // D6O_CLIENTID option factory.
1416  LibDHCP::OptionFactoryRegister(Option::V6,
1417  D6O_CLIENTID,
1419  // D6O_SERVERID option factory.
1420  LibDHCP::OptionFactoryRegister(Option::V6,
1421  D6O_SERVERID,
1423  // D6O_IA_NA option factory.
1424  LibDHCP::OptionFactoryRegister(Option::V6,
1425  D6O_IA_NA,
1427 
1428  // D6O_IA_PD option factory.
1429  LibDHCP::OptionFactoryRegister(Option::V6,
1430  D6O_IA_PD,
1432 
1433 
1434  }
1435  factories_registered = true;
1436 }
1437 
1438 void
1441  switch(options.getIpVersion()) {
1442  case 4:
1444  break;
1445  case 6:
1447  break;
1448  default:
1449  isc_throw(InvalidOperation, "command line options have to be parsed "
1450  "before DHCP option factories can be registered");
1451  }
1452 }
1453 
1454 void
1458  basic_rate_control_.setRate(options.getRate());
1463 
1464  transid_gen_.reset();
1465  last_report_ = microsec_clock::universal_time();
1466  // Actual generators will have to be set later on because we need to
1467  // get command line parameters first.
1470  first_packet_serverid_.clear();
1471  interrupted_ = false;
1472 }
1473 
1474 int
1476  // Reset singleton state before test starts.
1477  reset();
1478 
1480  // Ip version is not set ONLY in case the command options
1481  // were not parsed. This surely means that parse() function
1482  // was not called prior to starting the test. This is fatal
1483  // error.
1484  if (options.getIpVersion() == 0) {
1486  "command options must be parsed before running a test");
1487  } else if (options.getIpVersion() == 4) {
1488  // Turn off packet queueing.
1489  IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr());
1491  } else {
1492  // Turn off packet queueing.
1493  IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, data::ElementPtr());
1495  }
1496 
1497  uint32_t clients_num = options.getClientsNum() == 0 ?
1498  1 : options.getClientsNum();
1500 
1501  // Diagnostics are command line options mainly.
1502  printDiagnostics();
1503  // Option factories have to be registered.
1505  TestControlSocket socket(openSocket());
1506  if (!socket.valid_) {
1507  isc_throw(Unexpected, "invalid socket descriptor");
1508  }
1509  // Initialize packet templates.
1511  // Initialize randomization seed.
1512  if (options.isSeeded()) {
1513  srandom(options.getSeed());
1514  } else {
1515  // Seed with current time.
1516  time_period duration(from_iso_string("20111231T235959"),
1517  microsec_clock::universal_time());
1518  srandom(duration.length().total_seconds()
1519  + duration.length().fractional_seconds());
1520  }
1521  // If user interrupts the program we will exit gracefully.
1522  signal(SIGINT, TestControl::handleInterrupt);
1523 
1524  // Preload server with the number of packets.
1525  sendPackets(socket, options.getPreload(), true);
1526 
1527  // Fork and run command specified with -w<wrapped-command>
1528  if (!options.getWrapped().empty()) {
1529  runWrapped();
1530  }
1531 
1532  // Initialize Statistics Manager. Release previous if any.
1534  for (;;) {
1535  // Calculate number of packets to be sent to stay
1536  // catch up with rate.
1537  uint64_t packets_due = basic_rate_control_.getOutboundMessageCount();
1539  if ((packets_due == 0) && testDiags('i')) {
1540  if (options.getIpVersion() == 4) {
1541  stats_mgr4_->incrementCounter("shortwait");
1542  } else if (options.getIpVersion() == 6) {
1543  stats_mgr6_->incrementCounter("shortwait");
1544  }
1545  }
1546 
1547  // @todo: set non-zero timeout for packets once we implement
1548  // microseconds timeout in IfaceMgr.
1549  receivePackets(socket);
1550 
1551  // If test period finished, maximum number of packet drops
1552  // has been reached or test has been interrupted we have to
1553  // finish the test.
1554  if (checkExitConditions()) {
1555  break;
1556  }
1557 
1558  if (!hasLateExitCommenced()) {
1559  // Initiate new DHCP packet exchanges.
1560  sendPackets(socket, packets_due);
1561  }
1562 
1563  // If -f<renew-rate> option was specified we have to check how many
1564  // Renew packets should be sent to catch up with a desired rate.
1565  if (options.getRenewRate() != 0) {
1566  uint64_t renew_packets_due =
1569 
1570  // Send multiple renews to satisfy the desired rate.
1571  if (options.getIpVersion() == 4) {
1572  sendMultipleRequests(socket, renew_packets_due);
1573  } else {
1574  sendMultipleMessages6(socket, DHCPV6_RENEW, renew_packets_due);
1575  }
1576  }
1577 
1578  // If -F<release-rate> option was specified we have to check how many
1579  // Release messages should be sent to catch up with a desired rate.
1580  if ((options.getIpVersion() == 6) && (options.getReleaseRate() != 0)) {
1581  uint64_t release_packets_due =
1584  // Send Release messages.
1585  sendMultipleMessages6(socket, DHCPV6_RELEASE, release_packets_due);
1586  }
1587 
1588  // Report delay means that user requested printing number
1589  // of sent/received/dropped packets repeatedly.
1590  if (options.getReportDelay() > 0) {
1592  }
1593 
1594  // If we are sending Renews to the server, the Reply packets are cached
1595  // so as leases for which we send Renews can be identified. The major
1596  // issue with this approach is that most of the time we are caching
1597  // more packets than we actually need. This function removes excessive
1598  // Reply messages to reduce the memory and CPU utilization. Note that
1599  // searches in the long list of Reply packets increases CPU utilization.
1601  }
1602  printStats();
1603 
1604  if (!options.getWrapped().empty()) {
1605  // true means that we execute wrapped command with 'stop' argument.
1606  runWrapped(true);
1607  }
1608 
1609  // Print packet timestamps
1610  if (testDiags('t')) {
1611  if (options.getIpVersion() == 4) {
1612  stats_mgr4_->printTimestamps();
1613  } else if (options.getIpVersion() == 6) {
1614  stats_mgr6_->printTimestamps();
1615  }
1616  }
1617 
1618  // Print server id.
1619  if (testDiags('s') && (first_packet_serverid_.size() > 0)) {
1620  std::cout << "Server id: " << vector2Hex(first_packet_serverid_) << std::endl;
1621  }
1622 
1623  // Diagnostics flag 'e' means show exit reason.
1624  if (testDiags('e')) {
1625  std::cout << "Interrupted" << std::endl;
1626  }
1627  // Print packet templates. Even if -T options have not been specified the
1628  // dynamically build packet will be printed if at least one has been sent.
1629  if (testDiags('T')) {
1630  printTemplates();
1631  }
1632 
1633  int ret_code = 0;
1634  // Check if any packet drops occurred.
1635  if (options.getIpVersion() == 4) {
1636  ret_code = stats_mgr4_->droppedPackets() ? 3 : 0;
1637  } else if (options.getIpVersion() == 6) {
1638  ret_code = stats_mgr6_->droppedPackets() ? 3 : 0;
1639  }
1640  return (ret_code);
1641 }
1642 
1643 void
1644 TestControl::runWrapped(bool do_stop /*= false */) const {
1646  if (!options.getWrapped().empty()) {
1647  pid_t pid = 0;
1648  signal(SIGCHLD, handleChild);
1649  pid = fork();
1650  if (pid < 0) {
1651  isc_throw(Unexpected, "unable to fork");
1652  } else if (pid == 0) {
1653  execlp(options.getWrapped().c_str(),
1654  do_stop ? "stop" : "start",
1655  NULL);
1656  }
1657  }
1658 }
1659 
1660 void
1662  if (testDiags('T')) {
1663  if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1664  template_packets_v4_[pkt->getType()] = pkt;
1665  }
1666  }
1667 }
1668 
1669 void
1671  if (testDiags('T')) {
1672  if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1673  template_packets_v6_[pkt->getType()] = pkt;
1674  }
1675  }
1676 }
1677 
1678 void
1680  const bool preload /*= false*/) {
1682  // Generate the MAC address to be passed in the packet.
1683  uint8_t randomized = 0;
1684  std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1685  // Generate transaction id to be set for the new exchange.
1686  const uint32_t transid = generateTransid();
1687  Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1688  if (!pkt4) {
1689  isc_throw(Unexpected, "failed to create DISCOVER packet");
1690  }
1691 
1692  // Delete the default Message Type option set by Pkt4
1693  pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1694 
1695  // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1696  OptionBuffer buf_msg_type;
1697  buf_msg_type.push_back(DHCPDISCOVER);
1698  pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
1699  buf_msg_type));
1700  pkt4->addOption(Option::factory(Option::V4,
1702 
1703  // Set client's and server's ports as well as server's address,
1704  // and local (relay) address.
1705  setDefaults4(socket, pkt4);
1706 
1707  // Set hardware address
1708  pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1709 
1710  // Set client identifier
1711  pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1712 
1713  // Add any extra options that user may have specified.
1714  addExtraOpts(pkt4);
1715 
1716  pkt4->pack();
1717  IfaceMgr::instance().send(pkt4);
1718  if (!preload) {
1719  if (!stats_mgr4_) {
1720  isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1721  "hasn't been initialized");
1722  }
1723  stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
1724  }
1725  saveFirstPacket(pkt4);
1726 }
1727 
1728 void
1730  const std::vector<uint8_t>& template_buf,
1731  const bool preload /* = false */) {
1733  // Get the first argument if multiple the same arguments specified
1734  // in the command line. First one refers to DISCOVER packets.
1735  const uint8_t arg_idx = 0;
1736  // Generate the MAC address to be passed in the packet.
1737  uint8_t randomized = 0;
1738  std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1739  // Generate transaction id to be set for the new exchange.
1740  const uint32_t transid = generateTransid();
1741  // Get transaction id offset.
1742  size_t transid_offset = getTransactionIdOffset(arg_idx);
1743  // Get randomization offset.
1744  // We need to go back by HW_ETHER_LEN (MAC address length)
1745  // because this offset points to last octet of MAC address.
1746  size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1747  // Create temporary buffer with template contents. We will
1748  // modify this temporary buffer but we don't want to modify
1749  // the original template.
1750  std::vector<uint8_t> in_buf(template_buf.begin(),
1751  template_buf.end());
1752  // Check if we are not going out of bounds.
1753  if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1754  isc_throw(OutOfRange, "randomization offset is out of bounds");
1755  }
1756  PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1757  transid_offset,
1758  transid));
1759 
1760  // Replace MAC address in the template with actual MAC address.
1761  pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1762  // Create a packet from the temporary buffer.
1763  setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
1764  // Pack the input packet buffer to output buffer so as it can
1765  // be sent to server.
1766  pkt4->rawPack();
1767  IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
1768  if (!preload) {
1769  if (!stats_mgr4_) {
1770  isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1771  "hasn't been initialized");
1772  }
1773  // Update packet stats.
1774  stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO,
1775  boost::static_pointer_cast<Pkt4>(pkt4));
1776  }
1777  saveFirstPacket(pkt4);
1778 }
1779 
1780 bool
1782  // Update timestamp of last sent renewal.
1784 
1785  // Get one of the recorded DHCPACK messages.
1786  Pkt4Ptr ack = ack_storage_.getRandom();
1787  if (!ack) {
1788  return (false);
1789  }
1790 
1791  // Create message of the specified type.
1792  Pkt4Ptr msg = createRequestFromAck(ack);
1793  setDefaults4(socket, msg);
1794 
1795  // Add any extra options that user may have specified.
1796  addExtraOpts(msg);
1797 
1798  msg->pack();
1799  // And send it.
1800  IfaceMgr::instance().send(msg);
1801  if (!stats_mgr4_) {
1802  isc_throw(Unexpected, "Statistics Manager for DHCPv4 "
1803  "hasn't been initialized");
1804  }
1805  stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RNA, msg);
1806  return (true);
1807 }
1808 
1809 
1810 bool
1811 TestControl::sendMessageFromReply(const uint16_t msg_type,
1812  const TestControlSocket& socket) {
1813  // We only permit Release or Renew messages to be sent using this function.
1814  if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1815  isc_throw(isc::BadValue, "invalid message type " << msg_type
1816  << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1817  }
1818  // We track the timestamp of last Release and Renew in different variables.
1819  if (msg_type == DHCPV6_RENEW) {
1821  } else {
1823  }
1824  Pkt6Ptr reply = reply_storage_.getRandom();
1825  if (!reply) {
1826  return (false);
1827  }
1828  // Prepare the message of the specified type.
1829  Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1830  setDefaults6(socket, msg);
1831 
1832  // Add any extra options that user may have specified.
1833  addExtraOpts(msg);
1834 
1835  msg->pack();
1836  // And send it.
1837  IfaceMgr::instance().send(msg);
1838  if (!stats_mgr6_) {
1839  isc_throw(Unexpected, "Statistics Manager for DHCPv6 "
1840  "hasn't been initialized");
1841  }
1842  stats_mgr6_->passSentPacket((msg_type == DHCPV6_RENEW ? StatsMgr6::XCHG_RN
1843  : StatsMgr6::XCHG_RL), msg);
1844  return (true);
1845 }
1846 
1847 void
1849  const dhcp::Pkt4Ptr& discover_pkt4,
1850  const dhcp::Pkt4Ptr& offer_pkt4) {
1851  // Use the same transaction id as the one used in the discovery packet.
1852  const uint32_t transid = discover_pkt4->getTransid();
1853  Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1854 
1855  // Use first flags indicates that we want to use the server
1856  // id captured in first packet.
1857  if (CommandOptions::instance().isUseFirst() &&
1858  (first_packet_serverid_.size() > 0)) {
1859  pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
1861  } else {
1862  OptionPtr opt_serverid =
1863  offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1864  if (!opt_serverid) {
1865  isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1866  << "in OFFER message");
1867  }
1868  if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
1869  first_packet_serverid_ = opt_serverid->getData();
1870  }
1871  pkt4->addOption(opt_serverid);
1872  }
1873 
1875  asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1876  if (!yiaddr.isV4()) {
1877  isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1878  " IPv4 address");
1879  }
1880  OptionPtr opt_requested_address =
1882  OptionBuffer()));
1883  opt_requested_address->setUint32(yiaddr.toUint32());
1884  pkt4->addOption(opt_requested_address);
1885  OptionPtr opt_parameter_list =
1886  Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
1887  pkt4->addOption(opt_parameter_list);
1888  // Set client's and server's ports as well as server's address,
1889  // and local (relay) address.
1890  setDefaults4(socket, pkt4);
1891 
1892  // Add any extra options that user may have specified.
1893  addExtraOpts(pkt4);
1894 
1895  // Set hardware address
1896  pkt4->setHWAddr(offer_pkt4->getHWAddr());
1897  // Set client id.
1898  pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1899  // Set elapsed time.
1900  uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1901  pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1902  // Prepare on wire data to send.
1903  pkt4->pack();
1904  IfaceMgr::instance().send(pkt4);
1905  if (!stats_mgr4_) {
1906  isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
1907  "hasn't been initialized");
1908  }
1909  stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
1910  saveFirstPacket(pkt4);
1911 }
1912 
1913 void
1915  const std::vector<uint8_t>& template_buf,
1916  const dhcp::Pkt4Ptr& discover_pkt4,
1917  const dhcp::Pkt4Ptr& offer_pkt4) {
1918  // Get the second argument if multiple the same arguments specified
1919  // in the command line. Second one refers to REQUEST packets.
1920  const uint8_t arg_idx = 1;
1921  // Use the same transaction id as the one used in the discovery packet.
1922  const uint32_t transid = discover_pkt4->getTransid();
1923  // Get transaction id offset.
1924  size_t transid_offset = getTransactionIdOffset(arg_idx);
1925  // Get the offset of MAC's last octet.
1926  // We need to go back by HW_ETHER_LEN (MAC address length)
1927  // because this offset points to last octet of MAC address.
1928  size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1929  // Create temporary buffer from the template.
1930  std::vector<uint8_t> in_buf(template_buf.begin(),
1931  template_buf.end());
1932  // Check if given randomization offset is not out of bounds.
1933  if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1934  isc_throw(OutOfRange, "randomization offset is out of bounds");
1935  }
1936 
1937  // Create packet from the temporary buffer.
1938  PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1939  transid_offset,
1940  transid));
1941 
1942  // Set hardware address from OFFER packet received.
1943  HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1944  std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1945  uint8_t hw_len = hwaddr->hwaddr_.size();
1946  if (hw_len != 0) {
1947  memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1948  hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1949  }
1950  pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1951 
1952  // Set elapsed time.
1953  size_t elp_offset = getElapsedTimeOffset();
1954  uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1955  pkt4->writeValueAt<uint16_t>(elp_offset,
1956  static_cast<uint16_t>(elapsed_time / 1000));
1957 
1958  // Get the actual server id offset.
1959  size_t sid_offset = getServerIdOffset();
1960  // Use first flags indicates that we want to use the server
1961  // id captured in first packet.
1962  if (CommandOptions::instance().isUseFirst() &&
1963  (first_packet_serverid_.size() > 0)) {
1964  boost::shared_ptr<LocalizedOption>
1965  opt_serverid(new LocalizedOption(Option::V4,
1968  sid_offset));
1969  pkt4->addOption(opt_serverid);
1970  } else {
1971  // Copy the contents of server identifier received in
1972  // OFFER packet to put this into REQUEST.
1973  OptionPtr opt_serverid_offer =
1974  offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1975  if (!opt_serverid_offer) {
1976  isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1977  << "in OFFER message");
1978  }
1979  boost::shared_ptr<LocalizedOption>
1980  opt_serverid(new LocalizedOption(Option::V4,
1982  opt_serverid_offer->getData(),
1983  sid_offset));
1984  pkt4->addOption(opt_serverid);
1985  if (stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
1986  first_packet_serverid_ = opt_serverid_offer->getData();
1987  }
1988  }
1989 
1991  asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1992  if (!yiaddr.isV4()) {
1993  isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1994  " IPv4 address");
1995  }
1996 
1997  // Get the actual offset of requested ip.
1998  size_t rip_offset = getRequestedIpOffset();
1999  // Place requested IP option at specified position (rip_offset).
2000  boost::shared_ptr<LocalizedOption>
2001  opt_requested_ip(new LocalizedOption(Option::V4,
2003  OptionBuffer(),
2004  rip_offset));
2005  // The IOAddress is convertible to uint32_t and returns exactly what we need.
2006  opt_requested_ip->setUint32(yiaddr.toUint32());
2007  pkt4->addOption(opt_requested_ip);
2008 
2009  setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
2010 
2011  // Add any extra options that user may have specified.
2012  addExtraOpts(pkt4);
2013 
2014  // Prepare on-wire data.
2015  pkt4->rawPack();
2016  IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
2017  if (!stats_mgr4_) {
2018  isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
2019  "hasn't been initialized");
2020  }
2021  // Update packet stats.
2022  stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA,
2023  boost::static_pointer_cast<Pkt4>(pkt4));
2024  saveFirstPacket(pkt4);
2025 }
2026 
2027 void
2029  const Pkt6Ptr& advertise_pkt6) {
2030  const uint32_t transid = generateTransid();
2031  Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
2032  // Set elapsed time.
2033  OptionPtr opt_elapsed_time =
2034  Option::factory(Option::V6, D6O_ELAPSED_TIME);
2035  pkt6->addOption(opt_elapsed_time);
2036  // Set client id.
2037  OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
2038  if (!opt_clientid) {
2039  isc_throw(Unexpected, "client id not found in received packet");
2040  }
2041  pkt6->addOption(opt_clientid);
2042 
2043  // Use first flags indicates that we want to use the server
2044  // id captured in first packet.
2045  if (CommandOptions::instance().isUseFirst() &&
2046  (first_packet_serverid_.size() > 0)) {
2047  pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
2049  } else {
2050  OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
2051  if (!opt_serverid) {
2052  isc_throw(Unexpected, "server id not found in received packet");
2053  }
2054  if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
2055  first_packet_serverid_ = opt_serverid->getData();
2056  }
2057  pkt6->addOption(opt_serverid);
2058  }
2059 
2060  // Copy IA_NA or IA_PD option from the Advertise message to the Request
2061  // message being sent to the server. This will throw exception if the
2062  // option to be copied is not found. Note that this function will copy
2063  // one of IA_NA or IA_PD options, depending on the lease-type value
2064  // specified in the command line.
2065  copyIaOptions(advertise_pkt6, pkt6);
2066 
2067  // Set default packet data.
2068  setDefaults6(socket, pkt6);
2069 
2070  // Add any extra options that user may have specified.
2071  addExtraOpts(pkt6);
2072 
2073  // Prepare on-wire data.
2074  pkt6->pack();
2075  IfaceMgr::instance().send(pkt6);
2076  if (!stats_mgr6_) {
2077  isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2078  "hasn't been initialized");
2079  }
2080  stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
2081  saveFirstPacket(pkt6);
2082 }
2083 
2084 void
2086  const std::vector<uint8_t>& template_buf,
2087  const Pkt6Ptr& advertise_pkt6) {
2088  // Get the second argument if multiple the same arguments specified
2089  // in the command line. Second one refers to REQUEST packets.
2090  const uint8_t arg_idx = 1;
2091  // Generate transaction id.
2092  const uint32_t transid = generateTransid();
2093  // Get transaction id offset.
2094  size_t transid_offset = getTransactionIdOffset(arg_idx);
2095  PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
2096  transid_offset, transid));
2097  // Set elapsed time.
2098  size_t elp_offset = getElapsedTimeOffset();
2099  boost::shared_ptr<LocalizedOption>
2100  opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
2101  OptionBuffer(), elp_offset));
2102  pkt6->addOption(opt_elapsed_time);
2103 
2104  // Get the actual server id offset.
2105  size_t sid_offset = getServerIdOffset();
2106  // Use first flags indicates that we want to use the server
2107  // id captured in first packet.
2108  if (CommandOptions::instance().isUseFirst() &&
2109  (first_packet_serverid_.size() > 0)) {
2110  boost::shared_ptr<LocalizedOption>
2111  opt_serverid(new LocalizedOption(Option::V6,
2112  D6O_SERVERID,
2114  sid_offset));
2115  pkt6->addOption(opt_serverid);
2116 
2117  } else {
2118  // Copy the contents of server identifier received in
2119  // ADVERTISE packet to put this into REQUEST.
2120  OptionPtr opt_serverid_advertise =
2121  advertise_pkt6->getOption(D6O_SERVERID);
2122  if (!opt_serverid_advertise) {
2123  isc_throw(BadValue, "there is no SERVERID option "
2124  << "in ADVERTISE message");
2125  }
2126  boost::shared_ptr<LocalizedOption>
2127  opt_serverid(new LocalizedOption(Option::V6,
2128  D6O_SERVERID,
2129  opt_serverid_advertise->getData(),
2130  sid_offset));
2131  pkt6->addOption(opt_serverid);
2132  if (stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
2133  first_packet_serverid_ = opt_serverid_advertise->getData();
2134  }
2135  }
2136  // Set IA_NA
2137  boost::shared_ptr<Option6IA> opt_ia_na_advertise =
2138  boost::static_pointer_cast<Option6IA>(advertise_pkt6->getOption(D6O_IA_NA));
2139  if (!opt_ia_na_advertise) {
2140  isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
2141  "packet");
2142  }
2143  size_t addr_offset = getRequestedIpOffset();
2144  boost::shared_ptr<LocalizedOption>
2145  opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
2146  if (!opt_ia_na->valid()) {
2147  isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
2148  }
2149  pkt6->addOption(opt_ia_na);
2150  // Set server id.
2151  OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
2152  if (!opt_serverid_advertise) {
2153  isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
2154  "packet");
2155  }
2156  size_t srvid_offset = getServerIdOffset();
2157  boost::shared_ptr<LocalizedOption>
2158  opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
2159  opt_serverid_advertise->getData(),
2160  srvid_offset));
2161  pkt6->addOption(opt_serverid);
2162  // Get randomization offset.
2163  size_t rand_offset = getRandomOffset(arg_idx);
2164  OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
2165  if (!opt_clientid_advertise) {
2166  isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
2167  }
2168  rand_offset -= (opt_clientid_advertise->len() - 1);
2169  // Set client id.
2170  boost::shared_ptr<LocalizedOption>
2171  opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
2172  opt_clientid_advertise->getData(),
2173  rand_offset));
2174  pkt6->addOption(opt_clientid);
2175  // Set default packet data.
2176  setDefaults6(socket, pkt6);
2177 
2178  // Add any extra options that user may have specified.
2179  addExtraOpts(pkt6);
2180 
2181  // Prepare on wire data.
2182  pkt6->rawPack();
2183  // Send packet.
2184  IfaceMgr::instance().send(pkt6);
2185  if (!stats_mgr6_) {
2186  isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2187  "hasn't been initialized");
2188  }
2189  // Update packet stats.
2190  stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
2191 
2192  // When 'T' diagnostics flag is specified it means that user requested
2193  // printing packet contents. It will be just one (first) packet which
2194  // contents will be printed. Here we check if this packet has been already
2195  // collected. If it hasn't we save this packet so as we can print its
2196  // contents when test is finished.
2197  if (testDiags('T') &&
2200  }
2201 }
2202 
2203 void
2205  const bool preload /*= false*/) {
2207  // Generate DUID to be passed to the packet
2208  uint8_t randomized = 0;
2209  std::vector<uint8_t> duid = generateDuid(randomized);
2210  // Generate transaction id to be set for the new exchange.
2211  const uint32_t transid = generateTransid();
2212  Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
2213  if (!pkt6) {
2214  isc_throw(Unexpected, "failed to create SOLICIT packet");
2215  }
2216  pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
2217  if (CommandOptions::instance().isRapidCommit()) {
2218  pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
2219  }
2220  pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
2221  pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
2222 
2223  // Depending on the lease-type option specified, we should request
2224  // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
2225 
2226  // IA_NA
2227  if (CommandOptions::instance().getLeaseType()
2229  pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
2230  }
2231  // IA_PD
2232  if (CommandOptions::instance().getLeaseType()
2233  .includes(CommandOptions::LeaseType::PREFIX)) {
2234  pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
2235  }
2236 
2237  setDefaults6(socket, pkt6);
2238 
2239  // Add any extra options that user may have specified.
2240  addExtraOpts(pkt6);
2241 
2242  pkt6->pack();
2243  IfaceMgr::instance().send(pkt6);
2244  if (!preload) {
2245  if (!stats_mgr6_) {
2246  isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2247  "hasn't been initialized");
2248  }
2249  stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
2250  }
2251 
2252  saveFirstPacket(pkt6);
2253 }
2254 
2255 void
2257  const std::vector<uint8_t>& template_buf,
2258  const bool preload /*= false*/) {
2260  const int arg_idx = 0;
2261  // Get transaction id offset.
2262  size_t transid_offset = getTransactionIdOffset(arg_idx);
2263  // Generate transaction id to be set for the new exchange.
2264  const uint32_t transid = generateTransid();
2265  // Create packet.
2266  PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
2267  transid_offset, transid));
2268  if (!pkt6) {
2269  isc_throw(Unexpected, "failed to create SOLICIT packet");
2270  }
2271  size_t rand_offset = getRandomOffset(arg_idx);
2272  // randomized will pick number of bytes randomized so we can
2273  // just use part of the generated duid and substitute a few bytes
2275  uint8_t randomized = 0;
2276  std::vector<uint8_t> duid = generateDuid(randomized);
2277  if (rand_offset > template_buf.size()) {
2278  isc_throw(OutOfRange, "randomization offset is out of bounds");
2279  }
2280  // Store random part of the DUID into the packet.
2281  pkt6->writeAt(rand_offset - randomized + 1,
2282  duid.end() - randomized, duid.end());
2283 
2284  // Prepare on-wire data.
2285  pkt6->rawPack();
2286  setDefaults6(socket, pkt6);
2287 
2288  // Add any extra options that user may have specified.
2289  addExtraOpts(pkt6);
2290 
2291  // Send solicit packet.
2292  IfaceMgr::instance().send(pkt6);
2293  if (!preload) {
2294  if (!stats_mgr6_) {
2295  isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
2296  "hasn't been initialized");
2297  }
2298  // Update packet stats.
2299  stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
2300  }
2301  saveFirstPacket(pkt6);
2302 }
2303 
2304 
2305 void
2307  const Pkt4Ptr& pkt) {
2309  // Interface name.
2310  IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
2311  if (iface == NULL) {
2312  isc_throw(BadValue, "unable to find interface with given index");
2313  }
2314  pkt->setIface(iface->getName());
2315  // Interface index.
2316  pkt->setIndex(socket.ifindex_);
2317  // Local client's port (68)
2318  pkt->setLocalPort(DHCP4_CLIENT_PORT);
2319  // Server's port (67)
2320  pkt->setRemotePort(DHCP4_SERVER_PORT);
2321  // The remote server's name or IP.
2322  pkt->setRemoteAddr(IOAddress(options.getServerName()));
2323  // Set local address.
2324  pkt->setLocalAddr(IOAddress(socket.addr_));
2325  // Set relay (GIADDR) address to local address.
2326  pkt->setGiaddr(IOAddress(socket.addr_));
2327  // Pretend that we have one relay (which is us).
2328  pkt->setHops(1);
2329 }
2330 
2331 void
2333  const Pkt6Ptr& pkt) {
2335  // Interface name.
2336  IfacePtr iface = IfaceMgr::instance().getIface(socket.ifindex_);
2337  if (iface == NULL) {
2338  isc_throw(BadValue, "unable to find interface with given index");
2339  }
2340  pkt->setIface(iface->getName());
2341  // Interface index.
2342  pkt->setIndex(socket.ifindex_);
2343  // Local client's port (547)
2344  pkt->setLocalPort(DHCP6_CLIENT_PORT);
2345  // Server's port (548)
2346  pkt->setRemotePort(DHCP6_SERVER_PORT);
2347  // Set local address.
2348  pkt->setLocalAddr(socket.addr_);
2349  // The remote server's name or IP.
2350  pkt->setRemoteAddr(IOAddress(options.getServerName()));
2351 
2352  // only act as a relay agent when told so.
2353  // TODO: support more level of encapsulation, at the moment we only support
2354  // one, via -A1 option.
2355  if (options.isUseRelayedV6()) {
2356  Pkt6::RelayInfo relay_info;
2357  relay_info.msg_type_ = DHCPV6_RELAY_FORW;
2358  relay_info.hop_count_ = 1;
2359  relay_info.linkaddr_ = IOAddress(socket.addr_);
2360  relay_info.peeraddr_ = IOAddress(socket.addr_);
2361  pkt->addRelayInfo(relay_info);
2362  }
2363 }
2364 
2365 void
2367  // All all extra options that the user may have specified
2369  const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2370  for (auto entry : extra_opts) {
2371  pkt->addOption(entry.second);
2372  }
2373 }
2374 
2375 void
2377  // All all extra options that the user may have specified
2379  const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
2380  for (auto entry : extra_opts) {
2381  pkt->addOption(entry.second);
2382  }
2383 }
2384 
2385 bool
2386 TestControl::testDiags(const char diag) const {
2387  std::string diags(CommandOptions::instance().getDiags());
2388  if (diags.find(diag) != std::string::npos) {
2389  return (true);
2390  }
2391  return (false);
2392 }
2393 
2394 } // namespace perfdhcp
2395 } // namespace isc
PerfPkt6 (DHCPv6 packet)
Definition: perf_pkt6.h:40
NumberGeneratorPtr transid_gen_
Transaction id generator.
static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create generic option.
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
Definition: test_control.h:212
int getExitWaitTime() const
Returns the time in microseconds to delay the program by.
static bool interrupted_
Is program interrupted.
uint32_t generateTransid()
generate transaction id.
Definition: test_control.h:508
bool sendRequestFromAck(const TestControlSocket &socket)
Send DHCPv4 renew (DHCPREQUEST) using specified socket.
void printTemplates() const
Print templates information.
void printStats() const
Print performance statistics.
boost::shared_ptr< PerfPkt6 > PerfPkt6Ptr
Definition: perf_pkt6.h:126
DHCP option at specific offset.
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_NA option.
std::string getLocalName() const
Returns local address or interface name.
boost::shared_ptr< PerfPkt4 > PerfPkt4Ptr
Definition: perf_pkt4.h:126
int getAggressivity() const
Returns aggressivity value.
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
int getPeriod() const
Returns test period.
Exception thrown when the required option is not found in a packet.
Definition: test_control.h:54
RateControl release_rate_control_
A rate control class for Release messages.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
PacketStorage< dhcp::Pkt4 > ack_storage_
A storage for DHCPACK messages.
void setDefaults4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt)
Set default DHCPv4 packet parameters.
std::vector< uint8_t > generateDuid(uint8_t &randomized)
Generate DUID.
void registerOptionFactories4() const
Register option factory functions for DHCPv4.
static TestControl & instance()
TestControl is a singleton class.
std::vector< std::vector< uint8_t > > MacAddrsVector
A vector holding MAC addresses.
ExchangeType
DHCP packet exchange types.
boost::shared_ptr< Iface > IfacePtr
Definition: iface_mgr.h:457
int run()
brief\ Run performance test.
uint32_t getCurrentTimeout() const
Returns a timeout for packet reception.
int getServerIdOffset() const
Returns template offset for server-ID.
bool checkExitConditions() const
Check if test exit conditions fulfilled.
void processReceivedPacket6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt6)
Process received DHCPv6 packet.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:37
void setTransidGenerator(const NumberGeneratorPtr &generator)
Set new transaction id generator.
Definition: test_control.h:270
boost::posix_time::ptime getDue() const
Returns current due time to send next message.
Definition: rate_control.h:56
void checkLateMessages(RateControl &rate_control)
Increments counter of late sent messages if required.
StatsMgr4Ptr stats_mgr4_
Statistics Manager 4.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:67
Represents a DHCPv6 packet.
Definition: pkt6.h:44
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
static CommandOptions & instance()
CommandOptions is a singleton class.
void printIntermediateStats()
Print intermediate statistics.
std::vector< uint8_t > getMacTemplate() const
Returns MAC address template.
boost::shared_ptr< StatsMgr6 > StatsMgr6Ptr
Pointer to Statistics Manager for DHCPv6.
Definition: test_control.h:131
static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 RAPID_COMMIT option instance.
TestControl()
Default constructor.
ptime late_exit_target_time_
Definition: test_control.cc:45
std::vector< std::string > getTemplateFiles() const
Returns template file names.
StatsMgr< dhcp::Pkt4 > StatsMgr4
Statistics Manager for DHCPv4.
Definition: test_control.h:125
A message sending rate control class for perfdhcp.
Definition: rate_control.h:38
uint8_t getIpVersion() const
Returns IP version.
void initPacketTemplates()
Reads packet templates from files.
dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr &hwaddr) const
Generate DHCPv4 client identifier from HW address.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
RateControl renew_rate_control_
A rate control class for Renew messages.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:25
bool isSeeded() const
Checks if seed provided.
bool testDiags(const char diag) const
Find if diagnostic flag has been set.
int sockfd_
IPv4 or IPv6.
Definition: socket_info.h:26
uint64_t getOutboundMessageCount()
Returns number of messages to be sent "now".
Definition: rate_control.cc:36
static dhcp::OptionPtr factoryElapsedTime6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ELAPSED_TIME option.
TemplateBufferCollection template_buffers_
Packet template buffers.
StatsMgr6Ptr stats_mgr6_
Statistics Manager 6.
bool isBroadcast() const
Checks if broadcast address is to be used.
dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr &ack)
Creates DHCPREQUEST from a DHCPACK message.
void printRate() const
Print rate statistics.
void updateSendTime()
Sets the timestamp of the last sent message to current time.
uint64_t sendMultipleRequests(const TestControlSocket &socket, const uint64_t msg_num)
Send number of DHCPREQUEST (renew) messages to a server.
StatsMgr ::ExchangeType ExchangeType
Packet exchange type.
Definition: test_control.h:133
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
Definition: test_control.h:135
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
int openSocket() const
Open socket to communicate with DHCP server.
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
uint64_t getSentPacketsNum(ExchangeType xchg_type) const
Get number of sent packets.
std::string getWrapped() const
Returns wrapped command.
bool hasLateExitCommenced() const
Check if the program is in that period where the program was bound to exit, but was delayed by lateEx...
Definition: test_control.cc:48
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
structure that describes a single relay information
Definition: pkt6.h:85
int getRandomOffset(const int arg_idx) const
Return randomization offset in a packet.
void printCommandLine() const
Print command line arguments.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1114
bool isInterface() const
Checks if interface name was used.
void setRate(const int rate)
Sets the new rate.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
void runWrapped(bool do_stop=false) const
Run wrapped command.
StatsMgr< dhcp::Pkt6 > StatsMgr6
Statistics Manager for DHCPv6.
Definition: test_control.h:129
A generic exception that is thrown when an unexpected error condition occurs.
void processReceivedPacket4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &pkt4)
Process received DHCPv4 packet.
boost::posix_time::ptime last_report_
Last intermediate report time.
std::map< uint8_t, dhcp::Pkt6Ptr > template_packets_v6_
std::vector< double > getDropTime() const
Returns drop time.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:546
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
void sendRequest6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &advertise_pkt6)
Send DHCPv6 REQUEST message.
void sendPackets(const TestControlSocket &socket, const uint64_t packets_num, const bool preload=false)
Send number of packets to initiate new exchanges.
TemplateBuffer getTemplateBuffer(const size_t idx) const
Return template buffer.
int getTransactionIdOffset(const int arg_idx) const
Return transaction id offset in a packet.
Definition: dhcp6.h:26
std::string getServerName() const
Returns server name.
std::string vector2Hex(const std::vector< uint8_t > &vec, const std::string &separator="") const
Convert vector in hexadecimal string.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:41
void sendDiscover4(const TestControlSocket &socket, const bool preload=false)
Send DHCPv4 DISCOVER message.
std::vector< uint8_t > getDuidTemplate() const
Returns DUID template.
uint64_t receivePackets(const TestControlSocket &socket)
Receive DHCPv4 or DHCPv6 packets from the server.
bool sendMessageFromReply(const uint16_t msg_type, const TestControlSocket &socket)
Send DHCPv6 Renew or Release message using specified socket.
PerfPkt4 (DHCPv4 packet)
Definition: perf_pkt4.h:40
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
void initializeStatsMgr()
Initializes Statistics Manager.
int getLocalPort() const
Returns local port number.
Ethernet 10Mbps.
Definition: dhcp4.h:56
void setAggressivity(const int aggressivity)
Sets the value of aggressivity.
std::vector< uint8_t > generateMacAddress(uint8_t &randomized)
Generate MAC address.
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-dhcp-ddns.
static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv4 Request List option.
int getReportDelay() const
Returns delay between two performance reports.
void copyIaOptions(const dhcp::Pkt6Ptr &pkt_from, dhcp::Pkt6Ptr &pkt_to)
Copies IA_NA or IA_PD option from one packet to another.
Represents DHCPv4 packet.
Definition: pkt4.h:38
boost::shared_ptr< StatsMgr4 > StatsMgr4Ptr
Pointer to Statistics Manager for DHCPv4;.
Definition: test_control.h:127
static void handleChild(int sig)
Handle child signal.
void sendSolicit6(const TestControlSocket &socket, const bool preload=false)
Send DHCPv6 SOLICIT message.
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
void cleanCachedPackets()
Removes cached DHCPv6 Reply packets every second.
uint8_t hop_count_
number of traversed relays (up to 32)
Definition: pkt6.h:95
std::vector< int > getMaxDrop() const
Returns maximum drops number.
PacketStorage< dhcp::Pkt6 > reply_storage_
A storage for reply messages.
void saveFirstPacket(const dhcp::Pkt4Ptr &pkt)
Save the first DHCPv4 sent packet of the specified type.
void registerOptionFactories() const
Register option factory functions for DHCPv4 or DHCPv6.
void sendRequest4(const TestControlSocket &socket, const dhcp::Pkt4Ptr &discover_pkt4, const dhcp::Pkt4Ptr &offer_pkt4)
Send DHCPv4 REQUEST message.
void registerOptionFactories6() const
Register option factory functions for DHCPv6.
A generic exception that is thrown if a function is called in a prohibited way.
void printTemplate(const uint8_t packet_type) const
Print template information.
void readPacketTemplate(const std::string &file_name)
Read DHCP message template from file.
Test Control class.
Definition: test_control.h:121
const isc::dhcp::OptionCollection & getExtraOpts() const
Returns extra options to be inserted.
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
int getRate() const
Returns exchange rate.
NumberGeneratorPtr macaddr_gen_
Numbers generator for MAC address.
isc::util::random::UniformRandomIntegerGenerator number_generator_
Generate uniformly distributed integers in range of [min, max].
Definition: test_control.h:298
static dhcp::OptionPtr factoryOptionRequestOption6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create DHCPv6 ORO option.
static const uint8_t HW_ETHER_LEN
Length of the Ethernet HW address (MAC) in bytes.
Definition: test_control.h:247
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
dhcp::OptionBuffer first_packet_serverid_
Buffer holding server id received in first packet.
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u, uint16_t type, const dhcp::OptionBuffer &buf)
Factory function to create IA_PD option.
isc::asiolink::IOAddress peeraddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:97
dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type, const dhcp::Pkt6Ptr &reply)
Creates DHCPv6 message from the Reply packet.
void printDiagnostics() const
Print main diagnostics data.
~TestControlSocket()
Destructor of the socket wrapper class.
uint32_t getElapsedTime(const T &pkt1, const T &pkt2)
Calculate elapsed time between two packets.
ExchangeMode
2-way (cmd line param -i) or 4-way exchanges
void setDefaults6(const TestControlSocket &socket, const dhcp::Pkt6Ptr &pkt)
Set default DHCPv6 packet parameters.
void setMacAddrGenerator(const NumberGeneratorPtr &generator)
Set new MAC address generator.
Definition: test_control.h:281
uint64_t getRcvdPacketsNum(ExchangeType xchg_type) const
Get number of received packets.
std::string byte2Hex(const uint8_t b) const
Convert binary value to hex string.
const MacAddrsVector & getMacsFromFile() const
Returns reference to a vector of MAC addresses read from a file.
RateControl basic_rate_control_
A rate control class for Discover and Solicit messages.
std::map< uint8_t, dhcp::Pkt4Ptr > template_packets_v4_
First packets send.
int getRequestedIpOffset() const
Returns template offset for requested IP.
int getRequestedIpOffset() const
Return requested ip offset in a packet.
bool isLateSent() const
Returns the value of the late send flag.
Definition: rate_control.h:104
int getElapsedTimeOffset() const
Return elapsed time offset in a packet.
uint64_t sendMultipleMessages6(const TestControlSocket &socket, const uint32_t msg_type, const uint64_t msg_num)
Send number of DHCPv6 Renew or Release messages to the server.
int getServerIdOffset() const
Return server id offset in a packet.
bool waitToExit() const
Delay the exit by a fixed given time to catch up to all exchanges that were already started.
Definition: test_control.cc:53
uint32_t getSeed() const
Returns random seed.
bool isRapidCommit() const
Check if rapid commit option used.
void reset()
Resets internal state of the object.
Holds information about socket.
Definition: socket_info.h:19
static void handleInterrupt(int sig)
Handle interrupt signal.
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
uint32_t getClientsNum() const
Returns number of simulated clients.
Sequential numbers generator class.
Definition: test_control.h:215
void addExtraOpts(const dhcp::Pkt4Ptr &pkt4)
Inserts extra options specified by user.
uint8_t msg_type_
message type (RELAY-FORW oro RELAY-REPL)
Definition: pkt6.h:94
int getPreload() const
Returns number of preload exchanges.