command_options.cc 41.5 KB
Newer Older
1
// Copyright (C) 2012-2013, 2015 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15 16
#include <config.h>

17 18 19 20 21 22 23 24 25
#include "command_options.h"
#include <exceptions/exceptions.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/duid.h>

#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <sstream>
26
#include <stdio.h>
27 28
#include <stdlib.h>
#include <stdint.h>
29 30 31 32 33 34 35 36 37
#include <unistd.h>


using namespace std;
using namespace isc;

namespace isc {
namespace perfdhcp {

38
CommandOptions::LeaseType::LeaseType()
39
    : type_(ADDRESS) {
40 41 42 43 44 45 46 47 48 49 50
}

CommandOptions::LeaseType::LeaseType(const Type lease_type)
    : type_(lease_type) {
}

bool
CommandOptions::LeaseType::is(const Type lease_type) const {
    return (lease_type == type_);
}

51 52 53 54 55
bool
CommandOptions::LeaseType::includes(const Type lease_type) const {
    return (is(ADDRESS_AND_PREFIX) || (lease_type == type_));
}

56 57 58 59 60 61 62 63
void
CommandOptions::LeaseType::set(const Type lease_type) {
    type_ = lease_type;
}

void
CommandOptions::LeaseType::fromCommandLine(const std::string& cmd_line_arg) {
    if (cmd_line_arg == "address-only") {
64
        type_ = ADDRESS;
65 66

    } else if (cmd_line_arg == "prefix-only") {
67
        type_ = PREFIX;
68

69 70 71
    } else if (cmd_line_arg == "address-and-prefix") {
        type_ = ADDRESS_AND_PREFIX;

72 73 74 75 76 77 78 79 80 81
    } else {
        isc_throw(isc::InvalidParameter, "value of lease-type: -e<lease-type>,"
                  " must be one of the following: 'address-only' or"
                  " 'prefix-only'");
    }
}

std::string
CommandOptions::LeaseType::toText() const {
    switch (type_) {
82
    case ADDRESS:
83
        return ("address-only (IA_NA option added to the client's request)");
84
    case PREFIX:
85
        return ("prefix-only (IA_PD option added to the client's request)");
86 87 88
    case ADDRESS_AND_PREFIX:
        return ("address-and-prefix (Both IA_NA and IA_PD options added to the"
                " client's request)");
89 90 91 92 93 94
    default:
        isc_throw(Unexpected, "internal error: undefined lease type code when"
                  " returning textual representation of the lease type");
    }
}

95 96
CommandOptions&
CommandOptions::instance() {
97 98
    static CommandOptions options;
    return (options);
99 100
}

101 102
void
CommandOptions::reset() {
103
    // Default mac address used in DHCP messages
104
    // if -b mac=<mac-address> was not specified
105
    uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 };
106

107
    // Default packet drop time if -D<drop-time> parameter
108 109
    // was not specified
    double dt[2] = { 1., 1. };
110

Marcin Siodelski's avatar
Marcin Siodelski committed
111 112
    // We don't use constructor initialization list because we
    // will need to reset all members many times to perform unit tests
113
    ipversion_ = 0;
114
    exchange_mode_ = DORA_SARR;
115
    lease_type_.set(LeaseType::ADDRESS);
116
    rate_ = 0;
117
    renew_rate_ = 0;
118
    release_rate_ = 0;
119
    report_delay_ = 0;
120
    clients_num_ = 0;
121 122
    mac_template_.assign(mac, mac + 6);
    duid_template_.clear();
123 124
    base_.clear();
    num_request_.clear();
125
    period_ = 0;
126 127 128 129 130
    drop_time_set_ = 0;
    drop_time_.assign(dt, dt + 2);
    max_drop_.clear();
    max_pdrop_.clear();
    localname_.clear();
131 132 133 134 135 136 137 138 139
    is_interface_ = false;
    preload_ = 0;
    aggressivity_ = 1;
    local_port_ = 0;
    seeded_ = false;
    seed_ = 0;
    broadcast_ = false;
    rapid_commit_ = false;
    use_first_ = false;
140 141 142
    template_file_.clear();
    rnd_offset_.clear();
    xid_offset_.clear();
143 144 145
    elp_offset_ = -1;
    sid_offset_ = -1;
    rip_offset_ = -1;
146 147 148
    diags_.clear();
    wrapped_.clear();
    server_name_.clear();
149
    generateDuidTemplate();
150 151
}

152
bool
153
CommandOptions::parse(int argc, char** const argv, bool print_cmd_line) {
154
    // Reset internal variables used by getopt
Marcin Siodelski's avatar
Marcin Siodelski committed
155
    // to eliminate undefined behavior when
156
    // parsing different command lines multiple times
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

#ifdef __GLIBC__
    // Warning: non-portable code. This is due to a bug in glibc's
    // getopt() which keeps internal state about an old argument vector
    // (argc, argv) from last call and tries to scan them when a new
    // argument vector (argc, argv) is passed. As the old vector may not
    // be main()'s arguments, but heap allocated and may have been freed
    // since, this becomes a use after free and results in random
    // behavior. According to the NOTES section in glibc getopt()'s
    // manpage, setting optind=0 resets getopt()'s state. Though this is
    // not required in our usage of getopt(), the bug still happens
    // unless we set optind=0.
    //
    // Setting optind=0 is non-portable code.
    optind = 0;
#else
173
    optind = 1;
174 175
#endif

176 177 178 179 180 181 182 183 184
    // optreset is declared on BSD systems and is used to reset internal
    // state of getopt(). When parsing command line arguments multiple
    // times with getopt() the optreset must be set to 1 every time before
    // parsing starts. Failing to do so will result in random behavior of
    // getopt().
#ifdef HAVE_OPTRESET
    optreset = 1;
#endif

185
    opterr = 0;
186 187 188 189

    // Reset values of class members
    reset();

190
    // Informs if program has been run with 'h' or 'v' option.
191
    bool help_or_version_mode = initialize(argc, argv, print_cmd_line);
192 193 194 195
    if (!help_or_version_mode) {
        validate();
    }
    return (help_or_version_mode);
196 197
}

198
bool
199
CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
200
    int opt = 0;                // Subsequent options returned by getopt()
201 202
    std::string drop_arg;       // Value of -D<value>argument
    size_t percent_loc = 0;     // Location of % sign in -D<value>
203
    double drop_percent = 0;    // % value (1..100) in -D<value%>
204
    int num_drops = 0;          // Max number of drops specified in -D<value>
205 206
    int num_req = 0;            // Max number of dropped
                                // requests in -n<max-drops>
207 208 209
    int offset_arg = 0;         // Temporary variable holding offset arguments
    std::string sarg;           // Temporary variable for string args

210 211 212
    std::ostringstream stream;
    stream << "perfdhcp";

213
    // In this section we collect argument values from command line
Marcin Siodelski's avatar
Marcin Siodelski committed
214
    // they will be tuned and validated elsewhere
215
    while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
216
                        "s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
217
        stream << " -" << static_cast<char>(opt);
218 219
        if (optarg) {
            stream << " " << optarg;
220
        }
221
        switch (opt) {
222 223 224 225
        case '1':
            use_first_ = true;
            break;

226 227 228 229
        case '4':
            check(ipversion_ == 6, "IP version already set to 6");
            ipversion_ = 4;
            break;
230

231 232 233 234
        case '6':
            check(ipversion_ == 4, "IP version already set to 4");
            ipversion_ = 6;
            break;
Marcin Siodelski's avatar
Marcin Siodelski committed
235

236
        case 'a':
237 238
            aggressivity_ = positiveInteger("value of aggressivity: -a<value>"
                                            " must be a positive integer");
239
            break;
240

241
        case 'b':
242
            check(base_.size() > 3, "-b<value> already specified,"
243
                  " unexpected occurrence of 5th -b<value>");
244 245 246 247
            base_.push_back(optarg);
            decodeBase(base_.back());
            break;

248 249
        case 'B':
            broadcast_ = true;
250
            break;
251

252 253
        case 'c':
            rapid_commit_ = true;
254
            break;
255

256
        case 'd':
257 258
            check(drop_time_set_ > 1,
                  "maximum number of drops already specified, "
259
                  "unexpected 3rd occurrence of -d<value>");
260
            try {
261 262
                drop_time_[drop_time_set_] =
                    boost::lexical_cast<double>(optarg);
263 264
            } catch (boost::bad_lexical_cast&) {
                isc_throw(isc::InvalidParameter,
265 266
                          "value of drop time: -d<value>"
                          " must be positive number");
267
            }
268 269
            check(drop_time_[drop_time_set_] <= 0.,
                  "drop-time must be a positive number");
270
            drop_time_set_ = true;
271
            break;
272

273
        case 'D':
274 275
            drop_arg = std::string(optarg);
            percent_loc = drop_arg.find('%');
276 277
            check(max_pdrop_.size() > 1 || max_drop_.size() > 1,
                  "values of maximum drops: -D<value> already "
278
                  "specified, unexpected 3rd occurrence of -D<value>");
279 280
            if ((percent_loc) != std::string::npos) {
                try {
281 282
                    drop_percent =
                        boost::lexical_cast<double>(drop_arg.substr(0, percent_loc));
283 284
                } catch (boost::bad_lexical_cast&) {
                    isc_throw(isc::InvalidParameter,
285 286
                              "value of drop percentage: -D<value%>"
                              " must be 0..100");
287 288
                }
                check((drop_percent <= 0) || (drop_percent >= 100),
289
                  "value of drop percentage: -D<value%> must be 0..100");
290 291
                max_pdrop_.push_back(drop_percent);
            } else {
292 293
                num_drops = positiveInteger("value of max drops number:"
                                            " -d<value> must be a positive integer");
294
                max_drop_.push_back(num_drops);
295 296
            }
            break;
297

298 299 300 301
        case 'e':
            initLeaseType();
            break;

302
        case 'E':
303 304
            elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
                                             " must not be a negative integer");
305
            break;
306

307 308 309 310 311
        case 'f':
            renew_rate_ = positiveInteger("value of the renew rate: -f<renew-rate>"
                                          " must be a positive integer");
            break;

312 313 314 315 316 317
        case 'F':
            release_rate_ = positiveInteger("value of the release rate:"
                                            " -F<release-rate> must be a"
                                            " positive integer");
            break;

318 319
        case 'h':
            usage();
320
            return (true);
321 322 323

