1
   2
   3
   4
   5
   6
   7
   8
   9
  10
  11
  12
  13
  14
  15
  16
  17
  18
  19
  20
  21
  22
  23
  24
  25
  26
  27
  28
  29
  30
  31
  32
  33
  34
  35
  36
  37
  38
  39
  40
  41
  42
  43
  44
  45
  46
  47
  48
  49
  50
  51
  52
  53
  54
  55
  56
  57
  58
  59
  60
  61
  62
  63
  64
  65
  66
  67
  68
  69
  70
  71
  72
  73
  74
  75
  76
  77
  78
  79
  80
  81
  82
  83
  84
  85
  86
  87
  88
  89
  90
  91
  92
  93
  94
  95
  96
  97
  98
  99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 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
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef TEST_CONTROL_H
#define TEST_CONTROL_H

#include <perfdhcp/packet_storage.h>
#include <perfdhcp/rate_control.h>
#include <perfdhcp/stats_mgr.h>
#include <perfdhcp/receiver.h>
#include <perfdhcp/command_options.h>
#include <perfdhcp/perf_socket.h>

#include <dhcp/iface_mgr.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>

#include <boost/noncopyable.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/date_time/posix_time/posix_time.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <random><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace perfdhcp {

/// Default transaction id offset in the packet template.
static const size_t DHCPV4_TRANSID_OFFSET = 4;
/// Default offset of MAC's last octet in the packet template..
static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
/// Default elapsed time offset in the packet template.
static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
/// Default server id offset in the packet template.
static const size_t DHCPV4_SERVERID_OFFSET = 54;
/// Default requested ip offset in the packet template.
static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
/// Default DHCPV6 transaction id offset in t the packet template.
static const size_t DHCPV6_TRANSID_OFFSET = 1;
/// Default DHCPV6 randomization offset (last octet of DUID)
/// in the packet template.
static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
/// Default DHCPV6 elapsed time offset in the packet template.
static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
/// Default DHCPV6 server id offset in the packet template.
static const size_t DHCPV6_SERVERID_OFFSET = 22;
/// Default DHCPV6 IA_NA offset in the packet template.
static const size_t DHCPV6_IA_NA_OFFSET = 40;

/// \brief Test Control class.
///
/// This class is used to run the performance test with
/// with \ref TestControl::runWrapped function. This function can be executed
/// multiple times if desired because it resets TestControl's internal
/// state every time it is executed. Prior to running \ref TestControl::runWrapped,
/// one must make sure to parse command line options by calling
/// \ref CommandOptions::parse. Failing to do this will result in an exception.
///
/// The following major stages of the test are performed by this class:
/// - set default transaction id and MAC address generators - the generator
/// is an object of \ref TestControl::NumberGenerator type and it provides
/// the custom randomization algorithms,
/// - print command line arguments,
/// - register option factory functions which are used to generate DHCP options
/// being sent to a server,
/// - create the socket for communication with a server,
/// - read packet templates if user specified template files with '-T' command
/// line option,
/// - set the interrupt handler (invoked when ^C is pressed) which makes
/// perfdhcp stop gracefully and print the test results before exiting,
/// - executes an external command (if specified '-w' option), e.g. if user
/// specified -w ./foo in the command line then program will execute
/// "./foo start" at the beginning of the test and "./foo stop" when the test
/// ends,
/// - initialize the Statistics Manager,
/// - executes the main loop:
///   - calculate how many packets must be send to satisfy desired rate,
///   - receive incoming packets from the server,
///   - check the exit conditions - terminate the program if the exit criteria
///   are fulfilled, e.g. reached maximum number of packet drops,
///   - send the number of packets appropriate to satisfy the desired rate,
///   - optionally print intermediate reports,
/// - print statistics, e.g. achieved rate,
/// - optionally print some diagnostics.
///
/// With the '-w' command line option user may specify the external application
/// or script to be executed. This is executed twice, first when the test starts
/// and second time when the test ends. This external script or application must
/// accept 'start' and 'stop' arguments. The first time it is called, it is
/// called with the argument 'start' and the second time with the argument
/// 'stop'.
///
/// The application is executed by calling fork() to fork the current perfdhcp
/// process and then call execlp() to replace the current process image with
/// the new one.
///
/// Option factory functions are registered using
/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
/// provide a way to create options of the same type in the same way.
///  When a new option instance is needed, the corresponding factory
/// function is called to create it. This is done by calling
/// \ref dhcp::Option::factory with DHCP message type specified as one of
///  parameters. Some of the parameters passed to factory function
/// may be ignored (e.g. option buffer).
/// Please note that naming convention for factory functions within this
/// class is as follows:
/// - factoryABC4 - factory function for DHCPv4 option,
/// - factoryDEF6 - factory function for DHCPv6 option,
/// - factoryGHI - factory function that can be used to create either
/// DHCPv4 or DHCPv6 option.
class TestControl : public boost::noncopyable {
public:
    /// \brief Default constructor.
    TestControl(CommandOptions& options, BasePerfSocket& socket);

