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
// Copyright (C) 2014-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 PKT_H
#define PKT_H

#include <asiolink/io_address.h>
#include <util/buffer.h>
#include <dhcp/option.h>
#include <dhcp/hwaddr.h>
#include <dhcp/classify.h>
#include <hooks/callout_handle_associate.h>

#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 <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

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

namespace isc {

namespace dhcp {

/// @brief A value used to signal that the interface index was not set.
/// That means that more than UNSET_IFINDEX interfaces are not supported.
/// That's fine, since it would have overflowed with UNSET_IFINDEX + 1 anyway.
constexpr unsigned int UNSET_IFINDEX = std::numeric_limits<unsigned int>::max();

/// @brief RAII object enabling copying options retrieved from the
/// packet.
///
/// This object enables copying retrieved options from a packet within
/// a scope in which this object exists. When the object goes out of scope
/// copying options is disabled. This is applicable in cases when the
/// server is going to invoke a callout (hook library) where copying options
/// must be enabled by default. When the callouts return copying options
/// should be disabled. The use of RAII object eliminates the need for
/// explicitly re-disabling options copying and is safer in case of
/// exceptions thrown by callouts and a presence of multiple exit points.
///
/// @tparam PktType Type of the packet, e.g. Pkt4, Pkt6, Pkt4o6.
template<typename PktType>
class ScopedEnableOptionsCopy {
public:

    /// @brief Pointer to an encapsulated packet.
    typedef boost::shared_ptr<PktType> PktTypePtr;

    /// @brief Constructor.
    ///
    /// Enables options copying on a packet(s).
    ///
    /// @param pkt1 Pointer to first packet.
    /// @param pkt2 Optional pointer to the second packet.
    ScopedEnableOptionsCopy(const PktTypePtr& pkt1,
                            const PktTypePtr& pkt2 = PktTypePtr())
        : pkts_(pkt1, pkt2) {
        if (pkt1) {
            pkt1->setCopyRetrievedOptions(true);
        }
        if (pkt2) {
            pkt2->setCopyRetrievedOptions(true);
        }
    }

    /// @brief Destructor.
    ///
    /// Disables options copying on a packets.
    ~ScopedEnableOptionsCopy() {
        if (pkts_.first) {
            pkts_.first->setCopyRetrievedOptions(false);
        }
        if (pkts_.second) {
            pkts_.second->setCopyRetrievedOptions(false);
        }
    }

private:

    /// @brief Holds a pair of pointers of the packets.
    std::pair<PktTypePtr, PktTypePtr> pkts_;
};


/// @brief Describes an event during the life cycle of a packet.
class PktEvent {
public:
    /// @brief Event that marks when a packet is placed in the socket buffer
    /// by the kernel.
    static const std::string SOCKET_RECEIVED;

    /// @brief Event that marks when a packet is read from the socket buffer
    /// by application.
    static const std::string BUFFER_READ;

    /// @brief Event that marks when a packet is been written to the socket
    /// by application.
    static const std::string RESPONSE_SENT;

    /// @brief Constructor.
    ///
    /// @param label string identifying the event.
    /// @param timestamp time at which the event occurred.
    PktEvent(const std::string& label, boost::posix_time::ptime timestamp)
        : label_(label), timestamp_(timestamp) {
    }

    /// @brief Destructor.
    ~PktEvent() = default;

    /// @brief Fetch the current UTC system time, microsecond precision.
    ///
    /// @return ptime containing the microsecond system time.
    static boost::posix_time::ptime now() {
        return (boost::posix_time::microsec_clock::universal_time());
    }

    /// @brief Fetch an empty timestamp, used for logic comparisons
    ///
    /// @return an unset ptime.
    static boost::posix_time::ptime& EMPTY_TIME() {
        static boost::posix_time::ptime empty_time;
        return (empty_time);
    }

    /// @brief Fetches the minimum timestamp
    ///
    /// @return the minimum timestamp
    static boost::posix_time::ptime& MIN_TIME() {
        static auto min_time = boost::posix_time::ptime(boost::posix_time::min_date_time);
        return (min_time);
    }

    /// @brief Fetches the maximum timestamp
    ///
    /// @return the maximum timestamp
    static boost::posix_time::ptime& MAX_TIME() {
        static auto max_time = boost::posix_time::ptime(boost::posix_time::max_date_time);
        return (max_time);
    }

    /// @brief Label identifying this event.
    std::string label_;