        case 'i':
            exchange_mode_ = DO_SA;
324
            break;
325

326
        case 'I':
327 328 329
            rip_offset_ = positiveInteger("value of ip address offset:"
                                          " -I<value> must be a"
                                          " positive integer");
330 331 332 333
            break;

        case 'l':
            localname_ = std::string(optarg);
334
            initIsInterface();
335 336 337
            break;

        case 'L':
338 339 340 341 342
             local_port_ = nonNegativeInteger("value of local port:"
                                              " -L<value> must not be a"
                                              " negative integer");
             check(local_port_ >
                   static_cast<int>(std::numeric_limits<uint16_t>::max()),
343 344 345 346
                  "local-port must be lower than " +
                  boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
            break;

347
        case 'n':
348 349
            num_req = positiveInteger("value of num-request:"
                                      " -n<value> must be a positive integer");
350
            if (num_request_.size() >= 2) {
351 352
                isc_throw(isc::InvalidParameter,
                          "value of maximum number of requests: -n<value> "
353
                          "already specified, unexpected 3rd occurrence"
354
                          " of -n<value>");
355 356
            }
            num_request_.push_back(num_req);
357 358
            break;

359 360
        case 'O':
            if (rnd_offset_.size() < 2) {
361 362
                offset_arg = positiveInteger("value of random offset: "
                                             "-O<value> must be greater than 3");
363 364
            } else {
                isc_throw(isc::InvalidParameter,
365
                          "random offsets already specified,"
366
                          " unexpected 3rd occurrence of -O<value>");
367
            }
368 369
            check(offset_arg < 3, "value of random random-offset:"
                  " -O<value> must be greater than 3 ");
370
            rnd_offset_.push_back(offset_arg);
371 372
            break;

373
        case 'p':
374 375
            period_ = positiveInteger("value of test period:"
                                      " -p<value> must be a positive integer");
376 377
            break;

378
        case 'P':
379 380
            preload_ = nonNegativeInteger("number of preload packets:"
                                          " -P<value> must not be "
381
                                          "a negative integer");
382 383
            break;

384
        case 'r':
385 386
            rate_ = positiveInteger("value of rate:"
                                    " -r<value> must be a positive integer");
387 388
            break;

389 390
        case 'R':
            initClientsNum();
391 392
            break;

393 394
        case 's':
            seed_ = static_cast<unsigned int>
395 396
                (nonNegativeInteger("value of seed:"
                                    " -s <seed> must be non-negative integer"));
397
            seeded_ = seed_ > 0 ? true : false;
398 399
            break;

400
        case 'S':
401 402 403
            sid_offset_ = positiveInteger("value of server id offset:"
                                          " -S<value> must be a"
                                          " positive integer");
404 405
            break;

406
        case 't':
407 408 409
            report_delay_ = positiveInteger("value of report delay:"
                                            " -t<value> must be a"
                                            " positive integer");
410 411
            break;

412 413
        case 'T':
            if (template_file_.size() < 2) {
414 415
                sarg = nonEmptyString("template file name not specified,"
                                      " expected -T<filename>");
416 417 418
                template_file_.push_back(sarg);
            } else {
                isc_throw(isc::InvalidParameter,
419
                          "template files are already specified,"
420
                          " unexpected 3rd -T<filename> occurrence");
421
            }
422 423
            break;

424 425 426 427
        case 'v':
            version();
            return (true);

428
        case 'w':
429 430
            wrapped_ = nonEmptyString("command for wrapped mode:"
                                      " -w<command> must be specified");
431 432 433
            break;

        case 'x':
434 435
            diags_ = nonEmptyString("value of diagnostics selectors:"
                                    " -x<value> must be specified");
436 437
            break;

438 439
        case 'X':
            if (xid_offset_.size() < 2) {
440 441 442
                offset_arg = positiveInteger("value of transaction id:"
                                             " -X<value> must be a"
                                             " positive integer");
443 444
            } else {
                isc_throw(isc::InvalidParameter,
445
                          "transaction ids already specified,"
446
                          " unexpected 3rd -X<value> occurrence");
447 448
            }
            xid_offset_.push_back(offset_arg);
449 450 451
            break;

        default:
452
            isc_throw(isc::InvalidParameter, "unknown command line option");
453
        }
454 455
    }