    /// Packet template buffer.
    typedef std::vector<uint8_t> TemplateBuffer;
    /// Packet template buffers list.
    typedef std::vector<TemplateBuffer> TemplateBufferCollection;

    /// @brief Delay the exit by a fixed given time to catch up to all exchanges
    ///     that were already started.
    /// @return true if need to wait, false = ok to exit now
    bool waitToExit();

    /// @brief Checks if all expected packets were already received
    bool haveAllPacketsBeenReceived() const;

    /// \brief Number generator class.
    ///
    /// This is default numbers generator class. The member function is
    /// used to generate uint32_t values. Other generator classes should
    /// derive from this one to implement generation algorithms
    /// (e.g. sequential or based on random function).
    class NumberGenerator {
    public:

        /// \brief Destructor.
        virtual ~NumberGenerator() { }

        /// \brief Generate number.
        ///
        /// \return Generate number.
        virtual uint32_t generate() = 0;
    };

    /// The default generator pointer.
    typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;

    /// \brief Sequential numbers generator class.
    class SequentialGenerator : public NumberGenerator {
    public:
        /// \brief Constructor.
        ///
        /// \param range maximum number generated. If 0 is given then
        /// range defaults to maximum uint32_t value.
        SequentialGenerator(uint32_t range = 0xFFFFFFFF) :
            NumberGenerator(),
            num_(0),
            range_(range) {
            if (range_ == 0) {
                range_ = 0xFFFFFFFF;
            }
        }

        /// \brief Generate number sequentially.
        ///
        /// \return generated number.
        virtual uint32_t generate() {
            uint32_t num = num_;
            num_ = (num_ + 1) % range_;
            return (num);
        }
    private:
        uint32_t num_;   ///< Current number.
        uint32_t range_; ///< Number of unique numbers generated.
    };

    /// \brief Random numbers generator class. The generated numbers
    /// are uniformly distributed in the range of [min, max].
    class RandomGenerator : public NumberGenerator {
    public:
        /// \brief Constructor.
        ///
        /// \param min minimum number generated.
        /// \param max maximum number generated.
        RandomGenerator(uint32_t min, uint32_t max) :
            NumberGenerator(),
            distribution_(min, max) {
            // Initialize the randomness source with the current time.
            randomness_generator_.seed(time(0));
        }

        /// \brief Generate number in range of [min, max].
        ///
        /// \return generated number.
        virtual uint32_t generate() {
            return distribution_(randomness_generator_);
        }

    private:
        /// \brief The random values will be uniformly distributed in
        /// the range provided in the constructor.
        std::uniform_int_distribution<> distribution_;

        /// \brief It is a high quality general-purpose random
        /// generator. It isn't suitable for cryptographic use but is
        /// good enough for testing.
        std::mt19937 randomness_generator_;
    };

    /// \brief Length of the Ethernet HW address (MAC) in bytes.
    ///
    /// \todo Make this variable length as there are cases when HW
    /// address is longer than this (e.g. 20 bytes).
    static const uint8_t HW_ETHER_LEN = 6;

    /// \brief Set new transaction id generator.
    ///
    /// \param generator generator object to be used.
    void setTransidGenerator(const NumberGeneratorPtr& generator) {
        transid_gen_.reset();
        transid_gen_ = generator;
    }

    /// \brief Set new MAC address generator.
    ///
    /// Set numbers generator that will be used to generate various
    /// MAC addresses to simulate number of clients.
    ///
    /// \param generator object to be used.
    void setMacAddrGenerator(const NumberGeneratorPtr& generator) {
        macaddr_gen_.reset();
        macaddr_gen_ = generator;
    }

    /// \brief Removes cached DHCPv6 Reply packets every second.
    ///
    /// This function wipes cached Reply packets from the storage.
    /// The number of packets left in the storage after the call
    /// to this function should guarantee that the Renew packets
    /// can be sent at the given rate. Note that the Renew packets
    /// are generated for the existing leases, represented here as
    /// replies from the server.
    /// @todo Instead of cleaning packets periodically we could
    /// just stop adding new packets when the certain threshold
    /// has been reached.
    void cleanCachedPackets();

    /// \brief Get interrupted flag.
    bool interrupted() const { return interrupted_; }

    /// \brief Get stats manager.
    StatsMgr& getStatsMgr() { return stats_mgr_; };

    /// \brief Start receiver.
    void start() { receiver_.start(); }

    /// \brief Stop receiver.
    void stop() { receiver_.stop(); }

    /// \brief Run wrapped command.
    ///
    /// \param do_stop execute wrapped command with "stop" argument.
    void runWrapped(bool do_stop = false) const;

    /// \brief Get received server id flag.
    bool serverIdReceived() const { return first_packet_serverid_.size() > 0; }

    /// \brief Get received server id.
    std::string getServerId() const { return vector2Hex(first_packet_serverid_); }

