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