456 457
    // If the IP version was not specified in the
    // command line, assume IPv4.
458
    if (ipversion_ == 0) {
459
        ipversion_ = 4;
460
    }
Marcin Siodelski's avatar
Marcin Siodelski committed
461

462 463 464 465 466
    // If template packet files specified for both DISCOVER/SOLICIT
    // and REQUEST/REPLY exchanges make sure we have transaction id
    // and random duid offsets for both exchanges. We will duplicate
    // value specified as -X<value> and -R<value> for second
    // exchange if user did not specified otherwise.
467
    if (template_file_.size() > 1) {
468
        if (xid_offset_.size() == 1) {
469
            xid_offset_.push_back(xid_offset_[0]);
470 471
        }
        if (rnd_offset_.size() == 1) {
472
            rnd_offset_.push_back(rnd_offset_[0]);
473
        }
474
    }
475

Marcin Siodelski's avatar
Marcin Siodelski committed
476
    // Get server argument
477 478 479
    // NoteFF02::1:2 and FF02::1:3 are defined in RFC3315 as
    // All_DHCP_Relay_Agents_and_Servers and All_DHCP_Servers
    // addresses
480 481 482
    check(optind < argc -1, "extra arguments?");
    if (optind == argc - 1) {
        server_name_ = argv[optind];
483
        stream << " " << server_name_;
Marcin Siodelski's avatar
Marcin Siodelski committed
484
        // Decode special cases
485
        if ((ipversion_ == 4) && (server_name_.compare("all") == 0)) {
486 487 488
            broadcast_ = true;
            // Use broadcast address as server name.
            server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
489
        } else if ((ipversion_ == 6) && (server_name_.compare("all") == 0)) {
490
            server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
491 492
        } else if ((ipversion_ == 6) &&
                   (server_name_.compare("servers") == 0)) {
493
            server_name_ = ALL_DHCP_SERVERS;
494 495
        }
    }