    /// \brief Send number of packets to initiate new exchanges.
    ///
    /// Method initiates the new DHCP exchanges by sending number
    /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload
    /// mode was requested sent packets will not be counted in
    /// the statistics. The responses from the server will be
    /// received and counted as orphans because corresponding sent
    /// packets are not included in StatsMgr for match.
    /// When preload mode is disabled and diagnostics flag 'i' is
    /// specified then function will be trying to receive late packets
    /// before new packets are sent to the server. Statistics of
    /// late received packets is updated accordingly.
    ///
    /// \todo do not count responses in preload mode as orphans.
    ///
    /// \param packets_num number of packets to be sent.
    /// \param preload preload mode, packets not included in statistics.
    /// \throw isc::Unexpected if thrown by packet sending method.
    /// \throw isc::InvalidOperation if thrown by packet sending method.
    /// \throw isc::OutOfRange if thrown by packet sending method.
    void sendPackets(const uint64_t packets_num,
                     const bool preload = false);

    /// \brief Send number of DHCPREQUEST (renew) messages to a server.
    ///
    /// \param msg_type A type of the messages to be sent (DHCPREQUEST or
    /// DHCPRELEASE).
    /// \param msg_num A number of messages to be sent.
    ///
    /// \return A number of messages actually sent.
    uint64_t sendMultipleMessages4(const uint32_t msg_type,
                                   const uint64_t msg_num);

    /// \brief Send number of DHCPv6 Renew or Release messages to the server.
    ///
    /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
    /// DHCPV6_RELEASE).
    /// \param msg_num A number of messages to be sent.
    ///
    /// \return A number of messages actually sent.
    uint64_t sendMultipleMessages6(const uint32_t msg_type,
                                   const uint64_t msg_num);

    /// \brief Pull packets from receiver and process them.
    ///
    /// It runs in a loop until there are no packets in receiver.
    unsigned int consumeReceivedPackets();

    /// \brief Print intermediate statistics.
    ///
    /// Print brief statistics regarding number of sent packets,
    /// received packets and dropped packets so far.
    void printIntermediateStats();

    /// \brief Print performance statistics.
    ///
    /// Method prints performance statistics.
    /// \throws isc::InvalidOperation if Statistics Manager was
    /// not initialized.
    void printStats() const;

    /// \brief Print templates information.
    ///
    /// Method prints information about data offsets
    /// in packet templates and their contents.
    void printTemplates() const;

    /// \brief Get set of unique replied addresses.
    std::set<std::string>& getAllUniqueAddrReply() {
        return unique_reply_address_;
    }

    /// \brief Get set of unique advertised addresses.
    std::set<std::string>& getAllUniqueAddrAdvert() {
        return unique_address_;
    }

    /// \brief Convert binary value to hex string.
    ///
    /// \todo Consider moving this function to src/lib/util.
    ///
    /// \param b byte to convert.
    /// \return hex string.
    static std::string byte2Hex(const uint8_t b);

    /// \brief Convert vector in hexadecimal string.
    ///
    /// \todo Consider moving this function to src/lib/util.
    ///
    /// \param vec vector to be converted.
    /// \param separator separator.
    static std::string vector2Hex(const std::vector<uint8_t>& vec,
                                  const std::string& separator = "");

    /// \brief Initialized at first exit condition with the time perfdhcp
    ///  should exit
    boost::posix_time::ptime exit_time_;

    // We would really like following methods and members to be private but
    // they have to be accessible for unit-testing. Another, possibly better,
    // solution is to make this class friend of test class but this is not
    // what's followed in other classes.
protected:
    /// \brief Creates DHCPREQUEST from a DHCPACK message.
    ///
    /// @param msg_type the message type to be created (DHCPREQUEST or DHCPRELEASE)
    /// \param ack An instance of the DHCPACK message to be used to
    /// create a new message.
    ///
    /// \return Pointer to the created message.
    dhcp::Pkt4Ptr createMessageFromAck(const uint16_t msg_type,
                                       const dhcp::Pkt4Ptr& ack);

