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