496

497 498 499
    if (print_cmd_line) {
        std::cout << "Running: " << stream.str() << std::endl;
    }
500

501 502 503 504
    // Handle the local '-l' address/interface
    if (!localname_.empty()) {
        if (server_name_.empty()) {
            if (is_interface_ && (ipversion_ == 4)) {
505 506
                broadcast_ = true;
                server_name_ = DHCP_IPV4_BROADCAST_ADDRESS;
507
            } else if (is_interface_ && (ipversion_ == 6)) {
508
                server_name_ = ALL_DHCP_RELAY_AGENTS_AND_SERVERS;
509 510 511 512 513
            }
        }
    }
    if (server_name_.empty()) {
        isc_throw(InvalidParameter,
514
                  "without an interface, server is required");
515
    }
516 517 518

    // If DUID is not specified from command line we need to
    // generate one.
519
    if (duid_template_.empty()) {
520
        generateDuidTemplate();
521
    }
522
    return (false);
523 524
}

525
void
526
CommandOptions::initClientsNum() {
527 528
    const std::string errmsg =
        "value of -R <value> must be non-negative integer";
529

530
    try {
531 532 533 534 535
        // Declare clients_num as as 64-bit signed value to
        // be able to detect negative values provided
        // by user. We would not detect negative values
        // if we casted directly to unsigned value.
        long long clients_num = boost::lexical_cast<long long>(optarg);
536
        check(clients_num < 0, errmsg);
537
        clients_num_ = boost::lexical_cast<uint32_t>(optarg);
538 539 540 541 542
    } catch (boost::bad_lexical_cast&) {
        isc_throw(isc::InvalidParameter, errmsg);
    }
}