    /// @brief Timestamp at which the event occurred.
    boost::posix_time::ptime timestamp_;
};

/// @brief Base class for classes representing DHCP messages.
///
/// This is a base class that holds common information (e.g. source
/// and destination ports) and operations (e.g. add, get, delete options)
/// for derived classes representing both DHCPv4 and DHCPv6 messages.
/// The @c Pkt4 and @c Pkt6 classes derive from it.
///
/// @note This is abstract class. Please instantiate derived classes
/// such as @c Pkt4 or @c Pkt6.
class Pkt : public hooks::CalloutHandleAssociate {
protected:

    /// @brief Constructor.
    ///
    /// This constructor is typically used for transmitted messages as it
    /// creates an empty (no options) packet. The constructor is protected,
    /// so only derived classes can call it. Pkt class cannot be instantiated
    /// anyway, because it is an abstract class.
    ///
    /// @param transid transaction-id
    /// @param local_addr local IPv4 or IPv6 address
    /// @param remote_addr remote IPv4 or IPv6 address
    /// @param local_port local UDP (one day also TCP) port
    /// @param remote_port remote UDP (one day also TCP) port
    Pkt(uint32_t transid, const isc::asiolink::IOAddress& local_addr,
        const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
        uint16_t remote_port);

    /// @brief Constructor.
    ///
    /// This constructor is typically used for received messages as it takes
    /// a buffer that's going to be parsed as one of arguments. The constructor
    /// is protected, so only derived classes can call it. Pkt class cannot be
    /// instantiated anyway, because it is an abstract class.
    ///
    /// @param buf pointer to a buffer that contains on-wire data
    /// @param len length of the pointer specified in buf
    /// @param local_addr local IPv4 or IPv6 address
    /// @param remote_addr remote IPv4 or IPv6 address
    /// @param local_port local UDP (one day also TCP) port
    /// @param remote_port remote UDP (one day also TCP) port
    Pkt(const uint8_t* buf, uint32_t len,
        const isc::asiolink::IOAddress& local_addr,
        const isc::asiolink::IOAddress& remote_addr, uint16_t local_port,
        uint16_t remote_port);

public:

    /// @brief Prepares on-wire format of DHCP (either v4 or v6) packet.
    ///
    /// Prepares on-wire format of message and all its options.
    /// A caller must ensure that options are stored in options_ field
    /// prior to calling this method.
    ///
    /// Output buffer will be stored in buffer_out_.
    /// The buffer_out_ should be cleared before writing to the buffer
    /// in the derived classes.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// @throw InvalidOperation if packing fails
    virtual void pack() = 0;

    /// @brief Parses on-wire form of DHCP (either v4 or v6) packet.
    ///
    /// Parses received packet, stored in on-wire format in data_.
    ///
    /// Will create a collection of option objects that will
    /// be stored in options_ container.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// Method will throw exception if packet parsing fails.
    ///
    /// @throw tbd
    virtual void unpack() = 0;

    /// @brief Returns reference to output buffer.
    ///
    /// Returned buffer will contain reasonable data only for
    /// output (TX) packet and after pack() was called.
    ///
    /// RX packet or TX packet before pack() will return buffer with
    /// zero length. This buffer is returned as non-const, so hooks
    /// framework (and user's callouts) can modify them if needed
    ///
    /// @note This buffer is only valid till object that returned it exists.
    ///
    /// @return reference to output buffer
    isc::util::OutputBuffer& getBuffer() {
        return (buffer_out_);
    }

    /// @brief Adds an option to this packet.
    ///
    /// Derived classes may provide more specialized implementations.
    /// In particular @c Pkt4 provides one that checks if option is
    /// unique.
    ///
    /// @param opt option to be added.
    virtual void addOption(const OptionPtr& opt);

    /// @brief Attempts to delete first suboption of requested type.
    ///
    /// If there are several options of the same type present, only
    /// the first option will be deleted.
    ///
    /// @param type Type of option to be deleted.
    ///
    /// @return true if option was deleted, false if no such option existed
    bool delOption(uint16_t type);

    /// @brief Returns text representation primary packet identifiers
    ///
    /// This method is intended to be used to provide as a consistent way to
    /// identify packets within log statements.  Derivations should supply
    /// there own implementation.
    ///
    /// @return string with text representation
    virtual std::string getLabel() const {
        isc_throw(NotImplemented, "Pkt::getLabel()");
    }

