Kea  2.1.7-git
command_options.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2021 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 
11 #include <exceptions/exceptions.h>
12 #include <dhcp/iface_mgr.h>
13 #include <dhcp/duid.h>
14 #include <dhcp/option.h>
15 #include <cfgrpt/config_report.h>
16 #include <util/encode/hex.h>
17 #include <asiolink/io_error.h>
18 
19 #include <boost/lexical_cast.hpp>
20 #include <boost/date_time/posix_time/posix_time.hpp>
21 #include <sstream>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdint.h>
25 #include <unistd.h>
26 #include <fstream>
27 #include <thread>
28 #include <getopt.h>
29 
30 #ifdef HAVE_OPTRESET
31 extern int optreset;
32 #endif
33 
34 using namespace std;
35 using namespace isc;
36 using namespace isc::dhcp;
37 
38 namespace isc {
39 namespace perfdhcp {
40 
41 // Refer to config_report so it will be embedded in the binary
43 
44 CommandOptions::LeaseType::LeaseType()
45  : type_(ADDRESS) {
46 }
47 
49  : type_(lease_type) {
50 }
51 
52 bool
53 CommandOptions::LeaseType::is(const Type lease_type) const {
54  return (lease_type == type_);
55 }
56 
57 bool
58 CommandOptions::LeaseType::includes(const Type lease_type) const {
59  return (is(ADDRESS_AND_PREFIX) || (lease_type == type_));
60 }
61 
62 void
64  type_ = lease_type;
65 }
66 
67 void
68 CommandOptions::LeaseType::fromCommandLine(const std::string& cmd_line_arg) {
69  if (cmd_line_arg == "address-only") {
70  type_ = ADDRESS;
71 
72  } else if (cmd_line_arg == "prefix-only") {
73  type_ = PREFIX;
74 
75  } else if (cmd_line_arg == "address-and-prefix") {
76  type_ = ADDRESS_AND_PREFIX;
77 
78  } else {
79  isc_throw(isc::InvalidParameter, "value of lease-type: -e<lease-type>,"
80  " must be one of the following: 'address-only' or"
81  " 'prefix-only'");
82  }
83 }
84 
85 std::string
87  switch (type_) {
88  case ADDRESS:
89  return ("address-only (IA_NA option added to the client's request)");
90  case PREFIX:
91  return ("prefix-only (IA_PD option added to the client's request)");
92  case ADDRESS_AND_PREFIX:
93  return ("address-and-prefix (Both IA_NA and IA_PD options added to the"
94  " client's request)");
95  default:
96  isc_throw(Unexpected, "internal error: undefined lease type code when"
97  " returning textual representation of the lease type");
98  }
99 }
100 
101 void
103  // Default mac address used in DHCP messages
104  // if -b mac=<mac-address> was not specified
105  uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 };
106 
107  // Default packet drop time if -D<drop-time> parameter
108  // was not specified
109  double dt[2] = { 1., 1. };
110 
111  // We don't use constructor initialization list because we
112  // will need to reset all members many times to perform unit tests
113  ipversion_ = 0;
114  exchange_mode_ = DORA_SARR;
115  lease_type_.set(LeaseType::ADDRESS);
116  rate_ = 0;
117  renew_rate_ = 0;
118  release_rate_ = 0;
119  report_delay_ = 0;
120  clean_report_ = false;
121  clean_report_separator_ = "";
122  clients_num_ = 0;
123  mac_template_.assign(mac, mac + 6);
124  duid_template_.clear();
125  base_.clear();
126  addr_unique_ = false;
127  mac_list_file_.clear();
128  mac_list_.clear();
129  relay_addr_list_file_.clear();
130  relay_addr_list_.clear();
131  multi_subnet_ = false;
132  num_request_.clear();
133  exit_wait_time_ = 0;
134  period_ = 0;
135  wait_for_elapsed_time_ = -1;
136  increased_elapsed_time_ = -1;
137  drop_time_set_ = 0;
138  drop_time_.assign(dt, dt + 2);
139  max_drop_.clear();
140  max_pdrop_.clear();
141  localname_.clear();
142  is_interface_ = false;
143  preload_ = 0;
144  local_port_ = 0;
145  remote_port_ = 0;
146  seeded_ = false;
147  seed_ = 0;
148  broadcast_ = false;
149  rapid_commit_ = false;
150  use_first_ = false;
151  template_file_.clear();
152  rnd_offset_.clear();
153  xid_offset_.clear();
154  elp_offset_ = -1;
155  sid_offset_ = -1;
156  rip_offset_ = -1;
157  diags_.clear();
158  wrapped_.clear();
159  server_name_.clear();
160  v6_relay_encapsulation_level_ = 0;
161  generateDuidTemplate();
162  extra_opts_.clear();
163  if (std::thread::hardware_concurrency() == 1) {
164  single_thread_mode_ = true;
165  } else {
166  single_thread_mode_ = false;
167  }
168  scenario_ = Scenario::BASIC;
169 }
170 
171 bool
172 CommandOptions::parse(int argc, char** const argv, bool print_cmd_line) {
173  // Reset internal variables used by getopt
174  // to eliminate undefined behavior when
175  // parsing different command lines multiple times
176 
177 #ifdef __GLIBC__
178  // Warning: non-portable code. This is due to a bug in glibc's
179  // getopt() which keeps internal state about an old argument vector
180  // (argc, argv) from last call and tries to scan them when a new
181  // argument vector (argc, argv) is passed. As the old vector may not
182  // be main()'s arguments, but heap allocated and may have been freed
183  // since, this becomes a use after free and results in random
184  // behavior. According to the NOTES section in glibc getopt()'s
185  // manpage, setting optind=0 resets getopt()'s state. Though this is
186  // not required in our usage of getopt(), the bug still happens
187  // unless we set optind=0.
188  //
189  // Setting optind=0 is non-portable code.
190  optind = 0;
191 #else
192  optind = 1;
193 #endif
194 
195  // optreset is declared on BSD systems and is used to reset internal
196  // state of getopt(). When parsing command line arguments multiple
197  // times with getopt() the optreset must be set to 1 every time before
198  // parsing starts. Failing to do so will result in random behavior of
199  // getopt().
200 #ifdef HAVE_OPTRESET
201  optreset = 1;
202 #endif
203 
204  opterr = 0;
205 
206  // Reset values of class members
207  reset();
208 
209  // Informs if program has been run with 'h' or 'v' option.
210  bool help_or_version_mode = initialize(argc, argv, print_cmd_line);
211  if (!help_or_version_mode) {
212  validate();
213  }
214  return (help_or_version_mode);
215 }
216 
217 const int LONG_OPT_SCENARIO = 300;
218 
219 bool
220 CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
221  int opt = 0; // Subsequent options returned by getopt()
222  std::string drop_arg; // Value of -D<value>argument
223  size_t percent_loc = 0; // Location of % sign in -D<value>
224  double drop_percent = 0; // % value (1..100) in -D<value%>
225  int num_drops = 0; // Max number of drops specified in -D<value>
226  int num_req = 0; // Max number of dropped
227  // requests in -n<max-drops>
228  int offset_arg = 0; // Temporary variable holding offset arguments
229  std::string sarg; // Temporary variable for string args
230 
231  std::ostringstream stream;
232  stream << "perfdhcp";
233  int num_mac_list_files = 0;
234  int num_subnet_list_files = 0;
235 
236  struct option long_options[] = {
237  {"scenario", required_argument, 0, LONG_OPT_SCENARIO},
238  {0, 0, 0, 0}
239  };
240 
241  // In this section we collect argument values from command line
242  // they will be tuned and validated elsewhere
243  while((opt = getopt_long(argc, argv,
244  "huv46A:r:t:R:b:n:p:d:D:l:P:a:L:N:M:s:iBc1"
245  "J:T:X:O:o:E:S:I:x:W:w:e:f:F:g:C:y:Y:",
246  long_options, NULL)) != -1) {
247  stream << " -" << static_cast<char>(opt);
248  if (optarg) {
249  stream << " " << optarg;
250  }
251  switch (opt) {
252  case '1':
253  use_first_ = true;
254  break;
255 
256  // Simulate DHCPv6 relayed traffic.
257  case 'A':
258  // @todo: At the moment we only support simulating a single relay
259  // agent. In the future we should extend it to up to 32.
260  // See comment in https://github.com/isc-projects/kea/pull/22#issuecomment-243405600
261  v6_relay_encapsulation_level_ =
262  static_cast<uint8_t>(positiveInteger("-A<encapsulation-level> must"
263  " be a positive integer"));
264  if (v6_relay_encapsulation_level_ != 1) {
265  isc_throw(isc::InvalidParameter, "-A only supports 1 at the moment.");
266  }
267  break;
268 
269  case 'u':
270  addr_unique_ = true;
271  break;
272 
273  case '4':
274  check(ipversion_ == 6, "IP version already set to 6");
275  ipversion_ = 4;
276  break;
277 
278  case '6':
279  check(ipversion_ == 4, "IP version already set to 4");
280  ipversion_ = 6;
281  break;
282 
283  case 'b':
284  check(base_.size() > 3, "-b<value> already specified,"
285  " unexpected occurrence of 5th -b<value>");
286  base_.push_back(optarg);
287  decodeBase(base_.back());
288  break;
289 
290  case 'B':
291  broadcast_ = true;
292  break;
293 
294  case 'c':
295  rapid_commit_ = true;
296  break;
297 
298  case 'C':
299  clean_report_ = true;
300  clean_report_separator_ = optarg;
301  break;
302 
303  case 'd':
304  check(drop_time_set_ > 1,
305  "maximum number of drops already specified, "
306  "unexpected 3rd occurrence of -d<value>");
307  try {
308  drop_time_[drop_time_set_] =
309  boost::lexical_cast<double>(optarg);
310  } catch (const boost::bad_lexical_cast&) {
312  "value of drop time: -d<value>"
313  " must be positive number");
314  }
315  check(drop_time_[drop_time_set_] <= 0.,
316  "drop-time must be a positive number");
317  drop_time_set_ = true;
318  break;
319 
320  case 'D':
321  drop_arg = std::string(optarg);
322  percent_loc = drop_arg.find('%');
323  check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
324  "values of maximum drops: -D<value> already "
325  "specified, unexpected 3rd occurrence of -D<value>");
326  if ((percent_loc) != std::string::npos) {
327  try {
328  drop_percent =
329  boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
330  } catch (const boost::bad_lexical_cast&) {
332  "value of drop percentage: -D<value%>"
333  " must be 0..100");
334  }
335  check((drop_percent <= 0) || (drop_percent >= 100),
336  "value of drop percentage: -D<value%> must be 0..100");
337  max_pdrop_.push_back(drop_percent);
338  } else {
339  num_drops = positiveInteger("value of max drops number:"
340  " -D<value> must be a positive integer");
341  max_drop_.push_back(num_drops);
342  }
343  break;
344 
345  case 'e':
346  initLeaseType();
347  break;
348 
349  case 'E':
350  elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
351  " must not be a negative integer");
352  break;
353 
354  case 'f':
355  renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
356  " must be a positive integer");
357  break;
358 
359  case 'F':
360  release_rate_ = positiveInteger("value of the release rate:"
361  " -F<release-rate> must be a"
362  " positive integer");
363  break;
364 
365  case 'g': {
366  auto optarg_text = std::string(optarg);
367  if (optarg_text == "single") {
368  single_thread_mode_ = true;
369  } else if (optarg_text == "multi") {
370  single_thread_mode_ = false;
371  } else {
372  isc_throw(InvalidParameter, "value of thread mode (-g) '" << optarg << "' is wrong - should be '-g single' or '-g multi'");
373  }
374  break;
375  }
376  case 'h':
377  usage();
378  return (true);
379 
380  case 'i':
381  exchange_mode_ = DO_SA;
382  break;
383 
384  case 'I':
385  rip_offset_ = positiveInteger("value of ip address offset:"
386  " -I<value> must be a"
387  " positive integer");
388  break;
389 
390  case 'J':
391  check(num_subnet_list_files >= 1, "only one -J option can be specified");
392  num_subnet_list_files++;
393  relay_addr_list_file_ = std::string(optarg);
394  loadRelayAddr();
395  break;
396 
397  case 'l':
398  localname_ = std::string(optarg);
399  initIsInterface();
400  break;
401 
402  case 'L':
403  local_port_ = nonNegativeInteger("value of local port:"
404  " -L<value> must not be a"
405  " negative integer");
406  check(local_port_ >
407  static_cast<int>(std::numeric_limits<uint16_t>::max()),
408  "local-port must be lower than " +
409  boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
410  break;
411 
412  case 'N':
413  remote_port_ = nonNegativeInteger("value of remote port:"
414  " -L<value> must not be a"
415  " negative integer");
416  check(remote_port_ >
417  static_cast<int>(std::numeric_limits<uint16_t>::max()),
418  "remote-port must be lower than " +
419  boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
420  break;
421 
422  case 'M':
423  check(num_mac_list_files >= 1, "only one -M option can be specified");
424  num_mac_list_files++;
425  mac_list_file_ = std::string(optarg);
426  loadMacs();
427  break;
428 
429  case 'W':
430  exit_wait_time_ = nonNegativeInteger("value of exist wait time: "
431  "-W<value> must not be a "
432  "negative integer");
433  break;
434 
435  case 'n':
436  num_req = positiveInteger("value of num-request:"
437  " -n<value> must be a positive integer");
438  if (num_request_.size() >= 2) {
440  "value of maximum number of requests: -n<value> "
441  "already specified, unexpected 3rd occurrence"
442  " of -n<value>");
443  }
444  num_request_.push_back(num_req);
445  break;
446 
447  case 'O':
448  if (rnd_offset_.size() < 2) {
449  offset_arg = positiveInteger("value of random offset: "
450  "-O<value> must be greater than 3");
451  } else {
453  "random offsets already specified,"
454  " unexpected 3rd occurrence of -O<value>");
455  }
456  check(offset_arg < 3, "value of random random-offset:"
457  " -O<value> must be greater than 3 ");
458  rnd_offset_.push_back(offset_arg);
459  break;
460  case 'o': {
461 
462  // we must know how to contruct the option: whether it's v4 or v6.
463  check( (ipversion_ != 4) && (ipversion_ != 6),
464  "-4 or -6 must be explicitly specified before -o is used.");
465 
466  // custom option (expected format: code,hexstring)
467  std::string opt_text = std::string(optarg);
468  size_t coma_loc = opt_text.find(',');
469  check(coma_loc == std::string::npos,
470  "-o option must provide option code, a coma and hexstring for"
471  " the option content, e.g. -o60,646f63736973 for sending option"
472  " 60 (class-id) with the value 'docsis'");
473  int code = 0;
474 
475  // Try to parse the option code
476  try {
477  code = boost::lexical_cast<int>(opt_text.substr(0,coma_loc));
478  check(code <= 0, "Option code can't be negative");
479  } catch (const boost::bad_lexical_cast&) {
480  isc_throw(InvalidParameter, "Invalid option code specified for "
481  "-o option, expected format: -o<integer>,<hexstring>");
482  }
483 
484  // Now try to interpret the hexstring
485  opt_text = opt_text.substr(coma_loc + 1);
486  std::vector<uint8_t> bin;
487  try {
488  isc::util::encode::decodeHex(opt_text, bin);
489  } catch (const BadValue& e) {
490  isc_throw(InvalidParameter, "Error during encoding option -o:"
491  << e.what());
492  }
493 
494  // Create and remember the option.
495  OptionPtr opt(new Option(ipversion_ == 4 ? Option::V4 : Option::V6,
496  code, bin));
497  extra_opts_.insert(make_pair(code, opt));
498  break;
499  }
500  case 'p':
501  period_ = positiveInteger("value of test period:"
502  " -p<value> must be a positive integer");
503  break;
504 
505  case 'P':
506  preload_ = nonNegativeInteger("number of preload packets:"
507  " -P<value> must not be "
508  "a negative integer");
509  break;
510 
511  case 'r':
512  rate_ = positiveInteger("value of rate:"
513  " -r<value> must be a positive integer");
514  break;
515 
516  case 'R':
517  initClientsNum();
518  break;
519 
520  case 's':
521  seed_ = static_cast<unsigned int>
522  (nonNegativeInteger("value of seed:"
523  " -s <seed> must be non-negative integer"));
524  seeded_ = seed_ > 0 ? true : false;
525  break;
526 
527  case 'S':
528  sid_offset_ = positiveInteger("value of server id offset:"
529  " -S<value> must be a"
530  " positive integer");
531  break;
532 
533  case 't':
534  report_delay_ = positiveInteger("value of report delay:"
535  " -t<value> must be a"
536  " positive integer");
537  break;
538 
539  case 'T':
540  if (template_file_.size() < 2) {
541  sarg = nonEmptyString("template file name not specified,"
542  " expected -T<filename>");
543  template_file_.push_back(sarg);
544  } else {
546  "template files are already specified,"
547  " unexpected 3rd -T<filename> occurrence");
548  }
549  break;
550 
551  case 'v':
552  version();
553  return (true);
554 
555  case 'w':
556  wrapped_ = nonEmptyString("command for wrapped mode:"
557  " -w<command> must be specified");
558  break;
559 
560  case 'x':
561  diags_ = nonEmptyString("value of diagnostics selectors:"
562  " -x<value> must be specified");
563  break;
564 
565  case 'X':
566  if (xid_offset_.size() < 2) {
567  offset_arg = positiveInteger("value of transaction id:"
568  " -X<value> must be a"
569  " positive integer");
570  } else {
572  "transaction ids already specified,"
573  " unexpected 3rd -X<value> occurrence");
574  }
575  xid_offset_.push_back(offset_arg);
576  break;
577 
578  case 'Y':
579  wait_for_elapsed_time_ = nonNegativeInteger("value of time:"
580  " -Y<value> must be a non negative integer");
581  break;
582 
583  case 'y':
584  increased_elapsed_time_ = positiveInteger("value of time:"
585  " -y<value> must be a positive integer");
586  break;
587 
588  case LONG_OPT_SCENARIO: {
589  auto optarg_text = std::string(optarg);
590  if (optarg_text == "basic") {
591  scenario_ = Scenario::BASIC;
592  } else if (optarg_text == "avalanche") {
593  scenario_ = Scenario::AVALANCHE;
594  } else {
595  isc_throw(InvalidParameter, "scenario value '" << optarg << "' is wrong - should be 'basic' or 'avalanche'");
596  }
597  break;
598  }
599  default:
600  isc_throw(isc::InvalidParameter, "wrong command line option");
601  }
602  }
603 
604  // If the IP version was not specified in the
605  // command line, assume IPv4.
606  if (ipversion_ == 0) {
607  ipversion_ = 4;
608  }
609 
610  // If template packet files specified for both DISCOVER/SOLICIT
611  // and REQUEST/REPLY exchanges make sure we have transaction id
612  // and random duid offsets for both exchanges. We will duplicate
613  // value specified as -X<value> and -R<value> for second
614  // exchange if user did not specified otherwise.
615  if (template_file_.size() > 1) {
616  if (xid_offset_.size() == 1) {
617  xid_offset_.push_back(xid_offset_[0]);
618  }
619  if (rnd_offset_.size() == 1) {
620  rnd_offset_.push_back(rnd_offset_[0]);
621  }
622  }
623 
624  // Get server argument
625  // NoteFF02::1:2 and FF02::1:3 are defined in RFC 8415 as
626  // All_DHCP_Relay_Agents_and_Servers and All_DHCP_Servers
627  // addresses
628  check(optind < argc -1, "extra arguments?");
629  if (optind == argc - 1) {
630  server_name_ = argv[optind];
631  stream << " " << server_name_;
632  // Decode special cases
633  if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
634  broadcast_ = true;
635  // Use broadcast address as server name.
636  server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
637  } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
638  server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
639  } else if ((ipversion_ == 6) &&
640  (server_name_.compare("servers") == 0)) {
641  server_name_ = ALL_DHCP_SERVERS;
642  }
643  }
644  if (!getCleanReport()) {
645  if (print_cmd_line) {
646  std::cout << "Running: " << stream.str() << std::endl;
647  }
648 
649  if (scenario_ == Scenario::BASIC) {
650  std::cout << "Scenario: basic." << std::endl;
651  } else if (scenario_ == Scenario::AVALANCHE) {
652  std::cout << "Scenario: avalanche." << std::endl;
653  }
654 
655  if (!isSingleThreaded()) {
656  std::cout << "Multi-thread mode enabled." << std::endl;
657  }
658  }
659 
660  // Handle the local '-l' address/interface
661  if (!localname_.empty()) {
662  if (server_name_.empty()) {
663  if (is_interface_ && (ipversion_ == 4)) {
664  broadcast_ = true;
665  server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
666  } else if (is_interface_ && (ipversion_ == 6)) {
667  server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
668  }
669  }
670  }
671  if (server_name_.empty()) {
673  "without an interface, server is required");
674  }
675 
676  // If DUID is not specified from command line we need to
677  // generate one.
678  if (duid_template_.empty()) {
679  generateDuidTemplate();
680  }
681  return (false);
682 }
683 
684 void
685 CommandOptions::initClientsNum() {
686  const std::string errmsg =
687  "value of -R <value> must be non-negative integer";
688 
689  try {
690  // Declare clients_num as a 64-bit signed value to
691  // be able to detect negative values provided
692  // by user. We would not detect negative values
693  // if we casted directly to unsigned value.
694  long long clients_num = boost::lexical_cast<long long>(optarg);
695  check(clients_num < 0, errmsg);
696  clients_num_ = boost::lexical_cast<uint32_t>(optarg);
697  } catch (const boost::bad_lexical_cast&) {
699  }
700 }
701 
702 void
703 CommandOptions::initIsInterface() {
704  is_interface_ = false;
705  if (!localname_.empty()) {
707  if (iface_mgr.getIface(localname_) != NULL) {
708  is_interface_ = true;
709  }
710  }
711 }
712 
713 void
714 CommandOptions::decodeBase(const std::string& base) {
715  std::string b(base);
716  boost::algorithm::to_lower(b);
717 
718  // Currently we only support mac and duid
719  if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) {
720  decodeMacBase(b);
721  } else if (b.substr(0, 5) == "duid=") {
722  decodeDuid(b);
723  } else {
725  "base value not provided as -b<value>,"
726  " expected -b mac=<mac> or -b duid=<duid>");
727  }
728 }
729 
730 void
731 CommandOptions::decodeMacBase(const std::string& base) {
732  // Strip string from mac=
733  size_t found = base.find('=');
734  static const char* errmsg = "expected -b<base> format for"
735  " mac address is -b mac=00::0C::01::02::03::04 or"
736  " -b mac=00:0C:01:02:03:04";
737  check(found == std::string::npos, errmsg);
738 
739  // Decode mac address to vector of uint8_t
740  std::istringstream s1(base.substr(found + 1));
741  std::string token;
742  mac_template_.clear();
743  // Get pieces of MAC address separated with : (or even ::)
744  while (std::getline(s1, token, ':')) {
745  // Convert token to byte value using std::istringstream
746  if (token.length() > 0) {
747  unsigned int ui = 0;
748  try {
749  // Do actual conversion
750  ui = convertHexString(token);
751  } catch (const isc::InvalidParameter&) {
753  "invalid characters in MAC provided");
754 
755  }
756  // If conversion succeeded store byte value
757  mac_template_.push_back(ui);
758  }
759  }
760  // MAC address must consist of 6 octets, otherwise it is invalid
761  check(mac_template_.size() != 6, errmsg);
762 }
763 
764 void
765 CommandOptions::decodeDuid(const std::string& base) {
766  // Strip argument from duid=
767  std::vector<uint8_t> duid_template;
768  size_t found = base.find('=');
769  check(found == std::string::npos, "expected -b<base>"
770  " format for duid is -b duid=<duid>");
771  std::string b = base.substr(found + 1);
772 
773  // DUID must have even number of digits and must not be longer than 64 bytes
774  check(b.length() & 1, "odd number of hexadecimal digits in duid");
775  check(b.length() > 128, "duid too large");
776  check(b.length() == 0, "no duid specified");
777 
778  // Turn pairs of hexadecimal digits into vector of octets
779  for (size_t i = 0; i < b.length(); i += 2) {
780  unsigned int ui = 0;
781  try {
782  // Do actual conversion
783  ui = convertHexString(b.substr(i, 2));
784  } catch (const isc::InvalidParameter&) {
786  "invalid characters in DUID provided,"
787  " expected hex digits");
788  }
789  duid_template.push_back(static_cast<uint8_t>(ui));
790  }
791  // @todo Get rid of this limitation when we manage add support
792  // for DUIDs other than LLT. Shorter DUIDs may be useful for
793  // server testing purposes.
794  check(duid_template.size() < 6, "DUID must be at least 6 octets long");
795  // Assign the new duid only if successfully generated.
796  std::swap(duid_template, duid_template_);
797 }
798 
799 void
800 CommandOptions::generateDuidTemplate() {
801  using namespace boost::posix_time;
802  // Duid template will be most likely generated only once but
803  // it is ok if it is called more then once so we simply
804  // regenerate it and discard previous value.
805  duid_template_.clear();
806  const uint8_t duid_template_len = 14;
807  duid_template_.resize(duid_template_len);
808  // The first four octets consist of DUID LLT and hardware type.
809  duid_template_[0] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) >> 8);
810  duid_template_[1] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) & 0xff);
811  duid_template_[2] = HWTYPE_ETHERNET >> 8;
812  duid_template_[3] = HWTYPE_ETHERNET & 0xff;
813 
814  // As described in RFC 8415: 'the time value is the time
815  // that the DUID is generated represented in seconds
816  // since midnight (UTC), January 1, 2000, modulo 2^32.'
817  ptime now = microsec_clock::universal_time();
818  ptime duid_epoch(from_iso_string("20000101T000000"));
819  time_period period(duid_epoch, now);
820  uint32_t duration_sec = htonl(period.length().total_seconds());
821  memcpy(&duid_template_[4], &duration_sec, 4);
822 
823  // Set link layer address (6 octets). This value may be
824  // randomized before sending a packet to simulate different
825  // clients.
826  memcpy(&duid_template_[8], &mac_template_[0], 6);
827 }
828 
829 uint8_t
830 CommandOptions::convertHexString(const std::string& text) const {
831  unsigned int ui = 0;
832  // First, check if we are dealing with hexadecimal digits only
833  for (size_t i = 0; i < text.length(); ++i) {
834  if (!std::isxdigit(text[i])) {
836  "The following digit: " << text[i] << " in "
837  << text << "is not hexadecimal");
838  }
839  }
840  // If we are here, we have valid string to convert to octet
841  std::istringstream text_stream(text);
842  text_stream >> std::hex >> ui >> std::dec;
843  // Check if for some reason we have overflow - this should never happen!
844  if (ui > 0xFF) {
845  isc_throw(isc::InvalidParameter, "Can't convert more than"
846  " two hex digits to byte");
847  }
848  return ui;
849 }
850 
851 bool CommandOptions::validateIP(const std::string& line) {
852  try {
853  isc::asiolink::IOAddress ip_address(line);
854  if ((getIpVersion() == 4 && !ip_address.isV4()) ||
855  (getIpVersion() == 6 && !ip_address.isV6())) {
856  return (true);
857  }
858  } catch (const isc::asiolink::IOError& e) {
859  return (true);
860  }
861  relay_addr_list_.push_back(line);
862  multi_subnet_ = true;
863  return (false);
864 }
865 
866 void CommandOptions::loadRelayAddr() {
867  std::string line;
868  std::ifstream infile(relay_addr_list_file_.c_str());
869  size_t cnt = 0;
870  while (std::getline(infile, line)) {
871  cnt++;
872  stringstream tmp;
873  tmp << "invalid address or wrong address version in line: " << cnt;
874  check(validateIP(line), tmp.str());
875  }
876  check(cnt == 0, "file with addresses is empty!");
877 }
878 
879 void CommandOptions::loadMacs() {
880  std::string line;
881  std::ifstream infile(mac_list_file_.c_str());
882  size_t cnt = 0;
883  while (std::getline(infile, line)) {
884  cnt++;
885  stringstream tmp;
886  tmp << "invalid mac in input line " << cnt;
887  // Let's print more meaningful error that contains line with error.
888  check(decodeMacString(line), tmp.str());
889  }
890 }
891 
892 bool CommandOptions::decodeMacString(const std::string& line) {
893  // decode mac string into a vector of uint8_t returns true in case of error.
894  std::istringstream s(line);
895  std::string token;
896  std::vector<uint8_t> mac;
897  while(std::getline(s, token, ':')) {
898  // Convert token to byte value using std::istringstream
899  if (token.length() > 0) {
900  unsigned int ui = 0;
901  try {
902  // Do actual conversion
903  ui = convertHexString(token);
904  } catch (const isc::InvalidParameter&) {
905  return (true);
906  }
907  // If conversion succeeded store byte value
908  mac.push_back(ui);
909  }
910  }
911  mac_list_.push_back(mac);
912  return (false);
913 }
914 
915 void
916 CommandOptions::validate() {
917  check((getIpVersion() != 4) && (isBroadcast() != 0),
918  "-B is not compatible with IPv6 (-6)");
919  check((getIpVersion() != 6) && (isRapidCommit() != 0),
920  "-6 (IPv6) must be set to use -c");
921  check(getIpVersion() == 4 && isUseRelayedV6(),
922  "Can't use -4 with -A, it's a V6 only option.");
923  check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
924  "second -n<num-request> is not compatible with -i");
925  check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
926  "-6 option must be used if lease type other than '-e address-only'"
927  " is specified");
928  check(!getTemplateFiles().empty() &&
930  "template files may be only used with '-e address-only'");
931  check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
932  "second -d<drop-time> is not compatible with -i");
933  check((getExchangeMode() == DO_SA) &&
934  ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
935  "second -D<max-drop> is not compatible with -i");
936  check((getExchangeMode() == DO_SA) && (isUseFirst()),
937  "-1 is not compatible with -i");
938  check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
939  "second -T<template-file> is not compatible with -i");
940  check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
941  "second -X<xid-offset> is not compatible with -i");
942  check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
943  "second -O<random-offset is not compatible with -i");
944  check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
945  "-E<time-offset> is not compatible with -i");
946  check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
947  "-S<srvid-offset> is not compatible with -i");
948  check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
949  "-I<ip-offset> is not compatible with -i");
950  check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
951  "-f<renew-rate> is not compatible with -i");
952  check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
953  "-F<release-rate> is not compatible with -i");
954  check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
955  "-i must be set to use -c");
956  check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
957  "The sum of Renew rate (-f<renew-rate>) and Release rate"
958  " (-F<release-rate>) must not be greater than the exchange"
959  " rate specified as -r<rate>");
960  check((getRate() == 0) && (getRenewRate() != 0),
961  "Renew rate specified as -f<renew-rate> must not be specified"
962  " when -r<rate> parameter is not specified");
963  check((getRate() == 0) && (getReleaseRate() != 0),
964  "Release rate specified as -F<release-rate> must not be specified"
965  " when -r<rate> parameter is not specified");
966  check((getTemplateFiles().size() < getTransactionIdOffset().size()),
967  "-T<template-file> must be set to use -X<xid-offset>");
968  check((getTemplateFiles().size() < getRandomOffset().size()),
969  "-T<template-file> must be set to use -O<random-offset>");
970  check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
971  "second/request -T<template-file> must be set to use -E<time-offset>");
972  check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
973  "second/request -T<template-file> must be set to "
974  "use -S<srvid-offset>");
975  check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
976  "second/request -T<template-file> must be set to "
977  "use -I<ip-offset>");
978  check((!getMacListFile().empty() && base_.size() > 0),
979  "Can't use -b with -M option");
980  check((getWaitForElapsedTime() == -1 && getIncreaseElapsedTime() != -1),
981  "Option -y can't be used without -Y");
982  check((getWaitForElapsedTime() != -1 && getIncreaseElapsedTime() == -1),
983  "Option -Y can't be used without -y");
984  auto nthreads = std::thread::hardware_concurrency();
985  if (nthreads == 1 && isSingleThreaded() == false) {
986  std::cout << "WARNING: Currently system can run only 1 thread in parallel." << std::endl
987  << "WARNING: Better results are achieved when run in single-threaded mode." << std::endl
988  << "WARNING: To switch use -g single option." << std::endl;
989  } else if (nthreads > 1 && isSingleThreaded()) {
990  std::cout << "WARNING: Currently system can run more than 1 thread in parallel." << std::endl
991  << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl
992  << "WARNING: To switch use -g multi option." << std::endl;
993  }
994 
995  if (scenario_ == Scenario::AVALANCHE) {
996  check(getClientsNum() <= 0,
997  "in case of avalanche scenario number\nof clients must be specified"
998  " using -R option explicitly");
999 
1000  // in case of AVALANCHE drops ie. long responses should not be observed by perfdhcp
1001  double dt[2] = { 1000.0, 1000.0 };
1002  drop_time_.assign(dt, dt + 2);
1003  if (drop_time_set_) {
1004  std::cout << "INFO: in avalanche scenario drop time is ignored" << std::endl;
1005  }
1006  }
1007 }
1008 
1009 void
1010 CommandOptions::check(bool condition, const std::string& errmsg) const {
1011  // The same could have been done with macro or just if statement but
1012  // we prefer functions to macros here
1013  std::ostringstream stream;
1014  stream << errmsg << "\n";
1015  if (condition) {
1017  }
1018 }
1019 
1020 int
1021 CommandOptions::positiveInteger(const std::string& errmsg) const {
1022  try {
1023  int value = boost::lexical_cast<int>(optarg);
1024  check(value <= 0, errmsg);
1025  return (value);
1026  } catch (const boost::bad_lexical_cast&) {
1027  isc_throw(InvalidParameter, errmsg);
1028  }
1029 }
1030 
1031 int
1032 CommandOptions::nonNegativeInteger(const std::string& errmsg) const {
1033  try {
1034  int value = boost::lexical_cast<int>(optarg);
1035  check(value < 0, errmsg);
1036  return (value);
1037  } catch (const boost::bad_lexical_cast&) {
1038  isc_throw(InvalidParameter, errmsg);
1039  }
1040 }
1041 
1042 std::string
1043 CommandOptions::nonEmptyString(const std::string& errmsg) const {
1044  std::string sarg = optarg;
1045  if (sarg.length() == 0) {
1047  }
1048  return sarg;
1049 }
1050 
1051 void
1052 CommandOptions::initLeaseType() {
1053  std::string lease_type_arg = optarg;
1054  lease_type_.fromCommandLine(lease_type_arg);
1055 }
1056 
1057 void
1059  std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
1060  if (exchange_mode_ == DO_SA) {
1061  if (ipversion_ == 4) {
1062  std::cout << "DISCOVER-OFFER only" << std::endl;
1063  } else {
1064  std::cout << "SOLICIT-ADVERTISE only" << std::endl;
1065  }
1066  }
1067  std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
1068  if (rate_ != 0) {
1069  std::cout << "rate[1/s]=" << rate_ << std::endl;
1070  }
1071  if (getRenewRate() != 0) {
1072  std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
1073  }
1074  if (getReleaseRate() != 0) {
1075  std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
1076  }
1077  if (report_delay_ != 0) {
1078  std::cout << "report[s]=" << report_delay_ << std::endl;
1079  }
1080  if (clients_num_ != 0) {
1081  std::cout << "clients=" << clients_num_ << std::endl;
1082  }
1083  for (size_t i = 0; i < base_.size(); ++i) {
1084  std::cout << "base[" << i << "]=" << base_[i] << std::endl;
1085  }
1086  for (size_t i = 0; i < num_request_.size(); ++i) {
1087  std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl;
1088  }
1089  if (period_ != 0) {
1090  std::cout << "test-period=" << period_ << std::endl;
1091  }
1092  for (size_t i = 0; i < drop_time_.size(); ++i) {
1093  std::cout << "drop-time[" << i << "]=" << drop_time_[i] << std::endl;
1094  }
1095  for (size_t i = 0; i < max_drop_.size(); ++i) {
1096  std::cout << "max-drop{" << i << "]=" << max_drop_[i] << std::endl;
1097  }
1098  for (size_t i = 0; i < max_pdrop_.size(); ++i) {
1099  std::cout << "max-pdrop{" << i << "]=" << max_pdrop_[i] << std::endl;
1100  }
1101  if (preload_ != 0) {
1102  std::cout << "preload=" << preload_ << std::endl;
1103  }
1104  if (getLocalPort() != 0) {
1105  std::cout << "local-port=" << local_port_ << std::endl;
1106  }
1107  if (getRemotePort() != 0) {
1108  std::cout << "remote-port=" << remote_port_ << std::endl;
1109  }
1110  if (seeded_) {
1111  std::cout << "seed=" << seed_ << std::endl;
1112  }
1113  if (broadcast_) {
1114  std::cout << "broadcast" << std::endl;
1115  }
1116  if (rapid_commit_) {
1117  std::cout << "rapid-commit" << std::endl;
1118  }
1119  if (use_first_) {
1120  std::cout << "use-first" << std::endl;
1121  }
1122  if (!mac_list_file_.empty()) {
1123  std::cout << "mac-list-file=" << mac_list_file_ << std::endl;
1124  }
1125  for (size_t i = 0; i < template_file_.size(); ++i) {
1126  std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
1127  }
1128  for (size_t i = 0; i < xid_offset_.size(); ++i) {
1129  std::cout << "xid-offset[" << i << "]=" << xid_offset_[i] << std::endl;
1130  }
1131  if (elp_offset_ != 0) {
1132  std::cout << "elp-offset=" << elp_offset_ << std::endl;
1133  }
1134  for (size_t i = 0; i < rnd_offset_.size(); ++i) {
1135  std::cout << "rnd-offset[" << i << "]=" << rnd_offset_[i] << std::endl;
1136  }
1137  if (sid_offset_ != 0) {
1138  std::cout << "sid-offset=" << sid_offset_ << std::endl;
1139  }
1140  if (rip_offset_ != 0) {
1141  std::cout << "rip-offset=" << rip_offset_ << std::endl;
1142  }
1143  if (!diags_.empty()) {
1144  std::cout << "diagnostic-selectors=" << diags_ << std::endl;
1145  }
1146  if (!wrapped_.empty()) {
1147  std::cout << "wrapped=" << wrapped_ << std::endl;
1148  }
1149  if (!localname_.empty()) {
1150  if (is_interface_) {
1151  std::cout << "interface=" << localname_ << std::endl;
1152  } else {
1153  std::cout << "local-addr=" << localname_ << std::endl;
1154  }
1155  }
1156  if (!server_name_.empty()) {
1157  std::cout << "server=" << server_name_ << std::endl;
1158  }
1159  if (single_thread_mode_) {
1160  std::cout << "single-thread-mode" << std::endl;
1161  } else {
1162  std::cout << "multi-thread-mode" << std::endl;
1163  }
1164 }
1165 
1166 void
1168  std::cout <<
1169 R"(perfdhcp [-1] [-4 | -6] [-A encapsulation-level] [-b base] [-B] [-c]
1170  [-C separator] [-d drop-time] [-D max-drop] [-e lease-type]
1171  [-E time-offset] [-f renew-rate] [-F release-rate] [-g thread-mode]
1172  [-h] [-i] [-I ip-offset] [-J remote-address-list-file]
1173  [-l local-address|interface] [-L local-port] [-M mac-list-file]
1174  [-n num-request] [-N remote-port] [-O random-offset]
1175  [-o code,hexstring] [-p test-period] [-P preload] [-r rate]
1176  [-R num-clients] [-s seed] [-S srvid-offset] [--scenario name]
1177  [-t report] [-T template-file] [-u] [-v] [-W exit-wait-time]
1178  [-w script_name] [-x diagnostic-selector] [-X xid-offset] [server]
1179 
1180 The [server] argument is the name/address of the DHCP server to
1181 contact. For DHCPv4 operation, exchanges are initiated by
1182 transmitting a DHCP DISCOVER to this address.
1183 
1184 For DHCPv6 operation, exchanges are initiated by transmitting a DHCP
1185 SOLICIT to this address. In the DHCPv6 case, the special name 'all'
1186 can be used to refer to All_DHCP_Relay_Agents_and_Servers (the
1187 multicast address FF02::1:2), or the special name 'servers' to refer
1188 to All_DHCP_Servers (the multicast address FF05::1:3). The [server]
1189 argument is optional only in the case that -l is used to specify an
1190 interface, in which case [server] defaults to 'all'.
1191 
1192 The default is to perform a single 4-way exchange, effectively pinging
1193 the server.
1194 The -r option is used to set up a performance test, without
1195 it exchanges are initiated as fast as possible.
1196 The other scenario is an avalanche which is selected by
1197 --scenario avalanche. It first sends as many Discovery or Solicit
1198 messages as request in -R option then back off mechanism is used for
1199 each simulated client until all requests are answered. At the end
1200 time of whole scenario is reported.
1201 
1202 Options:
1203 -1: Take the server-ID option from the first received message.
1204 -4: DHCPv4 operation (default). This is incompatible with the -6 option.
1205 -6: DHCPv6 operation. This is incompatible with the -4 option.
1206 -b<base>: The base mac, duid, IP, etc, used to simulate different
1207  clients. This can be specified multiple times, each instance is
1208  in the <type>=<value> form, for instance:
1209  (and default) mac=00:0c:01:02:03:04.
1210 -d<drop-time>: Specify the time after which a request is treated as
1211  having been lost. The value is given in seconds and may contain a
1212  fractional component. The default is 1 second.
1213 -e<lease-type>: A type of lease being requested from the server. It
1214  may be one of the following: address-only, prefix-only or
1215  address-and-prefix. The address-only indicates that the regular
1216  address (v4 or v6) will be requested. The prefix-only indicates
1217  that the IPv6 prefix will be requested. The address-and-prefix
1218  indicates that both IPv6 address and prefix will be requested.
1219  The '-e prefix-only' and -'e address-and-prefix' must not be
1220  used with -4.
1221 -E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)
1222  elapsed-time option in the (second/request) template.
1223  The value 0 disables it.
1224 -F<release-rate>: Rate at which Release requests are sent to
1225  a server. This value is only valid when used in conjunction with
1226  the exchange rate (given by -r<rate>). Furthermore the sum of
1227  this value and the renew-rate (given by -f<rate>) must be equal
1228  to or less than the exchange rate.
1229 -f<renew-rate>: Rate at which DHCPv4 or DHCPv6 renew requests are sent
1230  to a server. This value is only valid when used in conjunction
1231  with the exchange rate (given by -r<rate>). Furthermore the sum of
1232  this value and the release-rate (given by -F<rate>) must be equal
1233  to or less than the exchange rate.
1234 -g<thread-mode>: 'single' or 'multi'. In multi-thread mode packets
1235  are received in separate thread. This allows better utilisation of CPUs.
1236  If more than 1 CPU is present then multi-thread mode is the default,
1237  otherwise single-thread is the default.
1238 -h: Print this help.
1239 -i: Do only the initial part of an exchange: DO or SA, depending on
1240  whether -6 is given.
1241 -I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP
1242  option / (DHCPv6) IA_NA option in the (second/request) template.
1243 -J<remote-address-list-file>: Text file that include multiple addresses.
1244  If provided perfdhcp will choose randomly one of addresses for each
1245  exchange.
1246 -l<local-addr|interface>: For DHCPv4 operation, specify the local
1247  hostname/address to use when communicating with the server. By
1248  default, the interface address through which traffic would
1249  normally be routed to the server is used.
1250  For DHCPv6 operation, specify the name of the network interface
1251  via which exchanges are initiated.
1252 -L<local-port>: Specify the local port to use
1253  (the value 0 means to use the default).
1254 -M<mac-list-file>: A text file containing a list of MAC addresses,
1255  one per line. If provided, a MAC address will be chosen randomly
1256  from this list for every new exchange. In the DHCPv6 case, MAC
1257  addresses are used to generate DUID-LLs. This parameter must not be
1258  used in conjunction with the -b parameter.
1259 -N<remote-port>: Specify the remote port to use
1260  (the value 0 means to use the default).
1261 -o<code,hexstring>: Send custom option with the specified code and the
1262  specified buffer in hexstring format.
1263 -O<random-offset>: Offset of the last octet to randomize in the template.
1264 -P<preload>: Initiate first <preload> exchanges back to back at startup.
1265 -r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)
1266  exchanges per second. A periodic report is generated showing the
1267  number of exchanges which were not completed, as well as the
1268  average response latency. The program continues until
1269  interrupted, at which point a final report is generated.
1270 -R<range>: Specify how many different clients are used. With 1
1271  (the default), all requests seem to come from the same client.
1272 -s<seed>: Specify the seed for randomization, making it repeatable.
1273 --scenario <name>: where name is 'basic' (default) or 'avalanche'.
1274 -S<srvid-offset>: Offset of the server-ID option in the
1275  (second/request) template.
1276 -T<template-file>: The name of a file containing the template to use
1277  as a stream of hexadecimal digits.
1278 -u: Enable checking address uniqueness. Lease valid lifetime should not be
1279  shorter than test duration and clients should not request address more than
1280  once without releasing it first.
1281 -v: Report the version number of this program.
1282 -W<time>: Specifies exit-wait-time parameter, that makes perfdhcp wait
1283  for <time> us after an exit condition has been met to receive all
1284  packets without sending any new packets. Expressed in microseconds.
1285 -w<wrapped>: Command to call with start/stop at the beginning/end of
1286  the program.
1287 -x<diagnostic-selector>: Include extended diagnostics in the output.
1288  <diagnostic-selector> is a string of single-keywords specifying
1289  the operations for which verbose output is desired. The selector
1290  key letters are:
1291  * 'a': print the decoded command line arguments
1292  * 'e': print the exit reason
1293  * 'i': print rate processing details
1294  * 'l': print received leases
1295  * 's': print first server-id
1296  * 't': when finished, print timers of all successful exchanges
1297  * 'T': when finished, print templates
1298 -X<xid-offset>: Transaction ID (aka. xid) offset in the template.
1299 -Y<time>: time in seconds after which perfdhcp will start sending
1300  messages with increased elapsed time option.
1301 -y<time>: period of time in seconds in which perfdhcp will be sending
1302  messages with increased elapsed time option.
1303 DHCPv4 only options:
1304 -B: Force broadcast handling.
1305 
1306 DHCPv6 only options:
1307 -c: Add a rapid commit option (exchanges will be SA).
1308 -A<encapsulation-level>: Specifies that relayed traffic must be
1309  generated. The argument specifies the level of encapsulation, i.e.
1310  how many relay agents are simulated. Currently the only supported
1311  <encapsulation-level> value is 1, which means that the generated
1312  traffic is an equivalent of the traffic passing through a single
1313  relay agent.
1314 
1315 The remaining options are typically used in conjunction with -r:
1316 
1317 -D<max-drop>: Abort the test immediately if max-drop requests have
1318  been dropped. max-drop must be a positive integer. If max-drop
1319  includes the suffix '%', it specifies a maximum percentage of
1320  requests that may be dropped before abort. In this case, testing
1321  of the threshold begins after 10 requests have been expected to
1322  be received.
1323 -n<num-request>: Initiate <num-request> transactions. No report is
1324  generated until all transactions have been initiated/waited-for,
1325  after which a report is generated and the program terminates.
1326 -p<test-period>: Send requests for the given test period, which is
1327  specified in the same manner as -d. This can be used as an
1328  alternative to -n, or both options can be given, in which case the
1329  testing is completed when either limit is reached.
1330 -t<report>: Delay in seconds between two periodic reports.
1331 -C<separator>: Output reduced, an argument is a separator for periodic
1332  (-t) reports generated in easy parsable mode. Data output won't be
1333  changed, remain identical as in -t option.
1334 
1335 Errors:
1336 - tooshort: received a too short message
1337 - orphans: received a message which doesn't match an exchange
1338  (duplicate, late or not related)
1339 - locallimit: reached to local system limits when sending a message.
1340 
1341 Exit status:
1342 The exit status is:
1343 0 on complete success.
1344 1 for a general error.
1345 2 if an error is found in the command line arguments.
1346 3 if there are no general failures in operation, but one or more
1347  exchanges are not successfully completed.
1348 )";
1349 }
1350 
1351 void
1353  std::cout << "VERSION: " << VERSION << std::endl;
1354 }
1355 
1356 
1357 } // namespace perfdhcp
1358 } // namespace isc
#define ALL_DHCP_SERVERS
Definition: dhcp6.h:294
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:894
int getCleanReport() const
Returns clean report mode.
bool parse(int argc, char **const argv, bool print_cmd_line=false)
Parse command line.
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
const char *const * perfdhcp_config_report
link-layer + time, see RFC3315, section 11.2
Definition: duid.h:40
void set(const Type lease_type)
Sets the lease type code.
#define DHCP_IPV4_BROADCAST_ADDRESS
Definition: dhcp4.h:42
int getServerIdOffset() const
Returns template offset for server-ID.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
bool is(const Type lease_type) const
Checks if lease type has the specified code.
STL namespace.
bool isUseFirst() const
Check if server-ID to be taken from first package.
Handles network interfaces, transmission and reception.
Definition: iface_mgr.h:632
std::vector< std::string > getTemplateFiles() const
Returns template file names.
uint8_t getIpVersion() const
Returns IP version.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
void fromCommandLine(const std::string &cmd_line_arg)
Sets the lease type from the command line argument.
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS
Definition: dhcp6.h:293
bool isBroadcast() const
Checks if broadcast address is to be used.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
void version() const
Print program version.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static void usage()
Print usage.
const int LONG_OPT_SCENARIO
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
void printCommandLine() const
Print command line arguments.
bool isSingleThreaded() const
Check if single-threaded mode is enabled.
LeaseType getLeaseType() const
\ brief Returns the type of lease being requested.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 (&#39;hex&#39;) format into the original data.
Definition: base_n.cc:474
A generic exception that is thrown when an unexpected error condition occurs.
std::vector< double > getDropTime() const
Returns drop time.
std::string toText() const
Return textual representation of the lease type.
bool includes(const Type lease_type) const
Checks if lease type implies request for the address, prefix (or both) as specified by the function a...
int getLocalPort() const
Returns local port number.
Defines the logger used by the top-level component of kea-lfc.
const char *const config_report[]
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
void reset()
Reset to defaults.
std::vector< int > getMaxDrop() const
Returns maximum drops number.
int getRemotePort() const
Returns remote port number.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
int getRate() const
Returns exchange rate.
int getWaitForElapsedTime() const
Returns time to wait for elapsed time increase.
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
int getIncreaseElapsedTime() const
Returns increased elapsed time.
std::string getMacListFile() const
Returns location of the file containing list of MAC addresses.
int getRequestedIpOffset() const
Returns template offset for requested IP.
bool isRapidCommit() const
Check if rapid commit option used.
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
uint32_t getClientsNum() const
Returns number of simulated clients.