543 544 545 546 547 548 549 550 551 552 553
void
CommandOptions::initIsInterface() {
    is_interface_ = false;
    if (!localname_.empty()) {
        dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance();
        if (iface_mgr.getIface(localname_) != NULL)  {
            is_interface_ = true;
        }
    }
}

554 555 556 557
void
CommandOptions::decodeBase(const std::string& base) {
    std::string b(base);
    boost::algorithm::to_lower(b);
Marcin Siodelski's avatar
Marcin Siodelski committed
558

559
    // Currently we only support mac and duid
560 561 562 563
    if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) {
        decodeMac(b);
    } else if (b.substr(0, 5) == "duid=") {
        decodeDuid(b);
564 565
    } else {
        isc_throw(isc::InvalidParameter,
566 567
                  "base value not provided as -b<value>,"
                  " expected -b mac=<mac> or -b duid=<duid>");
568 569 570 571 572
    }
}

void
CommandOptions::decodeMac(const std::string& base) {
Marcin Siodelski's avatar
Marcin Siodelski committed
573
    // Strip string from mac=
574
    size_t found = base.find('=');
575
    static const char* errmsg = "expected -b<base> format for"
576 577
        " mac address is -b mac=00::0C::01::02::03::04 or"
        " -b mac=00:0C:01:02:03:04";
578
    check(found == std::string::npos, errmsg);
Marcin Siodelski's avatar
Marcin Siodelski committed
579

580 581
    // Decode mac address to vector of uint8_t
    std::istringstream s1(base.substr(found + 1));
582
    std::string token;
583
    mac_template_.clear();
584
    // Get pieces of MAC address separated with : (or even ::)
585
    while (std::getline(s1, token, ':')) {
586
        // Convert token to byte value using std::istringstream
587
        if (token.length() > 0) {
588
            unsigned int ui = 0;
589
            try {
590
                // Do actual conversion
591 592 593 594 595 596
                ui = convertHexString(token);
            } catch (isc::InvalidParameter&) {
                isc_throw(isc::InvalidParameter,
                          "invalid characters in MAC provided");

            }
597
            // If conversion succeeded store byte value
598
            mac_template_.push_back(ui);
599
        }
600
    }
601
    // MAC address must consist of 6 octets, otherwise it is invalid
602
    check(mac_template_.size() != 6, errmsg);
603 604 605 606
}