    /// @brief Returns text representation of the packet.
    ///
    /// This function is useful mainly for debugging.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// @return string with text representation
    virtual std::string toText() const = 0;

    /// @brief Returns packet size in binary format.
    ///
    /// Returns size of the packet in on-wire format or size needed to store
    /// it in on-wire format.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// @return packet size in bytes
    virtual size_t len() = 0;

    /// @brief Returns message type (e.g. 1 = SOLICIT).
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// @return message type
    virtual uint8_t getType() const = 0;

    /// @brief Sets message type (e.g. 1 = SOLICIT).
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt4 and @c Pkt6 class have respective
    /// implementations of this method.
    ///
    /// @param type message type to be set
    virtual void setType(uint8_t type) = 0;

    /// @brief Returns name of the DHCP message.
    ///
    /// For all unsupported messages the derived classes must return
    /// "UNKNOWN".
    ///
    /// @return Pointer to "const" string containing DHCP message name.
    /// The implementations in the derived classes should statically
    /// allocate returned strings and the caller must not release the
    /// returned pointer.
    virtual const char* getName() const = 0;

    /// @brief Sets transaction-id value.
    ///
    /// @param transid transaction-id to be set.
    void setTransid(uint32_t transid) {
        transid_ = transid;
    }

    /// @brief Returns value of transaction-id field.
    ///
    /// @return transaction-id
    uint32_t getTransid() const {
        return (transid_);
    }

    /// @brief Checks whether a client belongs to a given class.
    ///
    /// @param client_class name of the class
    /// @return true if belongs
    bool inClass(const isc::dhcp::ClientClass& client_class);

    /// @brief Adds a specified class to the packet.
    ///
    /// A class can be added to the same packet repeatedly. Any additional
    /// attempts to add to a packet the class already added, will be
    /// ignored silently.
    ///
    /// @param client_class name of the class to be added
    void addClass(const isc::dhcp::ClientClass& client_class);

    /// @brief Adds a specified class to the packet's additional class list.
    ///
    /// A class can be added to the same packet repeatedly. Any additional
    /// attempts to add to a packet the class already added, will be
    /// ignored silently.
    ///
    /// @param client_class name of the class to be added
    void addAdditionalClass(const isc::dhcp::ClientClass& client_class);

    /// @brief Adds a specified subclass to the packet.
    ///
    /// A subclass can be added to the same packet repeatedly. Any additional
    /// attempts to add to a packet the subclass already added, will be
    /// ignored silently.
    ///
    /// @param class_def name of the class definition to be added
    /// @param subclass name of the subclass to be added
    void addSubClass(const isc::dhcp::ClientClass& class_def,
                     const isc::dhcp::ClientClass& subclass);

    /// @brief Returns the class set
    ///
    /// @note This should be used only to iterate over the class set.
    /// @return Classes to which the packet belongs
    const ClientClasses& getClasses() const {
        return (classes_);
    }

    /// @brief Returns the additional class list.
    ///
    /// @note This should be used only to iterate over the additional class set.
    /// @return The classes to be evaluated.
    const ClientClasses& getAdditionalClasses() const {
        return (additional_classes_);
    }

    /// @brief Returns the class set including template classes associated with
    /// subclasses
    ///
    /// @note This should be used only to iterate over the class set.
    /// @note SubClasses are always last.
    /// @return sub class relationships to which the packet belongs.
    const SubClassRelationContainer& getSubClassesRelations() const {
        return (subclasses_);
    }

    /// @brief Unparsed data (in received packets).
    ///
    /// @warning This public member is accessed by derived
    /// classes directly. One of such derived classes is
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
    /// behavior must be taken into consideration before making
    /// changes to this member such as access scope restriction or
    /// data format change etc.
    OptionBuffer data_;

protected:

    /// @brief Returns the first option of specified type without copying.
    ///
    /// This method is internally used by the @ref Pkt class and derived
    /// classes to retrieve a pointer to the specified option. This
    /// method doesn't copy the option before returning it to the
    /// caller.
    ///
    /// @param type Option type.
    ///
    /// @return Pointer to the option of specified type or NULL pointer
    /// if such option is not present.
    OptionPtr getNonCopiedOption(const uint16_t type) const;

