Kea  1.9.9-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 as 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((getIpVersion() != 6) && (getReleaseRate() != 0),
924  "-F<release-rate> may be used with -6 (IPv6) only");
925  check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
926  "second -n<num-request> is not compatible with -i");
927  check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
928  "-6 option must be used if lease type other than '-e address-only'"
929  " is specified");
930  check(!getTemplateFiles().empty() &&
932  "template files may be only used with '-e address-only'");
933  check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
934  "second -d<drop-time> is not compatible with -i");
935  check((getExchangeMode() == DO_SA) &&
936  ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
937  "second -D<max-drop> is not compatible with -i");
938  check((getExchangeMode() == DO_SA) && (isUseFirst()),
939  "-1 is not compatible with -i");
940  check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
941  "second -T<template-file> is not compatible with -i");
942  check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
943  "second -X<xid-offset> is not compatible with -i");
944  check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
945  "second -O<random-offset is not compatible with -i");
946  check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
947  "-E<time-offset> is not compatible with -i");
948  check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
949  "-S<srvid-offset> is not compatible with -i");
950  check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
951  "-I<ip-offset> is not compatible with -i");
952  check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
953  "-f<renew-rate> is not compatible with -i");
954  check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
955  "-F<release-rate> is not compatible with -i");
956  check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
957  "-i must be set to use -c");
958  check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
959  "The sum of Renew rate (-f<renew-rate>) and Release rate"
960  " (-F<release-rate>) must not be greater than the exchange"
961  " rate specified as -r<rate>");
962  check((getRate() == 0) && (getRenewRate() != 0),
963  "Renew rate specified as -f<renew-rate> must not be specified"
964  " when -r<rate> parameter is not specified");
965  check((getRate() == 0) && (getReleaseRate() != 0),
966  "Release rate specified as -F<release-rate> must not be specified"
967  " when -r<rate> parameter is not specified");
968  check((getTemplateFiles().size() < getTransactionIdOffset().size()),
969  "-T<template-file> must be set to use -X<xid-offset>");
970  check((getTemplateFiles().size() < getRandomOffset().size()),
971  "-T<template-file> must be set to use -O<random-offset>");
972  check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
973  "second/request -T<template-file> must be set to use -E<time-offset>");
974  check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
975  "second/request -T<template-file> must be set to "
976  "use -S<srvid-offset>");
977  check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
978  "second/request -T<template-file> must be set to "
979  "use -I<ip-offset>");
980  check((!getMacListFile().empty() && base_.size() > 0),
981  "Can't use -b with -M option");
982  check((getWaitForElapsedTime() == -1 && getIncreaseElapsedTime() != -1),
983  "Option -y can't be used without -Y");
984  check((getWaitForElapsedTime() != -1 && getIncreaseElapsedTime() == -1),
985  "Option -Y can't be used without -y");
986  auto nthreads = std::thread::hardware_concurrency();
987  if (nthreads == 1 && isSingleThreaded() == false) {
988  std::cout << "WARNING: Currently system can run only 1 thread in parallel." << std::endl
989  << "WARNING: Better results are achieved when run in single-threaded mode." << std::endl
990  << "WARNING: To switch use -g single option." << std::endl;
991  } else if (nthreads > 1 && isSingleThreaded()) {
992  std::cout << "WARNING: Currently system can run more than 1 thread in parallel." << std::endl
993  << "WARNING: Better results are achieved when run in multi-threaded mode." << std::endl
994  << "WARNING: To switch use -g multi option." << std::endl;
995  }
996 
997  if (scenario_ == Scenario::AVALANCHE) {
998  check(getClientsNum() <= 0,
999  "in case of avalanche scenario number\nof clients must be specified"
1000  " using -R option explicitly");
1001 
1002  // in case of AVALANCHE drops ie. long responses should not be observed by perfdhcp
1003  double dt[2] = { 1000.0, 1000.0 };
1004  drop_time_.assign(dt, dt + 2);
1005  if (drop_time_set_) {
1006  std::cout << "INFO: in avalanche scenario drop time is ignored" << std::endl;
1007  }
1008  }
1009 }
1010 
1011 void
1012 CommandOptions::check(bool condition, const std::string& errmsg) const {
1013  // The same could have been done with macro or just if statement but
1014  // we prefer functions to macros here
1015  std::ostringstream stream;
1016  stream << errmsg << "\n";
1017  if (condition) {
1019  }
1020 }
1021 
1022 int
1023 CommandOptions::positiveInteger(const std::string& errmsg) const {
1024  try {
1025  int value = boost::lexical_cast<int>(optarg);
1026  check(value <= 0, errmsg);
1027  return (value);
1028  } catch (const boost::bad_lexical_cast&) {
1029  isc_throw(InvalidParameter, errmsg);
1030  }
1031 }
1032 
1033 int
1034 CommandOptions::nonNegativeInteger(const std::string& errmsg) const {
1035  try {
1036  int value = boost::lexical_cast<int>(optarg);
1037  check(value < 0, errmsg);
1038  return (value);
1039  } catch (const boost::bad_lexical_cast&) {
1040  isc_throw(InvalidParameter, errmsg);
1041  }
1042 }
1043 
1044 std::string
1045 CommandOptions::nonEmptyString(const std::string& errmsg) const {
1046  std::string sarg = optarg;
1047  if (sarg.length() == 0) {
1049  }
1050  return sarg;
1051 }
1052 
1053 void
1054 CommandOptions::initLeaseType() {
1055  std::string lease_type_arg = optarg;
1056  lease_type_.fromCommandLine(lease_type_arg);
1057 }
1058 
1059 void
1061  std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
1062  if (exchange_mode_ == DO_SA) {
1063  if (ipversion_ == 4) {
1064  std::cout << "DISCOVER-OFFER only" << std::endl;
1065  } else {
1066  std::cout << "SOLICIT-ADVERTISE only" << std::endl;
1067  }
1068  }
1069  std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
1070  if (rate_ != 0) {
1071  std::cout << "rate[1/s]=" << rate_ << std::endl;
1072  }
1073  if (getRenewRate() != 0) {
1074  std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
1075  }
1076  if (getReleaseRate() != 0) {
1077  std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
1078  }
1079  if (report_delay_ != 0) {
1080  std::cout << "report[s]=" << report_delay_ << std::endl;
1081  }
1082  if (clients_num_ != 0) {
1083  std::cout << "clients=" << clients_num_ << std::endl;
1084  }
1085  for (size_t i = 0; i < base_.size(); ++i) {
1086  std::cout << "base[" << i << "]=" << base_[i] << std::endl;
1087  }
1088  for (size_t i = 0; i < num_request_.size(); ++i) {
1089  std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl;
1090  }
1091  if (period_ != 0) {
1092  std::cout << "test-period=" << period_ << std::endl;
1093  }
1094  for (size_t i = 0; i < drop_time_.size(); ++i) {
1095  std::cout << "drop-time[" << i << "]=" << drop_time_[i] << std::endl;
1096  }
1097  for (size_t i = 0; i < max_drop_.size(); ++i) {
1098  std::cout << "max-drop{" << i << "]=" << max_drop_[i] << std::endl;
1099  }
1100  for (size_t i = 0; i < max_pdrop_.size(); ++i) {
1101  std::cout << "max-pdrop{" << i << "]=" << max_pdrop_[i] << std::endl;
1102  }
1103  if (preload_ != 0) {
1104  std::cout << "preload=" << preload_ << std::endl;
1105  }
1106  if (getLocalPort() != 0) {
1107  std::cout << "local-port=" << local_port_ << std::endl;
1108  }
1109  if (getRemotePort() != 0) {
1110  std::cout << "remote-port=" << remote_port_ << std::endl;
1111  }
1112  if (seeded_) {
1113  std::cout << "seed=" << seed_ << std::endl;
1114  }
1115  if (broadcast_) {
1116  std::cout << "broadcast" << std::endl;
1117  }
1118  if (rapid_commit_) {
1119  std::cout << "rapid-commit" << std::endl;
1120  }
1121  if (use_first_) {
1122  std::cout << "use-first" << std::endl;
1123  }
1124  if (!mac_list_file_.empty()) {
1125  std::cout << "mac-list-file=" << mac_list_file_ << std::endl;
1126  }
1127  for (size_t i = 0; i < template_file_.size(); ++i) {
1128  std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
1129  }
1130  for (size_t i = 0; i < xid_offset_.size(); ++i) {
1131  std::cout << "xid-offset[" << i << "]=" << xid_offset_[i] << std::endl;
1132  }
1133  if (elp_offset_ != 0) {
1134  std::cout << "elp-offset=" << elp_offset_ << std::endl;
1135  }
1136  for (size_t i = 0; i < rnd_offset_.size(); ++i) {
1137  std::cout << "rnd-offset[" << i << "]=" << rnd_offset_[i] << std::endl;
1138  }
1139  if (sid_offset_ != 0) {
1140  std::cout << "sid-offset=" << sid_offset_ << std::endl;
1141  }
1142  if (rip_offset_ != 0) {
1143  std::cout << "rip-offset=" << rip_offset_ << std::endl;
1144  }
1145  if (!diags_.empty()) {
1146  std::cout << "diagnostic-selectors=" << diags_ << std::endl;
1147  }
1148  if (!wrapped_.empty()) {
1149  std::cout << "wrapped=" << wrapped_ << std::endl;
1150  }
1151  if (!localname_.empty()) {
1152  if (is_interface_) {
1153  std::cout << "interface=" << localname_ << std::endl;
1154  } else {
1155  std::cout << "local-addr=" << localname_ << std::endl;
1156  }
1157  }
1158  if (!server_name_.empty()) {
1159  std::cout << "server=" << server_name_ << std::endl;
1160  }
1161  if (single_thread_mode_) {
1162  std::cout << "single-thread-mode" << std::endl;
1163  } else {
1164  std::cout << "multi-thread-mode" << std::endl;
1165  }
1166 }
1167 
1168 void
1170  std::cout <<
1171  "perfdhcp [-huv] [-4|-6] [-A<encapsulation-level>] [-e<lease-type>]\n"
1172  " [-r<rate>] [-f<renew-rate>]\n"
1173  " [-F<release-rate>] [-t<report>] [-C<separator>] [-R<range>]\n"
1174  " [-b<base>] [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
1175  " [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
1176  " [-L<local-port>] [-N<remote-port>]\n"
1177  " [-o<code,hexstring>] [-s<seed>] [-i] [-B] [-W<late-exit-delay>]\n"
1178  " [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
1179  " [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
1180  " [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n"
1181  " [-w<wrapped>] [server]\n"
1182  "\n"
1183  "The [server] argument is the name/address of the DHCP server to\n"
1184  "contact. For DHCPv4 operation, exchanges are initiated by\n"
1185  "transmitting a DHCP DISCOVER to this address.\n"
1186  "\n"
1187  "For DHCPv6 operation, exchanges are initiated by transmitting a DHCP\n"
1188  "SOLICIT to this address. In the DHCPv6 case, the special name 'all'\n"
1189  "can be used to refer to All_DHCP_Relay_Agents_and_Servers (the\n"
1190  "multicast address FF02::1:2), or the special name 'servers' to refer\n"
1191  "to All_DHCP_Servers (the multicast address FF05::1:3). The [server]\n"
1192  "argument is optional only in the case that -l is used to specify an\n"
1193  "interface, in which case [server] defaults to 'all'.\n"
1194  "\n"
1195  "The default is to perform a single 4-way exchange, effectively pinging\n"
1196  "the server.\n"
1197  "The -r option is used to set up a performance test, without\n"
1198  "it exchanges are initiated as fast as possible.\n"
1199  "The other scenario is an avalanche which is selected by\n"
1200  "--scenario avalanche. It first sends as many Discovery or Solicit\n"
1201  "messages as request in -R option then back off mechanism is used for\n"
1202  "each simulated client until all requests are answered. At the end\n"
1203  "time of whole scenario is reported.\n"
1204  "\n"
1205  "Options:\n"
1206  "-1: Take the server-ID option from the first received message.\n"
1207  "-4: DHCPv4 operation (default). This is incompatible with the -6 option.\n"
1208  "-6: DHCPv6 operation. This is incompatible with the -4 option.\n"
1209  "-u: Enable checking address uniqueness. Lease valid lifetime\n"
1210  " should not be shorter than test duration.\n"
1211  "-b<base>: The base mac, duid, IP, etc, used to simulate different\n"
1212  " clients. This can be specified multiple times, each instance is\n"
1213  " in the <type>=<value> form, for instance:\n"
1214  " (and default) mac=00:0c:01:02:03:04.\n"
1215  "-d<drop-time>: Specify the time after which a request is treated as\n"
1216  " having been lost. The value is given in seconds and may contain a\n"
1217  " fractional component. The default is 1 second.\n"
1218  "-e<lease-type>: A type of lease being requested from the server. It\n"
1219  " may be one of the following: address-only, prefix-only or\n"
1220  " address-and-prefix. The address-only indicates that the regular\n"
1221  " address (v4 or v6) will be requested. The prefix-only indicates\n"
1222  " that the IPv6 prefix will be requested. The address-and-prefix\n"
1223  " indicates that both IPv6 address and prefix will be requested.\n"
1224  " The '-e prefix-only' and -'e address-and-prefix' must not be\n"
1225  " used with -4.\n"
1226  "-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
1227  " elapsed-time option in the (second/request) template.\n"
1228  " The value 0 disables it.\n"
1229  "-f<renew-rate>: Rate at which DHCPv4 or DHCPv6 renew requests are sent\n"
1230  " to a server. This value is only valid when used in conjunction\n"
1231  " with the exchange rate (given by -r<rate>). Furthermore the sum of\n"
1232  " this value and the release-rate (given by -F<rate) must be equal\n"
1233  " to or less than the exchange rate.\n"
1234  "-g: Select thread mode: 'single' or 'multi'. In multi-thread mode packets\n"
1235  " are received in separate thread. This allows better utilisation of CPUs.\n"
1236  " If more than 1 CPU is present then multi-thread mode is the default,\n"
1237  " otherwise single-thread is the default.\n"
1238  "-h: Print this help.\n"
1239  "-i: Do only the initial part of an exchange: DO or SA, depending on\n"
1240  " whether -6 is given.\n"
1241  "-I<ip-offset>: Offset of the (DHCPv4) IP address in the requested-IP\n"
1242  " option / (DHCPv6) IA_NA option in the (second/request) template.\n"
1243  "-J<remote-address-list-file>: Text file that include multiple addresses.\n"
1244  " If provided perfdhcp will choose randomly one of addresses for each\n"
1245  " exchange.\n"
1246  "-l<local-addr|interface>: For DHCPv4 operation, specify the local\n"
1247  " hostname/address to use when communicating with the server. By\n"
1248  " default, the interface address through which traffic would\n"
1249  " normally be routed to the server is used.\n"
1250  " For DHCPv6 operation, specify the name of the network interface\n"
1251  " via which exchanges are initiated.\n"
1252  "-L<local-port>: Specify the local port to use\n"
1253  " (the value 0 means to use the default).\n"
1254  "-M<mac-list-file>: A text file containing a list of MAC addresses,\n"
1255  " one per line. If provided, a MAC address will be chosen randomly\n"
1256  " from this list for every new exchange. In the DHCPv6 case, MAC\n"
1257  " addresses are used to generate DUID-LLs. This parameter must not be\n"
1258  " used in conjunction with the -b parameter.\n"
1259  "-N<remote-port>: Specify the remote port to use\n"
1260  " (the value 0 means to use the default).\n"
1261  "-o<code,hexstring>: Send custom option with the specified code and the\n"
1262  " specified buffer in hexstring format.\n"
1263  "-O<random-offset>: Offset of the last octet to randomize in the template.\n"
1264  "-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
1265  "-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
1266  " exchanges per second. A periodic report is generated showing the\n"
1267  " number of exchanges which were not completed, as well as the\n"
1268  " average response latency. The program continues until\n"
1269  " interrupted, at which point a final report is generated.\n"
1270  "-R<range>: Specify how many different clients are used. With 1\n"
1271  " (the default), all requests seem to come from the same client.\n"
1272  "-s<seed>: Specify the seed for randomization, making it repeatable.\n"
1273  "--scenario <name>: where name is 'basic' (default) or 'avalanche'.\n"
1274  "-S<srvid-offset>: Offset of the server-ID option in the\n"
1275  " (second/request) template.\n"
1276  "-T<template-file>: The name of a file containing the template to use\n"
1277  " as a stream of hexadecimal digits.\n"
1278  "-v: Report the version number of this program.\n"
1279  "-W<time>: Specifies exit-wait-time parameter, that makes perfdhcp wait\n"
1280  " for <time> us after an exit condition has been met to receive all\n"
1281  " packets without sending any new packets. Expressed in microseconds.\n"
1282  "-w<wrapped>: Command to call with start/stop at the beginning/end of\n"
1283  " the program.\n"
1284  "-x<diagnostic-selector>: Include extended diagnostics in the output.\n"
1285  " <diagnostic-selector> is a string of single-keywords specifying\n"
1286  " the operations for which verbose output is desired. The selector\n"
1287  " key letters are:\n"
1288  " * 'a': print the decoded command line arguments\n"
1289  " * 'e': print the exit reason\n"
1290  " * 'i': print rate processing details\n"
1291  " * 'l': print received leases\n"
1292  " * 's': print first server-id\n"
1293  " * 't': when finished, print timers of all successful exchanges\n"
1294  " * 'T': when finished, print templates\n"
1295  "-X<xid-offset>: Transaction ID (aka. xid) offset in the template.\n"
1296  "-Y<time>: time in seconds after which perfdhcp will start sending\n"
1297  " messages with increased elapsed time option.\n"
1298  "-y<time>: period of time in seconds in which perfdhcp will be sending\n"
1299  " messages with increased elapsed time option.\n"
1300  "DHCPv4 only options:\n"
1301  "-B: Force broadcast handling.\n"
1302  "\n"
1303  "DHCPv6 only options:\n"
1304  "-c: Add a rapid commit option (exchanges will be SA).\n"
1305  "-F<release-rate>: Rate at which IPv6 Release requests are sent to\n"
1306  " a server. This value is only valid when used in conjunction with\n"
1307  " the exchange rate (given by -r<rate>). Furthermore the sum of\n"
1308  " this value and the renew-rate (given by -f<rate) must be equal\n"
1309  " to or less than the exchange rate.\n"
1310  "-A<encapsulation-level>: Specifies that relayed traffic must be\n"
1311  " generated. The argument specifies the level of encapsulation, i.e.\n"
1312  " how many relay agents are simulated. Currently the only supported\n"
1313  " <encapsulation-level> value is 1, which means that the generated\n"
1314  " traffic is an equivalent of the traffic passing through a single\n"
1315  " relay agent.\n"
1316  "\n"
1317  "The remaining options are typically used in conjunction with -r:\n"
1318  "\n"
1319  "-D<max-drop>: Abort the test immediately if max-drop requests have\n"
1320  " been dropped. max-drop must be a positive integer. If max-drop\n"
1321  " includes the suffix '%', it specifies a maximum percentage of\n"
1322  " requests that may be dropped before abort. In this case, testing\n"
1323  " of the threshold begins after 10 requests have been expected to\n"
1324  " be received.\n"
1325  "-n<num-request>: Initiate <num-request> transactions. No report is\n"
1326  " generated until all transactions have been initiated/waited-for,\n"
1327  " after which a report is generated and the program terminates.\n"
1328  "-p<test-period>: Send requests for the given test period, which is\n"
1329  " specified in the same manner as -d. This can be used as an\n"
1330  " alternative to -n, or both options can be given, in which case the\n"
1331  " testing is completed when either limit is reached.\n"
1332  "-t<report>: Delay in seconds between two periodic reports.\n"
1333  "-C<separator>: Output reduced, an argument is a separator for periodic\n"
1334  " (-t) reports generated in easy parsable mode. Data output won't be\n"
1335  " changed, remain identical as in -t option.\n"
1336  "\n"
1337  "Errors:\n"
1338  "- tooshort: received a too short message\n"
1339  "- orphans: received a message which doesn't match an exchange\n"
1340  " (duplicate, late or not related)\n"
1341  "- locallimit: reached to local system limits when sending a message.\n"
1342  "\n"
1343  "Exit status:\n"
1344  "The exit status is:\n"
1345  "0 on complete success.\n"
1346  "1 for a general error.\n"
1347  "2 if an error is found in the command line arguments.\n"
1348  "3 if there are no general failures in operation, but one or more\n"
1349  " exchanges are not successfully completed.\n";
1350 }
1351 
1352 void
1354  std::cout << "VERSION: " << VERSION << std::endl;
1355 }
1356 
1357 
1358 } // namespace perfdhcp
1359 } // namespace isc
#define ALL_DHCP_SERVERS
Definition: dhcp6.h:294
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:875
int getElapsedTimeOffset() const
Returns template offset for elapsed time.
bool isUseFirst() const
Check if server-ID to be taken from first package.
bool parse(int argc, char **const argv, bool print_cmd_line=false)
Parse command line.
LeaseType getLeaseType() const
\ brief Returns the type of lease being requested.
void printCommandLine() const
Print command line arguments.
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
int getRemotePort() const
Returns remote port number.
const char *const * perfdhcp_config_report
uint32_t getClientsNum() const
Returns number of simulated clients.
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
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
int getIncreaseElapsedTime() const
Returns increased elapsed time.
STL namespace.
int getRequestedIpOffset() const
Returns template offset for requested IP.
Handles network interfaces, transmission and reception.
Definition: iface_mgr.h:632
std::vector< std::string > getTemplateFiles() const
Returns template file names.
void fromCommandLine(const std::string &cmd_line_arg)
Sets the lease type from the command line argument.
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...
#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS
Definition: dhcp6.h:293
std::vector< double > getDropTime() const
Returns drop time.
std::vector< double > getMaxDropPercentage() const
Returns maximal percentage of drops.
#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...
const int LONG_OPT_SCENARIO
const char *const config_report[]
Definition: config_report.h:15
bool isRapidCommit() const
Check if rapid commit option used.
bool isSingleThreaded() const
Check if single-threaded mode is enabled.
void decodeHex(const string &input, vector< uint8_t > &result)
Decode a text encoded in the base16 ('hex') format into the original data.
Definition: base_n.cc:474
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
A generic exception that is thrown when an unexpected error condition occurs.
uint8_t getIpVersion() const
Returns IP version.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
std::vector< int > getMaxDrop() const
Returns maximum drops number.
std::vector< int > getTransactionIdOffset() const
brief Returns template offsets for xid.
std::string getMacListFile() const
Returns location of the file containing list of MAC addresses.
void version() const
Print program version.
Defines the logger used by the top-level component of kea-dhcp-ddns.
int getCleanReport() const
Returns clean report mode.
bool isBroadcast() const
Checks if broadcast address is to be used.
void reset()
Reset to defaults.
int getRate() const
Returns exchange rate.
ExchangeMode getExchangeMode() const
Returns packet exchange mode.
int getRenewRate() const
Returns a rate at which DHCPv6 Renew messages are sent.
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
void usage() const
Print usage.
int getReleaseRate() const
Returns a rate at which DHCPv6 Release messages are sent.
std::string toText() const
Return textual representation of the lease type.
int getWaitForElapsedTime() const
Returns time to wait for elapsed time increase.
bool is(const Type lease_type) const
Checks if lease type has the specified code.
std::vector< int > getNumRequests() const
Returns maximum number of exchanges.
int getServerIdOffset() const
Returns template offset for server-ID.
vector< uint8_t >::const_iterator base_
Definition: base_n.cc:149
int getLocalPort() const
Returns local port number.
std::vector< int > getRandomOffset() const
Returns template offsets for rnd.