void
CommandOptions::decodeDuid(const std::string& base) {
Marcin Siodelski's avatar
Marcin Siodelski committed
607
    // Strip argument from duid=
608
    std::vector<uint8_t> duid_template;
609
    size_t found = base.find('=');
610 611
    check(found == std::string::npos, "expected -b<base>"
          " format for duid is -b duid=<duid>");
612
    std::string b = base.substr(found + 1);
Marcin Siodelski's avatar
Marcin Siodelski committed
613

614
    // DUID must have even number of digits and must not be longer than 64 bytes
615 616 617 618
    check(b.length() & 1, "odd number of hexadecimal digits in duid");
    check(b.length() > 128, "duid too large");
    check(b.length() == 0, "no duid specified");

619
    // Turn pairs of hexadecimal digits into vector of octets
620 621
    for (int i = 0; i < b.length(); i += 2) {
        unsigned int ui = 0;
622
        try {
623
            // Do actual conversion
624 625 626
            ui = convertHexString(b.substr(i, 2));
        } catch (isc::InvalidParameter&) {
            isc_throw(isc::InvalidParameter,
627
                      "invalid characters in DUID provided,"
628
                      " expected hex digits");
629
        }
630
        duid_template.push_back(static_cast<uint8_t>(ui));
631
    }
632 633 634 635
    // @todo Get rid of this limitation when we manage add support
    // for DUIDs other than LLT. Shorter DUIDs may be useful for
    // server testing purposes.
    check(duid_template.size() < 6, "DUID must be at least 6 octets long");
636
    // Assign the new duid only if successfully generated.
637
    std::swap(duid_template, duid_template_);
638 639
}

640
void
641
CommandOptions::generateDuidTemplate() {
642
    using namespace boost::posix_time;
643
    // Duid template will be most likely generated only once but
644 645
    // it is ok if it is called more then once so we simply
    //  regenerate it and discard previous value.
646 647 648
    duid_template_.clear();
    const uint8_t duid_template_len = 14;
    duid_template_.resize(duid_template_len);
649
    // The first four octets consist of DUID LLT and hardware type.
650 651
    duid_template_[0] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) >> 8);
    duid_template_[1] = static_cast<uint8_t>(static_cast<uint16_t>(isc::dhcp::DUID::DUID_LLT) & 0xff);
652 653
    duid_template_[2] = HWTYPE_ETHERNET >> 8;
    duid_template_[3] = HWTYPE_ETHERNET & 0xff;
654

655 656 657 658 659 660 661
    // As described in RFC3315: 'the time value is the time
    // that the DUID is generated represented in seconds
    // since midnight (UTC), January 1, 2000, modulo 2^32.'
    ptime now = microsec_clock::universal_time();
    ptime duid_epoch(from_iso_string("20000101T000000"));
    time_period period(duid_epoch, now);
    uint32_t duration_sec = htonl(period.length().total_seconds());
662
    memcpy(&duid_template_[4], &duration_sec, 4);
663 664 665 666

    // Set link layer address (6 octets). This value may be
    // randomized before sending a packet to simulate different
    // clients.
667
    memcpy(&duid_template_[8], &mac_template_[0], 6);
668 669
}