    /// @brief Returns all option instances of specified type without
    /// copying.
    ///
    /// This is a variant of @ref getOptions method, which returns a collection
    /// of options without copying them. This method should be only used by
    /// the @ref Pkt6 class and derived classes. Any external callers should
    /// use @ref getOptions which copies option instances before returning them
    /// when the @ref Pkt::copy_retrieved_options_ flag is set to true.
    ///
    /// @param opt_type Option code.
    ///
    /// @return Collection of options found.
    OptionCollection getNonCopiedOptions(const uint16_t opt_type) const;

public:

    /// @brief Clones all options so that they can be safely modified.
    ///
    /// @return A container with option clones.
    OptionCollection cloneOptions();

    /// @brief Returns the first option of specified type.
    ///
    /// Returns the first option of specified type. Note that in DHCPv6 several
    /// instances of the same option are allowed (and frequently used).
    ///
    /// The options will be only returned after unpack() is called.
    ///
    /// @param type option type we are looking for
    ///
    /// @return pointer to found option (or NULL)
    OptionPtr getOption(const uint16_t type);

    /// @brief Returns all instances of specified type.
    ///
    /// Returns all instances of options of the specified type. DHCPv6 protocol
    /// allows (and uses frequently) multiple instances.
    ///
    /// @param type option type we are looking for
    /// @return instance of option collection with requested options
    isc::dhcp::OptionCollection getOptions(const uint16_t type);

    /// @brief Controls whether the option retrieved by the @ref Pkt::getOption
    /// should be copied before being returned.
    ///
    /// Setting this value to true enables the mechanism of copying options
    /// retrieved from the packet to prevent accidental modifications of
    /// options that shouldn't be modified. The typical use case for this
    /// mechanism is to prevent hook library from modifying instance of
    /// an option within the packet that would also affect the value for
    /// this option within the Kea configuration structures.
    ///
    /// Kea doesn't copy option instances which it stores in the packet.
    /// It merely copy pointers into the packets. Thus, any modification
    /// to an option would change the value of this option in the
    /// Kea configuration. To prevent this, option copying should be
    /// enabled prior to passing the pointer to a packet to a hook library.
    ///
    /// Not only does this method cause the server to copy
    /// an option, but the copied option also replaces the original
    /// option within the packet. The option can be then freely modified
    /// and the modifications will only affect the instance of this
    /// option within the packet but not within the server configuration.
    ///
    /// @param copy Indicates if the options should be copied when
    /// retrieved (if true), or not copied (if false).
    virtual void setCopyRetrievedOptions(const bool copy) {
        copy_retrieved_options_ = copy;
    }

    /// @brief Returns whether the copying of retrieved options is enabled.
    ///
    /// Also see @ref setCopyRetrievedOptions.
    ///
    /// @return true if retrieved options are copied.
    bool isCopyRetrievedOptions() const {
        return (copy_retrieved_options_);
    }

    /// @brief Update packet timestamp.
    ///
    /// Updates packet timestamp. This method is invoked
    /// by interface manager just before sending or
    /// just after receiving it.
    /// @throw isc::Unexpected if timestamp update failed
    void updateTimestamp();

    /// @brief Returns packet timestamp.
    ///
    /// Returns packet timestamp value updated when
    /// packet is received or send.
    ///
    /// @return packet timestamp.
    const boost::posix_time::ptime& getTimestamp() const {
        return timestamp_;
    }

    /// @brief Set socket receive timestamp.
    ///
    /// Sets the socket receive timestamp to an arbitrary value.
    void setTimestamp(boost::posix_time::ptime& timestamp) {<--- Parameter 'timestamp' can be declared as reference to const
        timestamp_ = timestamp;
    }

    /// @brief Adds an event to the end of the event stack.
    ///
    /// @param label string identifying the event
    /// @param timestamp time at which the event occurred. It is expected
    /// to be in UTC/microseconds.  Defaults to the current time.
    void addPktEvent(const std::string& label,
                     const boost::posix_time::ptime& timestamp = PktEvent::now());

    /// @brief Adds an event to the end of the event stack with the timestamp
    /// specified as a struct timeval.
    ///
    /// @param label string identifying the event
    /// @param timestamp time at which the event occurred. It is expected
    /// to be in UTC/microseconds.
    void addPktEvent(const std::string& label, const struct timeval& timestamp);

    /// @brief Updates (or adds) an event in the event stack.
    ///
    /// Updates the timestamp of the event described by label if it exists in
    /// the stack, otherwise it adds the event to the end of the stack.  This
    /// is intended to be used for testing.
    ///
    /// @param label string identifying the event
    /// @param timestamp time at which the event occurred. It is expected
    /// to be in UTC/microseconds.
    void setPktEvent(const std::string& label,
                     const boost::posix_time::ptime& timestamp = PktEvent::now());

