Kea: command_options.cc Source File

| Kea 3.2.0-git

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