670
uint8_t
671
CommandOptions::convertHexString(const std::string& text) const {
672
    unsigned int ui = 0;
673 674 675
    // First, check if we are dealing with hexadecimal digits only
    for (int i = 0; i < text.length(); ++i) {
        if (!std::isxdigit(text[i])) {
676
            isc_throw(isc::InvalidParameter,
677 678
                      "The following digit: " << text[i] << " in "
                      << text << "is not hexadecimal");
679 680
        }
    }
681 682 683 684
    // If we are here, we have valid string to convert to octet
    std::istringstream text_stream(text);
    text_stream >> std::hex >> ui >> std::dec;
    // Check if for some reason we have overflow - this should never happen!
685
    if (ui > 0xFF) {
686 687
        isc_throw(isc::InvalidParameter, "Can't convert more than"
                  " two hex digits to byte");
688 689 690 691
    }
    return ui;
}

692 693 694 695 696 697
void
CommandOptions::validate() const {
    check((getIpVersion() != 4) && (isBroadcast() != 0),
          "-B is not compatible with IPv6 (-6)");
    check((getIpVersion() != 6) && (isRapidCommit() != 0),
          "-6 (IPv6) must be set to use -c");
698 699
    check((getIpVersion() != 6) && (getRenewRate() !=0),
          "-f<renew-rate> may be used with -6 (IPv6) only");
700 701
    check((getIpVersion() != 6) && (getReleaseRate() != 0),
          "-F<release-rate> may be used with -6 (IPv6) only");
702 703
    check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
          "second -n<num-request> is not compatible with -i");
704
    check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
705 706
          "-6 option must be used if lease type other than '-e address-only'"
          " is specified");
707
    check(!getTemplateFiles().empty() &&
708
          !getLeaseType().is(LeaseType::ADDRESS),
709
          "template files may be only used with '-e address-only'");
710
    check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
711 712 713
          "second -d<drop-time> is not compatible with -i");
    check((getExchangeMode() == DO_SA) &&
          ((getMaxDrop().size() > 1) || (getMaxDropPercentage().size() > 1)),
714
          "second -D<max-drop> is not compatible with -i");
715
    check((getExchangeMode() == DO_SA) && (isUseFirst()),
716
          "-1 is not compatible with -i");
717
    check((getExchangeMode() == DO_SA) && (getTemplateFiles().size() > 1),
718
          "second -T<template-file> is not compatible with -i");
719
    check((getExchangeMode() == DO_SA) && (getTransactionIdOffset().size() > 1),
720
          "second -X<xid-offset> is not compatible with -i");
721
    check((getExchangeMode() == DO_SA) && (getRandomOffset().size() > 1),
722
          "second -O<random-offset is not compatible with -i");
723
    check((getExchangeMode() == DO_SA) && (getElapsedTimeOffset() >= 0),
724
          "-E<time-offset> is not compatible with -i");
725
    check((getExchangeMode() == DO_SA) && (getServerIdOffset() >= 0),
726
          "-S<srvid-offset> is not compatible with -i");
727
    check((getExchangeMode() == DO_SA) && (getRequestedIpOffset() >= 0),
728 729 730
          "-I<ip-offset> is not compatible with -i");
    check((getExchangeMode() == DO_SA) && (getRenewRate() != 0),
          "-f<renew-rate> is not compatible with -i");
731 732
    check((getExchangeMode() == DO_SA) && (getReleaseRate() != 0),
          "-F<release-rate> is not compatible with -i");
733
    check((getExchangeMode() != DO_SA) && (isRapidCommit() != 0),
734
          "-i must be set to use -c");
735
    check((getRate() == 0) && (getReportDelay() != 0),
736
          "-r<rate> must be set to use -t<report>");
737
    check((getRate() == 0) && (getNumRequests().size() > 0),
738
          "-r<rate> must be set to use -n<num-request>");
739
    check((getRate() == 0) && (getPeriod() != 0),
740
          "-r<rate> must be set to use -p<test-period>");
741
    check((getRate() == 0) &&
742
          ((getMaxDrop().size() > 0) || getMaxDropPercentage().size() > 0),
743
          "-r<rate> must be set to use -D<max-drop>");
744 745
    check((getRate() != 0) && (getRenewRate() + getReleaseRate() > getRate()),
          "The sum of Renew rate (-f<renew-rate>) and Release rate"
746 747
          " (-F<release-rate>) must not be greater than the exchange"
          " rate specified as -r<rate>");