    /// @brief Discards contents of the packet event stack.
    ///
    /// This is provided primarily for test purposes.
    void clearPktEvents();

    /// @brief Fetches the timestamp for a given event in the stack.
    ///
    /// @param label string identifying the event
    /// @return timestamp of the event (UTC/microseconds)
    boost::posix_time::ptime getPktEventTime(const std::string& label) const;

    /// @brief Fetches the current event stack contents.
    ///
    /// @return reference to the list of events.
    const std::list<PktEvent>& getPktEvents() {
        return (events_);
    }

    /// @brief Creates a dump of the stack contents to a string for logging.
    ///
    /// @param verbose when true the dump is more verbose, includes durations
    /// between events and spans multiple lines.  Defaults to false.
    std::string dumpPktEvents(bool verbose = false) const;

    /// @brief Copies content of input buffer to output buffer.
    ///
    /// This is mostly a diagnostic function. It is being used for sending
    /// received packet. Received packet is stored in data_, but
    /// transmitted data is stored in buffer_out_. If we want to send packet
    /// that we just received, a copy between those two buffers is necessary.
    void repack();

    /// @brief Sets remote IP address.
    ///
    /// @param remote specifies remote address
    void setRemoteAddr(const isc::asiolink::IOAddress& remote) {
        remote_addr_ = remote;
    }

    /// @brief Returns remote IP address.
    ///
    /// @return remote address
    const isc::asiolink::IOAddress& getRemoteAddr() const {
        return (remote_addr_);
    }

    /// @brief Sets local IP address.
    ///
    /// @param local specifies local address
    void setLocalAddr(const isc::asiolink::IOAddress& local) {
        local_addr_ = local;
    }

    /// @brief Returns local IP address.
    ///
    /// @return local address
    const isc::asiolink::IOAddress& getLocalAddr() const {
        return (local_addr_);
    }

    /// @brief Sets local UDP (and soon TCP) port.
    ///
    /// This sets a local port, i.e. destination port for recently received
    /// packet or a source port for to be transmitted packet.
    ///
    /// @param local specifies local port
    void setLocalPort(uint16_t local) {
        local_port_ = local;
    }

    /// @brief Returns local UDP (and soon TCP) port.
    ///
    /// This sets a local port, i.e. destination port for recently received
    /// packet or a source port for to be transmitted packet.
    ///
    /// @return local port
    uint16_t getLocalPort() const {
        return (local_port_);
    }

    /// @brief Sets remote UDP (and soon TCP) port.
    ///
    /// This sets a remote port, i.e. source port for recently received
    /// packet or a destination port for to be transmitted packet.
    ///
    /// @param remote specifies remote port
    void setRemotePort(uint16_t remote) {
        remote_port_ = remote;
    }

    /// @brief Returns remote port.
    ///
    /// @return remote port
    uint16_t getRemotePort() const {
        return (remote_port_);
    }

    /// @brief Sets interface index.
    ///
    /// @param ifindex specifies interface index.
    void setIndex(const unsigned int ifindex) {
        ifindex_ = ifindex;
    }

    /// @brief Resets interface index to negative value.
    void resetIndex() {
        ifindex_ = UNSET_IFINDEX;
    }

    /// @brief Returns interface index.
    ///
    /// @return interface index
    int getIndex() const {
        return (ifindex_);
    }

    /// @brief Checks if interface index has been set.
    ///
    /// @return true if interface index set, false otherwise.
    bool indexSet() const {
        return (ifindex_ != UNSET_IFINDEX);
    }

    /// @brief Returns interface name.
    ///
    /// Returns interface name over which packet was received or is
    /// going to be transmitted.
    ///
    /// @return interface name
    std::string getIface() const {<--- Function 'getIface()' should return member 'iface_' by const reference.
        return (iface_);
    }

    /// @brief Sets interface name.
    ///
    /// Sets interface name over which packet was received or is
    /// going to be transmitted.
    ///
    /// @param iface The interface name
    void setIface(const std::string& iface) {
        iface_ = iface;
    }

    /// @brief Sets remote hardware address.
    ///
    /// Sets hardware address (MAC) from an existing HWAddr structure.
    /// The remote address is a destination address for outgoing
    /// packet and source address for incoming packet. When this
    /// is an outgoing packet, this address will be used to
    /// construct the link layer header.
    ///
    /// @param hw_addr structure representing HW address.
    ///
    /// @throw BadValue if addr is null
    void setRemoteHWAddr(const HWAddrPtr& hw_addr);