    /// \brief Creates DHCPv6 message from the Reply packet.
    ///
    /// This function creates DHCPv6 Renew or Release message using the
    /// data from the Reply message by copying options from the Reply
    /// message.
    ///
    /// \param msg_type A type of the message to be created.
    /// \param reply An instance of the Reply packet which contents should
    /// be used to create an instance of the new message.
    ///
    /// \return created Release or Renew message
    /// \throw isc::BadValue if the msg_type is neither DHCPV6_RENEW nor
    /// DHCPV6_RELEASE or if the reply is NULL.
    /// \throw isc::Unexpected if mandatory options are missing in the
    /// Reply message.
    dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type,
                                         const dhcp::Pkt6Ptr& reply);

    /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
    ///
    /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
    /// If empty buffer is passed the option buffer will be initialized
    /// to length 2 and values will be initialized to zeros. Otherwise
    /// function will initialize option buffer with values in passed buffer.
    ///
    /// \param u universe (ignored)
    /// \param type option-type (ignored).
    /// \param buf option-buffer containing option content (2 bytes) or
    /// empty buffer if option content has to be set to default (0) value.
    /// \throw if elapsed time buffer size is neither 2 nor 0.
    /// \return instance o the option.
    static dhcp::OptionPtr
    factoryElapsedTime6(dhcp::Option::Universe u,
                        uint16_t type,
                        const dhcp::OptionBuffer& buf);

    /// \brief Factory function to create generic option.
    ///
    /// This factory function creates option with specified universe,
    /// type and buf. It does not have any additional logic validating
    /// the buffer contents, size  etc.
    ///
    /// \param u universe (V6 or V4).
    /// \param type option-type (ignored).
    /// \param buf option-buffer.
    /// \return instance o the option.
    static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u,
                                          uint16_t type,
                                          const dhcp::OptionBuffer& buf);

    /// \brief Factory function to create IA_NA option.
    ///
    /// This factory function creates DHCPv6 IA_NA option instance.
    ///
    /// \todo add support for IA Address options.
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer carrying IANA suboptions.
    /// \return instance of IA_NA option.
    static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u,
                                        uint16_t type,
                                        const dhcp::OptionBuffer& buf);

    /// \brief Factory function to create IA_PD option.
    ///
    /// this factory function creates DHCPv6 IA_PD option instance.
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer carrying sub-options.
    static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u,
                                        uint16_t type,
                                        const dhcp::OptionBuffer& buf);

    /// \brief Factory function to create DHCPv6 ORO option.
    ///
    /// This factory function creates DHCPv6 Option Request Option instance.
    /// The created option will contain the following set of requested options:
    /// - D6O_NAME_SERVERS
    /// - D6O_DOMAIN_SEARCH
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
    /// \return instance of ORO option.
    static dhcp::OptionPtr
    factoryOptionRequestOption6(dhcp::Option::Universe u,
                                uint16_t type,
                                const dhcp::OptionBuffer& buf);

    /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance.
    ///
    /// This factory function creates DHCPv6 RAPID_COMMIT option instance.
    /// The buffer passed to this option must be empty because option does
    /// not have any payload.
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
    /// \return instance of RAPID_COMMIT option..
    static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u,
                                               uint16_t type,
                                               const dhcp::OptionBuffer& buf);


    /// \brief Factory function to create DHCPv4 Request List option.
    ///
    /// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option
    /// instance with the following set of requested options:
    /// - DHO_SUBNET_MASK,
    /// - DHO_BROADCAST_ADDRESS,
    /// - DHO_TIME_OFFSET,
    /// - DHO_ROUTERS,
    /// - DHO_DOMAIN_NAME,
    /// - DHO_DOMAIN_NAME_SERVERS,
    /// - DHO_HOST_NAME.
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
    /// \return instance o the generic option.
    static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
                                               uint16_t type,
                                               const dhcp::OptionBuffer& buf);

    /// \brief Generate DHCPv4 client identifier from HW address.
    ///
    /// This method generates DHCPv4 client identifier option from a
    /// HW address.
    ///
    /// \param hwaddr HW address.
    ///
    /// \return Pointer to an instance of the generated option.
    dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr& hwaddr) const;

    /// \brief Generate DUID.
    ///
    /// Method generates unique DUID. The number of DUIDs it can generate
    /// depends on the number of simulated clients, which is specified
    /// from the command line. It uses \ref CommandOptions object to retrieve
    /// number of clients. Since the last six octets of DUID are constructed
    /// from the MAC address, this function uses \ref generateMacAddress
    /// internally to randomize the DUID.
    ///
    /// \todo add support for other types of DUID.
    ///
    /// \param [out] randomized number of bytes randomized (initial value
    /// is ignored).
    /// \throw isc::BadValue if \ref generateMacAddress throws.
    /// \return vector representing DUID.
    std::vector<uint8_t> generateDuid(uint8_t& randomized);

    /// \brief Generate MAC address.
    ///
    /// This method generates MAC address. The number of unique
    /// MAC addresses it can generate is determined by the number
    /// simulated DHCP clients specified from command line. It uses
    /// \ref CommandOptions object to retrieve number of clients.
    /// Based on this the random value is generated and added to
    /// the MAC address template (default MAC address).
    ///
    /// \param [out] randomized number of bytes randomized (initial
    /// value is ignored).
    /// \throw isc::BadValue if MAC address template (default or specified
    /// from the command line) has invalid size (expected 6 octets).
    /// \return generated MAC address.
    std::vector<uint8_t> generateMacAddress(uint8_t& randomized);

    /// \brief generate transaction id.
    ///
    /// Generate transaction id value (32-bit for DHCPv4,
    /// 24-bit for DHCPv6).
    ///
    /// \return generated transaction id.
    uint32_t generateTransid() {
        return (transid_gen_->generate());
    }

    /// \brief Return template buffer.
    ///
    /// Method returns template buffer at specified index.
    ///
    /// \param idx index of template buffer.
    /// \throw isc::OutOfRange if buffer index out of bounds.
    /// \return reference to template buffer.
    TemplateBuffer getTemplateBuffer(const size_t idx) const;

    /// \brief Reads packet templates from files.
    ///
    /// Method iterates through all specified template files, reads
    /// their content and stores it in class internal buffers. Template
    /// file names are specified from the command line with -T option.
    ///
    /// \throw isc::BadValue if any of the template files does not exist,
    /// contains characters other than hexadecimal digits or spaces.
    /// \throw OutOfRange if any of the template files is empty or has
    /// odd number of hexadecimal digits.
    void initPacketTemplates();

    /// \brief Print rate statistics.
    ///
    /// Method print packet exchange rate statistics.
    void printRate() const;

    /// \brief Process received DHCPv4 packet.
    ///
    /// Method performs processing of the received DHCPv4 packet,
    /// updates statistics and responds to the server if required,
    /// e.g. when OFFER packet arrives, this function will initiate
    /// REQUEST message to the server.
    ///
    /// \warning this method does not check if provided socket is
    /// valid (specifically if v4 socket for received v4 packet).
    ///
    /// \param [in] pkt4 object representing DHCPv4 packet received.
    /// \throw isc::BadValue if unknown message type received.
    /// \throw isc::Unexpected if unexpected error occurred.
    void processReceivedPacket4(const dhcp::Pkt4Ptr& pkt4);

    /// \brief Process IA in received DHCPv6 packet.
    ///
    /// Process IA in received message to check if it contain proper
    /// address and/or prefix
    ///
    /// \param [in] pkt6 object representing DHCPv6 packet received.
    /// \return true if the message include correct IA, false otherwise.
    bool validateIA(const dhcp::Pkt6Ptr& pkt6);

    /// \brief Process received v6 addresses uniqueness.
    ///
    /// Generate list of addresses and check for uniqueness.
    ///
    /// \param pkt6 object representing received DHCPv6 packet
    /// \param xchg_type ExchangeType enum value.
    void address6Uniqueness(const dhcp::Pkt6Ptr& pkt6, ExchangeType xchg_type);

    /// \brief Process received v4 addresses uniqueness.
    ///
    /// Generate list of addresses and check for uniqueness.
    ///
    /// \param pkt4 object representing received DHCPv4 packet
    /// \param xchg_type ExchangeType enum value.
    void address4Uniqueness(const dhcp::Pkt4Ptr& pkt4, ExchangeType xchg_type);

    /// \brief add unique address to already assigned list.
    ///
    /// Add address and/or prefix to unique set if it's not already there,
    /// otherwise increment the number of non unique addresses.
    ///
    /// \param current set of addresses that should be added to unique list
    /// \param xchg_type ExchangeType enum value.
    void addUniqeAddr(const std::set<std::string>& current, ExchangeType xchg_type) {
        switch(xchg_type) {
            case ExchangeType::SA: {
                for (auto const& current_it : current) {
                    // addresses should be unique cross packets
                    auto ret = unique_address_.emplace(current_it);
                    if (!ret.second) {
                        stats_mgr_.updateNonUniqueAddrNum(ExchangeType::SA);
                    }
                }
                break;
            }
            case ExchangeType::RR: {
                for (auto const& current_it : current) {
                    // addresses should be unique cross packets
                    auto ret = unique_reply_address_.emplace(current_it);
                    if (!ret.second) {
                        stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RR);
                    }
                }
                break;
            }
            case ExchangeType::RLA:
            case ExchangeType::RL: {
                removeUniqueAddr(current);
                break;
            }
            case ExchangeType::DO: {
                for (auto const& current_it : current) {
                    // addresses should be unique cross packets
                    auto ret = unique_address_.emplace(current_it);
                    if (!ret.second) {
                        stats_mgr_.updateNonUniqueAddrNum(ExchangeType::DO);
                    }
                }
                break;
            }
            case ExchangeType::RA: {
                for (auto const& current_it : current) {
                    // addresses should be unique cross packets
                    auto ret = unique_reply_address_.emplace(current_it);
                    if (!ret.second) {
                        stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RA);
                    }
                }
                break;
            }
            case ExchangeType::RNA:
            case ExchangeType::RN:
            default:
                break;
        }
    }

    /// \brief remove unique address from list.
    ///
    /// If address is released we should remove it from both
    /// advertised (offered) and assigned sets.
    ///
    /// \param addr holding value of unique address.
    void removeUniqueAddr(const std::set<std::string>& addr) {
        for (auto const& addr_it : addr) {
            auto it = unique_address_.find(addr_it);
            if (it != unique_address_.end()) {
                unique_address_.erase(it);
            }

            auto it2 = unique_reply_address_.find(addr_it);
            if (it2 != unique_reply_address_.end()) {
                unique_reply_address_.erase(it2);
            }
        }
    }

    /// \brief Process received DHCPv6 packet.
    ///
    /// Method performs processing of the received DHCPv6 packet,
    /// updates statistics and responds to the server if required,
    /// e.g. when ADVERTISE packet arrives, this function will initiate
    /// REQUEST message to the server.
    ///
    /// \param [in] pkt6 object representing DHCPv6 packet received.
    /// \throw isc::BadValue if unknown message type received.
    /// \throw isc::Unexpected if unexpected error occurred.
    void processReceivedPacket6(const dhcp::Pkt6Ptr& pkt6);

    /// \brief Register option factory functions for DHCPv4.
    ///
    /// Method registers option factory functions for DHCPv4.
    /// These functions are called to create instances of DHCPv4
    /// options. Call \ref dhcp::Option::factory to invoke factory
    /// function for particular option. Don't use this function directly.
    /// Use \ref registerOptionFactories instead.
    void registerOptionFactories4() const;

    /// \brief Register option factory functions for DHCPv6.
    ///
    /// Method registers option factory functions for DHCPv6.
    /// These functions are called to create instances of DHCPv6
    /// options. Call \ref dhcp::Option::factory to invoke factory
    /// function for particular option. Don't use this function directly.
    /// Use \ref registerOptionFactories instead.
    void registerOptionFactories6() const;

    /// \brief Register option factory functions for DHCPv4 or DHCPv6.
    ///
    /// Method registers option factory functions for DHCPv4 or DHCPv6,
    /// depending in which mode test is currently running.
    void registerOptionFactories() const;

    /// \brief Resets internal state of the object.
    ///
    /// Method resets internal state of the object. It has to be
    /// called before new test is started.
    void reset();

    /// \brief Save the first DHCPv4 sent packet of the specified type.
    ///
    /// This method saves first packet of the specified being sent
    /// to the server if user requested diagnostics flag 'T'. In
    /// such case program has to print contents of selected packets
    /// being sent to the server. It collects first packets of each
    /// type and keeps them around until test finishes. Then they
    /// are printed to the user. If packet of specified type has
    /// been already stored this function performs no operation.
    /// This function does not perform sanity check if packet
    /// pointer is valid. Make sure it is before calling it.
    ///
    /// \param pkt packet to be stored.
    inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt);

    /// \brief Save the first DHCPv6 sent packet of the specified type.
    ///
    /// This method saves first packet of the specified being sent
    /// to the server if user requested diagnostics flag 'T'. In
    /// such case program has to print contents of selected packets
    /// being sent to the server. It collects first packets of each
    /// type and keeps them around until test finishes. Then they
    /// are printed to the user. If packet of specified type has
    /// been already stored this function performs no operation.
    /// This function does not perform sanity check if packet
    /// pointer is valid. Make sure it is before calling it.
    ///
    /// \param pkt packet to be stored.
    inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt);

    /// \brief Send DHCPv4 DISCOVER message.
    ///
    /// Method creates and sends DHCPv4 DISCOVER message to the server
    /// with the following options:
    /// - MESSAGE_TYPE set to DHCPDISCOVER
    /// - PARAMETER_REQUEST_LIST with the same list of requested options
    /// as described in \ref factoryRequestList4.
    /// The transaction id and MAC address are randomly generated for
    /// the message. Range of unique MAC addresses generated depends
    /// on the number of clients specified from the command line.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param preload preload mode, packets not included in statistics.
    ///
    /// \throw isc::Unexpected if failed to create new packet instance.
    /// \throw isc::BadValue if MAC address has invalid length.
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendDiscover4(const bool preload = false);

    /// \brief Send DHCPv4 DISCOVER message from template.
    ///
    /// Method sends DHCPv4 DISCOVER message from template. The
    /// template data is expected to be in binary format. Provided
    /// buffer is copied and parts of it are replaced with actual
    /// data (e.g. MAC address, transaction id etc.).
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param template_buf buffer holding template packet.
    /// \param preload preload mode, packets not included in statistics.
    ///
    /// \throw isc::OutOfRange if randomization offset is out of bounds.
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendDiscover4(const std::vector<uint8_t>& template_buf,
                       const bool preload = false);

    /// \brief Send DHCPv4 renew (DHCPREQUEST).
    ///
    /// \param msg_type A type of the message to be sent (DHCPREQUEST or
    /// DHCPRELEASE).
    ///
    /// \return true if the message has been sent, false otherwise.
    bool sendMessageFromAck(const uint16_t msg_type);

    /// \brief Send DHCPv6 Renew or Release message.
    ///
    /// This method will select an existing lease from the Reply packet cache
    /// If there is no lease that can be renewed or released this method will
    /// return false.
    ///
    /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
    /// DHCPV6_RELEASE).
    ///
    /// \return true if the message has been sent, false otherwise.
    bool sendMessageFromReply(const uint16_t msg_type);

    /// \brief Send DHCPv4 REQUEST message.
    ///
    /// Method creates and sends DHCPv4 REQUEST message to the server.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param discover_pkt4 DISCOVER packet sent.
    /// \param offer_pkt4 OFFER packet object.
    ///
    /// \throw isc::Unexpected if unexpected error occurred.
    /// \throw isc::InvalidOperation if Statistics Manager has not been
    /// initialized.
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4,
                      const dhcp::Pkt4Ptr& offer_pkt4);

    /// \brief Send DHCPv4 REQUEST message from template.
    ///
    /// Method sends DHCPv4 REQUEST message from template.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param template_buf buffer holding template packet.
    /// \param discover_pkt4 DISCOVER packet sent.
    /// \param offer_pkt4 OFFER packet received.
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendRequest4(const std::vector<uint8_t>& template_buf,
                      const dhcp::Pkt4Ptr& discover_pkt4,
                      const dhcp::Pkt4Ptr& offer_pkt4);

    /// \brief Send DHCPv6 REQUEST message.
    ///
    /// Method creates and sends DHCPv6 REQUEST message to the server
    /// with the following options:
    /// - D6O_ELAPSED_TIME
    /// - D6O_CLIENTID
    /// - D6O_SERVERID
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param advertise_pkt6 ADVERTISE packet object.
    /// \throw isc::Unexpected if unexpected error occurred.
    /// \throw isc::InvalidOperation if Statistics Manager has not been
    /// initialized.
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendRequest6(const dhcp::Pkt6Ptr& advertise_pkt6);

    /// \brief Send DHCPv6 REQUEST message from template.
    ///
    /// Method sends DHCPv6 REQUEST message from template.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param template_buf packet template buffer.
    /// \param advertise_pkt6 ADVERTISE packet object.
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendRequest6(const std::vector<uint8_t>& template_buf,
                      const dhcp::Pkt6Ptr& advertise_pkt6);

    /// \brief Send DHCPv6 SOLICIT message.
    ///
    /// Method creates and sends DHCPv6 SOLICIT message to the server
    /// with the following options:
    /// - D6O_ELAPSED_TIME,
    /// - D6O_RAPID_COMMIT if rapid commit is requested in command line,
    /// - D6O_CLIENTID,
    /// - D6O_ORO (Option Request Option),
    /// - D6O_IA_NA.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param preload mode, packets not included in statistics.
    ///
    /// \throw isc::Unexpected if failed to create new packet instance.
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendSolicit6(const bool preload = false);

    /// \brief Send DHCPv6 SOLICIT message from template.
    ///
    /// Method sends DHCPv6 SOLICIT message from template.
    /// Copy of sent packet is stored in the stats_mgr_ object to
    /// update statistics.
    ///
    /// \param template_buf packet template buffer.
    /// \param preload mode, packets not included in statistics.
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
    void sendSolicit6(const std::vector<uint8_t>& template_buf,
                      const bool preload = false);

    /// \brief Set default DHCPv4 packet parameters.
    ///
    /// This method sets default parameters on the DHCPv4 packet:
    /// - interface name,
    /// - local port = 68 (DHCP client port),
    /// - remote port = 67 (DHCP server port),
    /// - server's address,
    /// - GIADDR = local address where socket is bound to,
    /// - hops = 1 (pretending that we are a relay)
    ///
    /// \param pkt reference to packet to be configured.
    void setDefaults4(const dhcp::Pkt4Ptr& pkt);

    /// \brief Set default DHCPv6 packet parameters.
    ///
    /// This method sets default parameters on the DHCPv6 packet:
    /// - interface name,
    /// - interface index,
    /// - local port,
    /// - remote port,
    /// - local address,
    /// - remote address (server).
    ///
    /// \param pkt reference to packet to be configured.
    void setDefaults6(const dhcp::Pkt6Ptr& pkt);

    /// @brief Inserts extra options specified by user.
    ///
    /// Note: addExtraOpts for v4 and v6 could easily be turned into a template.
    /// However, this would require putting code here that uses CommandOptions,
    /// and that would create dependency between test_control.h and
    /// command_options.h.
    ///
    /// @param pkt4 options will be added here.
    void addExtraOpts(const dhcp::Pkt4Ptr& pkt4);

    /// @brief Inserts extra options specified by user.
    ///
    /// Note: addExtraOpts for v4 and v6 could easily be turned into a template.
    /// However, this would require putting code here that uses CommandOptions,
    /// and that would create dependency between test_control.h and
    /// command_options.h.
    ///
    /// @param pkt6 options will be added here.
    void addExtraOpts(const dhcp::Pkt6Ptr& pkt6);

    /// \brief Copies IA_NA or IA_PD option from one packet to another.
    ///
    /// This function checks the lease-type specified in the command line
    /// with option -e<lease-type>. If 'address-only' value has been specified
    /// this function expects that IA_NA option is present in the packet
    /// encapsulated by pkt_from object. If 'prefix-only' value has been
    /// specified, this function expects that IA_PD option is present in the
    /// packet encapsulated by pkt_to object.
    ///
    /// \param [in] pkt_from A packet from which options should be copied.
    /// \param [out] pkt_to A packet to which options should be copied.
    ///
    /// \throw isc::NotFound if a required option is not found in the
    /// packet from which options should be copied.
    /// \throw isc::BadValue if any of the specified pointers to packets
    /// is NULL.
    void copyIaOptions(const dhcp::Pkt6Ptr& pkt_from, dhcp::Pkt6Ptr& pkt_to);

    /// \brief Calculate elapsed time between two packets.
    ///
    /// This function calculates the time elapsed between two packets. If
    /// the timestamp of the pkt2 is greater than timestamp of the pkt1,
    /// the positive value is returned. If the pkt2 timestamp is equal or
    /// less than pkt1 timestamp, 0 is returned.
    ///
    /// \tparam T Pkt4Ptr or Pkt6Ptr class.
    /// \param pkt1 first packet.
    /// \param pkt2 second packet.
    /// \throw InvalidOperation if packet timestamps are invalid.
    /// \return elapsed time in milliseconds between pkt1 and pkt2.
    template<class T>
    uint32_t getElapsedTime(const T& pkt1, const T& pkt2);

    /// \brief Return elapsed time offset in a packet.
    ///
    /// \return elapsed time offset in packet.
    int getElapsedTimeOffset() const;

    /// \brief Return randomization offset in a packet.
    ///
    /// \return randomization offset in packet.
    int getRandomOffset(const int arg_idx) const;

    /// \brief Return requested ip offset in a packet.
    ///
    /// \return randomization offset in a packet.
    int getRequestedIpOffset() const;

    /// \brief Return server id offset in a packet.
    ///
    /// \return server id offset in packet.
    int getServerIdOffset() const;

    /// \brief Return transaction id offset in a packet.
    ///
    /// \param arg_idx command line argument index to be used.
    /// If multiple -X parameters specified it points to the
    /// one to be used.
    /// \return transaction id offset in packet.
    int getTransactionIdOffset(const int arg_idx) const;

    /// \brief Handle child signal.
    ///
    /// Function handles child signal by waiting for
    /// the process to complete.
    ///
    /// \param sig signal (ignored).
    static void handleChild(int sig);

    /// \brief Handle interrupt signal.
    ///
    /// Function sets flag indicating that program has been
    /// interrupted.
    ///
    /// \param sig signal (ignored).
    static void handleInterrupt(int sig);

    /// \brief Print main diagnostics data.
    ///
    /// Method prints main diagnostics data.
    void printDiagnostics() const;

    /// \brief Print template information.
    ///
    /// \param packet_type packet type.
    void printTemplate(const uint8_t packet_type) const;

    /// \brief Read DHCP message template from file.
    ///
    /// Method reads DHCP message template from file and
    /// converts it to binary format. Read data is appended
    /// to template_buffers_ vector.
    ///
    /// \param file_name name of the packet template file.
    /// \throw isc::OutOfRange if file is empty or has odd number
    /// of hexadecimal digits.
    /// \throw isc::BadValue if file contains characters other than
    /// spaces or hexadecimal digits.
    void readPacketTemplate(const std::string& file_name);

    /// \brief Keep addresses and prefixes from advertise msg for uniqueness checks.
    std::set<std::string> unique_address_;

    /// \brief Keep addresses and prefixes from reply msg for uniqueness checks.
    std::set<std::string> unique_reply_address_;

    /// \brief Socket used for DHCP traffic.
    BasePerfSocket &socket_;

    /// \brief Receiver used to receive DHCP traffic.
    Receiver receiver_;

    /// \brief Last intermediate report time.
    boost::posix_time::ptime last_report_;

    /// \brief Statistics Manager.
    StatsMgr stats_mgr_;

    /// \brief Storage for DHCPACK messages.
    PacketStorage<dhcp::Pkt4> ack_storage_;

    /// \brief Storage for reply messages.
    PacketStorage<dhcp::Pkt6> reply_storage_;

    /// \brief Generate uniformly distributed integers in range of
    /// [min, max].
    NumberGeneratorPtr random_generator_;

    /// \brief Transaction id generator.
    NumberGeneratorPtr transid_gen_;

    /// \brief Numbers generator for MAC address.
    NumberGeneratorPtr macaddr_gen_;

    /// \brief Buffer holding server id received in first packet
    dhcp::OptionBuffer first_packet_serverid_;

    /// \brief Packet template buffers.
    TemplateBufferCollection template_buffers_;

    /// First packets send. They are used at the end of the test
    /// to print packet templates when diagnostics flag T is specified.

    /// \brief Template for v4.
    std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_;

    /// \brief Template for v6.
    std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_;

    /// \brief Program interrupted flag.
    static bool interrupted_;

    /// \brief Command options.
    CommandOptions& options_;
};

}  // namespace perfdhcp
}  // namespace isc

#endif // TEST_CONTROL_H