748 749 750
    check((getRate() == 0) && (getRenewRate() != 0),
          "Renew rate specified as -f<renew-rate> must not be specified"
          " when -r<rate> parameter is not specified");
751 752 753
    check((getRate() == 0) && (getReleaseRate() != 0),
          "Release rate specified as -F<release-rate> must not be specified"
          " when -r<rate> parameter is not specified");
754
    check((getTemplateFiles().size() < getTransactionIdOffset().size()),
755
          "-T<template-file> must be set to use -X<xid-offset>");
756
    check((getTemplateFiles().size() < getRandomOffset().size()),
757
          "-T<template-file> must be set to use -O<random-offset>");
758
    check((getTemplateFiles().size() < 2) && (getElapsedTimeOffset() >= 0),
759
          "second/request -T<template-file> must be set to use -E<time-offset>");
760
    check((getTemplateFiles().size() < 2) && (getServerIdOffset() >= 0),
761
          "second/request -T<template-file> must be set to "
762
          "use -S<srvid-offset>");
763 764
    check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
          "second/request -T<template-file> must be set to "
765
          "use -I<ip-offset>");
766 767 768 769

}

void
Marcin Siodelski's avatar
Marcin Siodelski committed
770 771 772
CommandOptions::check(bool condition, const std::string& errmsg) const {
    // The same could have been done with macro or just if statement but
    // we prefer functions to macros here
773 774
    std::ostringstream stream;
    stream << errmsg << "\n";
775 776
    if (condition) {
        isc_throw(isc::InvalidParameter, errmsg);
777 778 779
    }
}

780
int
781
CommandOptions::positiveInteger(const std::string& errmsg) const {
782 783 784 785 786 787 788 789 790 791
    try {
        int value = boost::lexical_cast<int>(optarg);
        check(value <= 0, errmsg);
        return (value);
    } catch (boost::bad_lexical_cast&) {
        isc_throw(InvalidParameter, errmsg);
    }
}

int
792
CommandOptions::nonNegativeInteger(const std::string& errmsg) const {
793 794 795 796 797 798 799 800 801 802
    try {
        int value = boost::lexical_cast<int>(optarg);
        check(value < 0, errmsg);
        return (value);
    } catch (boost::bad_lexical_cast&) {
        isc_throw(InvalidParameter, errmsg);
    }
}

std::string
803
CommandOptions::nonEmptyString(const std::string& errmsg) const {
804 805 806 807 808 809 810
    std::string sarg = optarg;
    if (sarg.length() == 0) {
        isc_throw(isc::InvalidParameter, errmsg);
    }
    return sarg;
}

811 812 813
void
CommandOptions::initLeaseType() {
    std::string lease_type_arg = optarg;
814
    lease_type_.fromCommandLine(lease_type_arg);
815 816
}

817 818 819 820 821 822 823
void
CommandOptions::printCommandLine() const {
    std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
    if (exchange_mode_ == DO_SA) {
        if (ipversion_ == 4) {
            std::cout << "DISCOVER-OFFER only" << std::endl;
        } else {
824
            std::cout << "SOLICIT-ADVERTISE only" << std::endl;
825
        }
826
    }
827
    std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
828 829 830
    if (rate_ != 0) {
        std::cout << "rate[1/s]=" << rate_ <<  std::endl;
    }
831 832 833
    if (getRenewRate() != 0) {
        std::cout << "renew-rate[1/s]=" << getRenewRate() << std::endl;
    }
834 835 836
    if (getReleaseRate() != 0) {
        std::cout << "release-rate[1/s]=" << getReleaseRate() << std::endl;
    }
837 838 839 840 841
    if (report_delay_ != 0) {
        std::cout << "report[s]=" << report_delay_ << std::endl;
    }
    if (clients_num_ != 0) {
        std::cout << "clients=" << clients_num_ << std::endl;
842
    }
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
    for (int i = 0; i < base_.size(); ++i) {
        std::cout << "base[" << i << "]=" << base_[i] <<  std::endl;
    }
    for (int i = 0; i < num_request_.size(); ++i) {
        std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl;
    }
    if (period_ != 0)