    /// @brief Sets remote hardware address.
    ///
    /// Sets the destination hardware (MAC) address for the outgoing packet
    /// or source HW address for the incoming packet. When this
    /// is an outgoing packet this address will be used to construct
    /// the link layer header.
    ///
    /// @note mac_addr must be a buffer of at least hlen bytes.
    ///
    /// In a typical case, hlen field would be redundant, as it could
    /// be extracted from mac_addr.size(). However, the difference is
    /// when running on exotic hardware, like Infiniband, that had
    /// MAC addresses 20 bytes long. In that case, hlen is set to zero
    /// in DHCPv4.
    ///
    /// @param htype hardware type (will be sent in htype field)
    /// @param hlen hardware length (will be sent in hlen field)
    /// @param hw_addr pointer to hardware address
    void setRemoteHWAddr(const uint8_t htype, const uint8_t hlen,
                         const std::vector<uint8_t>& hw_addr);

    /// @brief Returns the remote HW address obtained from raw sockets.
    ///
    /// @return remote HW address.
    HWAddrPtr getRemoteHWAddr() const {
        return (remote_hwaddr_);
    }

    /// @brief Returns MAC address.
    ///
    /// The difference between this method and getRemoteHWAddr() is that
    /// getRemoteHWAddr() returns only what was obtained from raw sockets.
    /// This method is more generic and can attempt to obtain MAC from
    /// varied sources: raw sockets, client-id, link-local IPv6 address,
    /// and various relay options.
    ///
    /// @note Technically the proper term for this information is a link layer
    /// address, but it is frequently referred to MAC or hardware address.
    /// Since we're calling the feature "MAC addresses in DHCPv6", we decided
    /// to keep the name of getMAC().
    ///
    /// hw_addr_src takes a combination of bit values specified in
    /// HWADDR_SOURCE_* constants.
    ///
    /// @param hw_addr_src a bitmask that specifies hardware address source
    HWAddrPtr getMAC(uint32_t hw_addr_src);

    /// @brief Virtual destructor.
    ///
    /// There is nothing to clean up here, but since there are virtual methods,
    /// we define virtual destructor to ensure that derived classes will have
    /// a virtual one, too.
    virtual ~Pkt() {
    }

    /// @brief Classes this packet belongs to.
    ///
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
    /// iterate over existing classes. Having it public also solves the problem
    /// of returned reference lifetime. It is preferred to use @ref inClass and
    /// @ref addClass to operate on this field.
    ClientClasses classes_;

    /// @brief Classes to be evaluated during additional class evaluation
    ///
    /// This list allows hook libraries a way to add classes to the list of classes
    /// which will be evaluated during evaluate-additional-classes evaluation.
    ///
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
    /// iterate over additional classes. Having it public also solves the problem
    /// of returned reference lifetime. It is preferred to use @ref addAdditionalClass
    /// operate on this field.
    ///
    /// Before output option processing these classes will be evaluated
    /// and if evaluation status is true added to the classes_ collection.
    ClientClasses additional_classes_;

    /// @brief SubClasses this packet belongs to.
    ///
    /// This field is public, so the code outside of Pkt4 or Pkt6 class can
    /// iterate over existing classes. Having it public also solves the problem
    /// of returned reference lifetime. It is preferred to use @ref inClass and
    /// @ref addSubClass to operate on this field.
    SubClassRelationContainer subclasses_;

    /// @brief Collection of options present in this message.
    ///
    /// @warning This public member is accessed by derived
    /// classes directly. One of such derived classes is
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
    /// behavior must be taken into consideration before making
    /// changes to this member such as access scope restriction or
    /// data format change etc.
    isc::dhcp::OptionCollection options_;

protected:

    /// @brief Attempts to obtain MAC address from source link-local
    /// IPv6 address
    ///
    /// This method is called from getMAC(HWADDR_SOURCE_IPV6_LINK_LOCAL)
    /// and should not be called directly. It is not 100% reliable.
    /// The source IPv6 address does not necessarily have to be link-local
    /// (may be global or ULA) and even if it's link-local, it doesn't
    /// necessarily be based on EUI-64. For example, Windows supports
    /// RFC4941, which randomized IID part of the link-local address.
    /// If this method fails, it will return NULL.
    ///
    /// For direct message, it attempts to use remote_addr_ field. For relayed
    /// message, it uses peer-addr of the first relay.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is not applicable to DHCPv4.
    ///
    /// @return hardware address (or NULL)
    virtual HWAddrPtr getMACFromSrcLinkLocalAddr() = 0;

    /// @brief Attempts to obtain MAC address from relay option
    /// client-linklayer-addr
    ///
    /// This method is called from getMAC(HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)
    /// and should not be called directly. It will extract the client's
    /// MAC/Hardware address from option client_linklayer_addr (RFC6939)
    /// inserted by the relay agent closest to the client.
    /// If this method fails, it will return NULL.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is not applicable to DHCPv4.
    ///
    /// @return hardware address (or NULL)
    virtual HWAddrPtr getMACFromIPv6RelayOpt() = 0;

    /// @brief Attempts to obtain MAC address from DUID-LL or DUID-LLT.
    ///
    /// This method is called from getMAC(HWADDR_SOURCE_DUID) and should not be
    /// called directly. It will attempt to extract MAC address information
    /// from DUID if its type is LLT or LL. If this method fails, it will
    /// return NULL.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is not applicable to DHCPv4.
    ///
    /// @return hardware address (or NULL)
    virtual HWAddrPtr getMACFromDUID() = 0;

    /// @brief Attempts to obtain MAC address from remote-id relay option.
    ///
    /// This method is called from getMAC(HWADDR_SOURCE_REMOTE_ID) and should not be
    /// called directly. It will attempt to extract MAC address information
    /// from remote-id option inserted by a relay agent closest to the client.
    /// If this method fails, it will return NULL.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is not applicable to DHCPv4.
    ///
    /// @return hardware address (or NULL)
    virtual HWAddrPtr getMACFromRemoteIdRelayOption() = 0;

    /// @brief Attempts to convert IPv6 address into MAC.
    ///
    /// Utility method that attempts to convert link-local IPv6 address to the
    /// MAC address. That works only for link-local IPv6 addresses that are
    /// based on EUI-64.
    ///
    /// @note This method uses hardware type of the interface the packet was
    /// received on. If you have multiple access technologies in your network
    /// (e.g. client connected to WiFi that relayed the traffic to the server
    /// over Ethernet), hardware type may be invalid.
    ///
    /// @param addr IPv6 address to be converted
    /// @return hardware address (or NULL)
    HWAddrPtr
    getMACFromIPv6(const isc::asiolink::IOAddress& addr);

    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
    ///        inserted by the modem itself.
    ///
    /// This is a generic mechanism for extracting hardware address from the
    /// DOCSIS options.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is currently not implemented in DHCPv4.
    ///
    /// @return hardware address (if necessary DOCSIS suboptions are present)
    virtual HWAddrPtr getMACFromDocsisModem() = 0;

    /// @brief Attempts to extract MAC/Hardware address from DOCSIS options
    ///        inserted by the CMTS (the relay agent)
    ///
    /// This is a generic mechanism for extracting hardware address from the
    /// DOCSIS options.
    ///
    /// @note This is a pure virtual method and must be implemented in
    /// the derived classes. The @c Pkt6 class have respective implementation.
    /// This method is currently not implemented in DHCPv4.
    ///
    /// @return hardware address (if necessary DOCSIS suboptions are present)
    virtual HWAddrPtr getMACFromDocsisCMTS() = 0;

    /// Transaction-id (32 bits for v4, 24 bits for v6)
    uint32_t transid_;

    /// Name of the network interface the packet was received/to be sent over.
    std::string iface_;

    /// @brief Interface index.
    ///
    /// Each network interface has assigned an unique ifindex.
    /// It is a functional equivalent of a name, but sometimes more useful, e.g.
    /// when using odd systems that allow spaces in interface names.
    unsigned int ifindex_;

    /// @brief Local IP (v4 or v6) address.
    ///
    /// Specifies local IPv4 or IPv6 address. It is a destination address for
    /// received packet, and a source address if it packet is being transmitted.
    isc::asiolink::IOAddress local_addr_;

    /// @brief Remote IP address.
    ///
    /// Specifies local IPv4 or IPv6 address. It is source address for received
    /// packet and a destination address for packet being transmitted.
    isc::asiolink::IOAddress remote_addr_;

    /// local TDP or UDP port
    uint16_t local_port_;

    /// remote TCP or UDP port
    uint16_t remote_port_;

    /// Output buffer (used during message transmission)
    ///
    /// @warning This protected member is accessed by derived
    /// classes directly. One of such derived classes is
    /// @ref perfdhcp::PerfPkt6. The impact on derived classes'
    /// behavior must be taken into consideration before making
    /// changes to this member such as access scope restriction or
    /// data format change etc.
    isc::util::OutputBuffer buffer_out_;

    /// @brief Indicates if a copy of the retrieved option should be
    /// returned when @ref Pkt::getOption is called.
    ///
    /// @see the documentation for @ref Pkt::setCopyRetrievedOptions.
    bool copy_retrieved_options_;

    /// packet timestamp
    boost::posix_time::ptime timestamp_;

    // remote HW address (src if receiving packet, dst if sending packet)
    HWAddrPtr remote_hwaddr_;

private:

    /// @brief Generic method that validates and sets HW address.
    ///
    /// This is a generic method used by all modifiers of this class
    /// which set class members representing HW address.
    ///
    /// @param htype hardware type.
    /// @param hlen hardware length.
    /// @param hw_addr pointer to actual hardware address.
    /// @param [out] storage pointer to a class member to be modified.
    ///
    /// @throw isc::OutOfRange if invalid HW address specified.
    virtual void setHWAddrMember(const uint8_t htype, const uint8_t hlen,
                                 const std::vector<uint8_t>& hw_addr,
                                 HWAddrPtr& storage);

    /// @brief List of timestamped packet events.
    std::list<PktEvent> events_;
};

/// @brief A pointer to either Pkt4 or Pkt6 packet
typedef boost::shared_ptr<isc::dhcp::Pkt> PktPtr;

/// @brief RAII object enabling duplication of the stored options and restoring
/// the original options on destructor.
///
/// This object enables duplication of the stored options and restoring the
/// original options on destructor. When the object goes out of scope, the
/// initial options are restored. This is applicable in cases when the server is
/// going to invoke a callout (hook library) where the list of options in the
/// packet will be modified. This can also be used to restore the initial
/// suboptions of an option when the suboptions are changed (e.g. when splitting
/// long options and suboptions). The use of RAII object eliminates the need for
/// explicitly copying and restoring the list of options and is safer in case of
/// exceptions thrown by callouts and a presence of multiple exit points.
class ScopedSubOptionsCopy {
public:

    /// @brief Constructor.
    ///
    /// Creates a copy of the initial options on an option.
    ///
    /// @param opt Pointer to the option.
    ScopedSubOptionsCopy(const OptionPtr& opt) : option_(opt) {
        if (opt) {
            options_ = opt->getMutableOptions();
        }
    }

    /// @brief Destructor.
    ///
    /// Restores the initial options on a packet.
    ~ScopedSubOptionsCopy() {
        if (option_) {
            option_->getMutableOptions() = options_;
        }
    }

private:

    /// @brief Holds a pointer to the option.
    OptionPtr option_;

    /// @brief Holds the initial options.
    OptionCollection options_;
};

/// @brief RAII object enabling duplication of the stored options and restoring
/// the original options on destructor.
///
/// This object enables duplication of the stored options and restoring the
/// original options on destructor. When the object goes out of scope, the
/// initial options are restored. This is applicable in cases when the server is
/// going to invoke a callout (hook library) where the list of options in the
/// packet will be modified. The use of RAII object eliminates the need for
/// explicitly copying and restoring the list of options and is safer in case of
/// exceptions thrown by callouts and a presence of multiple exit points.
///
/// @tparam PktType Type of the packet, e.g. Pkt4, Pkt6, Pkt4o6.
template<typename PktType>
class ScopedPktOptionsCopy {
public:

    /// @brief Constructor.
    ///
    /// Creates a copy of the initial options on a packet.
    ///
    /// @param pkt Pointer to the packet.
    ScopedPktOptionsCopy(PktType& pkt) : pkt_(pkt), options_(pkt.options_) {
        pkt_.options_ = pkt_.cloneOptions();
    }

    /// @brief Destructor.
    ///
    /// Restores the initial options on a packet.
    ~ScopedPktOptionsCopy() {
        pkt_.options_ = options_;
    }

private:

    /// @brief Holds a reference to the packet.
    PktType& pkt_;

    /// @brief Holds the initial options.
    OptionCollection options_;
};

} // end of namespace isc::dhcp
} // end of namespace isc

#endif