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
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
// Copyright (C) 2011-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 DHCPV6_SRV_H
#define DHCPV6_SRV_H

#include <asiolink/io_service.h>
#include <dhcp/dhcp6.h>
#include <dhcp/duid.h>
#include <dhcp/option.h>
#include <dhcp/option_string.h>
#include <dhcp/option6_client_fqdn.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_definition.h>
#include <dhcp_ddns/ncr_msg.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/callout_handle_store.h>
#include <dhcpsrv/cb_ctl_dhcp6.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/network_state.h>
#include <dhcpsrv/subnet.h>
#include <hooks/callout_handle.h>
#include <process/daemon.h>

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

// Undefine the macro OPTIONAL which is defined in some operating
// systems but conflicts with a member of the RequirementLevel enum in
// the server class.

#ifdef OPTIONAL
#undef OPTIONAL
#endif

namespace isc {
namespace dhcp {

/// @brief This exception is thrown when DHCP server hits the error which should
/// result in discarding the message being processed.
class DHCPv6DiscardMessageError : public Exception {
public:
    DHCPv6DiscardMessageError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

/// @brief DHCPv6 server service.
///
/// This singleton class represents DHCPv6 server. It contains all
/// top-level methods and routines necessary for server operation.
/// In particular, it instantiates IfaceMgr, loads or generates DUID
/// that is going to be used as server-identifier, receives incoming
/// packets, processes them, manages leases assignment and generates
/// appropriate responses.
///
/// This class does not support any controlling mechanisms directly.
/// See the derived \ref ControlledDhcpv6Srv class for support for
/// command and configuration updates over msgq.
class Dhcpv6Srv : public process::Daemon {
private:

    /// @brief Pointer to IO service used by the server.
    asiolink::IOServicePtr io_service_;

public:
    /// @brief defines if certain option may, must or must not appear
    typedef enum {
        FORBIDDEN,
        MANDATORY,
        OPTIONAL
    } RequirementLevel;

    /// @brief Minimum length of a MAC address to be used in DUID generation.
    static const size_t MIN_MAC_LEN = 6;

    /// @brief Default constructor.
    ///
    /// Instantiates necessary services, required to run DHCPv6 server.
    /// In particular, creates IfaceMgr that will be responsible for
    /// network interaction. Will instantiate lease manager, and load
    /// old or create new DUID. It is possible to specify alternate
    /// port on which DHCPv6 server will listen on and alternate port
    /// where DHCPv6 server sends all responses to. Those are mostly useful
    /// for testing purposes.
    ///
    /// @param server_port specifies port number to listen on
    /// @param client_port specifies port number to send to
    Dhcpv6Srv(uint16_t server_port = DHCP6_SERVER_PORT,
              uint16_t client_port = 0);

    /// @brief Destructor. Used during DHCPv6 service shutdown.
    virtual ~Dhcpv6Srv();

    /// @brief Checks if the server is running in unit test mode.
    ///
    /// @return true if the server is running in unit test mode,
    /// false otherwise.
    bool inTestMode() const {
        return (server_port_ == 0);
    }

    /// @brief Returns pointer to the IO service used by the server.
    asiolink::IOServicePtr& getIOService() {
        return (io_service_);
    }

    /// @brief Returns pointer to the network state used by the server.
    NetworkStatePtr& getNetworkState() {
        return (network_state_);
    }

    /// @brief Returns an object which controls access to the configuration
    /// backends.
    ///
    /// @return Pointer to the instance of the object which controls
    /// access to the configuration backends.
    CBControlDHCPv6Ptr getCBControl() const {
        return (cb_control_);
    }

    /// @brief returns Kea version on stdout and exit.
    /// redeclaration/redefinition. @ref isc::process::Daemon::getVersion()
    static std::string getVersion(bool extended);<--- Derived function 'Dhcpv6Srv::getVersion'

    /// @brief Returns server-identifier option.
    ///
    /// @return server-id option
    OptionPtr getServerID() { return serverid_; }

    /// @brief Main server processing loop.
    ///
    /// Main server processing loop. Call the processing step routine
    /// until shut down.
    ///
    /// @return The value returned by @c Daemon::getExitValue().
    int run();

    /// @brief Main server processing step.
    ///
    /// Main server processing step. Receives one incoming packet, calls
    /// the processing packet routing and (if necessary) transmits
    /// a response.
    void runOne();

    /// @brief Process a single incoming DHCPv6 packet and sends the response.
    ///
    /// It verifies correctness of the passed packet, calls per-type processXXX
    /// methods, generates appropriate answer, sends the answer to the client.
    ///
    /// @param query A pointer to the packet to be processed.
    void processPacketAndSendResponse(Pkt6Ptr query);

    /// @brief Process a single incoming DHCPv6 packet and sends the response.
    ///
    /// It verifies correctness of the passed packet, calls per-type processXXX
    /// methods, generates appropriate answer, sends the answer to the client.
    ///
    /// @param query A pointer to the packet to be processed.
    void processPacketAndSendResponseNoThrow(Pkt6Ptr query);

    /// @brief Process an unparked DHCPv6 packet and sends the response.
    ///
    /// @param callout_handle pointer to the callout handle.
    /// @param query A pointer to the packet to be processed.
    /// @param rsp A pointer to the response.
    /// @param subnet A pointer to the selected subnet.
    void sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
                             Pkt6Ptr query, Pkt6Ptr& rsp, Subnet6Ptr& subnet);

    /// @brief Process a single incoming DHCPv6 packet.
    ///
    /// It verifies correctness of the passed packet, localizes it,
    /// calls per-type processXXX methods, generates appropriate answer.
    ///
    /// @param query A pointer to the packet to be processed.
    /// @return A pointer to the response.
    Pkt6Ptr processPacket(Pkt6Ptr query);

    /// @brief Process a single incoming DHCPv6 query.
    ///
    /// It localizes the query, calls per-type processXXX methods,
    /// generates appropriate answer.
    ///
    /// @param query A pointer to the packet to be processed.
    /// @return A pointer to the response.
    Pkt6Ptr processDhcp6Query(Pkt6Ptr query);

    /// @brief Process a single incoming DHCPv6 query.
    ///
    /// It localizes the query, calls per-type processXXX methods,
    /// generates appropriate answer, sends the answer to the client.
    ///
    /// @param query A pointer to the packet to be processed.
    void processDhcp6QueryAndSendResponse(Pkt6Ptr query);

    /// @brief Process a localized incoming DHCPv6 query.
    ///
    /// It calls per-type processXXX methods, generates appropriate answer.
    ///
    /// @param ctx Pointer to The client context.
    /// @return A pointer to the response.
    Pkt6Ptr processLocalizedQuery6(AllocEngine::ClientContext6& ctx);

    /// @brief Process a localized incoming DHCPv6 query.
    ///
    /// It calls per-type processXXX methods, generates appropriate answer,
    /// sends the answer to the client.
    ///
    /// @param query A pointer to the packet to be processed.
    /// @param ctx Pointer to The client context.
    void processLocalizedQuery6AndSendResponse(Pkt6Ptr query,
                                               AllocEngine::ClientContext6& ctx);

    /// @brief Process a localized incoming DHCPv6 query.
    ///
    /// A variant of the precedent method used to resume processing
    /// for packets parked in the subnet6_select callout.
    ///
    /// @param query A pointer to the unparked packet.
    void processLocalizedQuery6AndSendResponse(Pkt6Ptr query);

    /// @brief Instructs the server to shut down.
    void shutdown() override;

    ///
    /// @name Public accessors returning values required to (re)open sockets.
    ///
    //@{
    ///
    /// @brief Get UDP port on which server should listen.
    ///
    /// Typically, server listens on UDP port number 547. Other ports are used
    /// for testing purposes only.
    ///
    /// @return UDP port on which server should listen.
    uint16_t getServerPort() const {
        return (server_port_);
    }
    //@}

    /// @brief Starts DHCP_DDNS client IO if DDNS updates are enabled.
    ///
    /// If updates are enabled, it instructs the D2ClientMgr singleton to
    /// enter send mode.  If D2ClientMgr encounters errors it may throw
    /// D2ClientError. This method does not catch exceptions.
    void startD2();

    /// @brief Stops DHCP_DDNS client IO if DDNS updates are enabled.
    ///
    /// If updates are enabled, it instructs the D2ClientMgr singleton to
    /// leave send mode.  If D2ClientMgr encounters errors it may throw
    /// D2ClientError. This method does not catch exceptions.
    void stopD2();

    /// @brief Implements the error handler for DHCP_DDNS IO errors
    ///
    /// Invoked when a NameChangeRequest send to kea-dhcp-ddns completes with
    /// a failed status.  These are communications errors, not data related
    /// failures.
    ///
    /// This method logs the failure and then suspends all further updates.
    /// Updating can only be restored by reconfiguration or restarting the
    /// server.  There is currently no retry logic so the first IO error that
    /// occurs will suspend updates.
    /// @todo We may wish to make this more robust or sophisticated.
    ///
    /// @param result Result code of the send operation.
    /// @param ncr NameChangeRequest which failed to send.
    virtual void d2ClientErrorHandler(const dhcp_ddns::
                                      NameChangeSender::Result result,
                                      dhcp_ddns::NameChangeRequestPtr& ncr);

    /// @brief Discards parked packets
    /// Clears the packet parking lots of all packets.
    /// Called during reconfigure and shutdown.
    void discardPackets();

    /// @brief Initialize client context (first part).
    ///
    /// @param query The query message.
    /// @param ctx Reference to client context.
    void initContext0(const Pkt6Ptr& query,
                      AllocEngine::ClientContext6& ctx);

    /// @brief Initialize client context and perform early global
    /// reservations lookup.
    ///
    /// @param query The query message.
    /// @param ctx Reference to client context.
    /// @return true if processing can continue, false if the query must be
    /// dropped.
    bool earlyGHRLookup(const Pkt6Ptr& query,
                        AllocEngine::ClientContext6& ctx);

    /// @brief Set host identifiers within a context.
    ///
    /// This method sets an ordered list of host identifier types and
    /// values which the server should use to find host reservations.
    /// The order of the set is determined by the configuration parameter,
    /// host-reservation-identifiers
    ///
    /// @param ctx reference to the context.
    static void setHostIdentifiers(AllocEngine::ClientContext6& ctx);

protected:

    /// @brief This function sets statistics related to DHCPv6 packets processing
    /// to their initial values.
    ///
    /// All of the statistics observed by the DHCPv6 server and with the names
    /// like "pkt6-" are reset to 0. This function must be invoked in the class
    /// constructor.
    void setPacketStatisticsDefaults();

    /// @brief Compare received server id with our server id
    ///
    /// Checks if the server id carried in a query from a client matches
    /// server identifier being used by the server.
    ///
    /// @param pkt DHCPv6 packet carrying server identifier to be checked.
    /// @return true if server id carried in the query matches server id
    /// used by the server; false otherwise.
    bool testServerID(const Pkt6Ptr& pkt);

    /// @brief Check if the message can be sent to unicast.
    ///
    /// This function checks if the received message conforms to the section 16
    /// of RFC 8415 which says that: "A server MUST discard any Solicit, Confirm,
    /// Rebind or Information-request messages it receives with a Layer 3 unicast
    /// destination address.
    ///
    /// @param pkt DHCPv6 message to be checked.
    /// @return false if the message has been sent to unicast address but it is
    /// not allowed according to RFC3315, section 15; true otherwise.
    bool testUnicast(const Pkt6Ptr& pkt) const;

    /// @brief Verifies if specified packet meets RFC requirements
    ///
    /// Checks if mandatory option is really there, that forbidden option
    /// is not there, and that client-id or server-id appears only once.
    ///
    /// @param pkt packet to be checked
    /// @return false if the message should be dropped as a result of the
    /// sanity check.
    bool sanityCheck(const Pkt6Ptr& pkt);

    /// @brief verifies if specified packet meets RFC requirements
    ///
    /// Checks if mandatory option is really there, that forbidden option
    /// is not there, and that client-id or server-id appears only once.
    ///
    /// @param pkt packet to be checked
    /// @param clientid expectation regarding client-id option
    /// @param serverid expectation regarding server-id option
    /// @throw RFCViolation if any issues are detected
    void sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
                     RequirementLevel serverid);

    /// @brief verifies if received DUID option (client-id or server-id) is sane
    ///
    /// @param opt option to be checked
    /// @param opt_name text name to be printed
    /// @throw RFCViolation if any issues are detected
    void sanityCheckDUID(const OptionPtr& opt, const std::string& opt_name);

    /// @brief Processes incoming Solicit and returns response.
    ///
    /// Processes received Solicit message and verifies that its sender
    /// should be served. In particular IA, TA and PD options are populated
    /// with to-be assigned addresses, temporary addresses and delegated
    /// prefixes, respectively. In the usual 4 message exchange, server is
    /// expected to respond with Advertise message. However, if client
    /// requests rapid-commit and server supports it, Reply will be sent
    /// instead of Advertise and requested leases will be assigned
    /// immediately.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Advertise, Reply message or NULL.
    Pkt6Ptr processSolicit(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming Request and returns Reply response.
    ///
    /// Processes incoming Request message and verifies that its sender
    /// should be served. In particular IA, TA and PD options are populated
    /// with assigned addresses, temporary addresses and delegated
    /// prefixes, respectively. Uses LeaseMgr to allocate or update existing
    /// leases.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return REPLY message or NULL
    Pkt6Ptr processRequest(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming Renew message.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message to be sent to the client.
    Pkt6Ptr processRenew(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming Rebind message.
    ///
    /// @todo There are cases when the Rebind message should be  discarded
    /// by the DHCP server. One of those is when the server doesn't have a
    /// record of the client and it is unable to determine whether the
    /// client is on the appropriate link or not. We don't seem to do it
    /// now.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message to be sent to the client.
    Pkt6Ptr processRebind(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming Confirm message and returns Reply.
    ///
    /// This function processes Confirm message from the client according
    /// to section 18.3.3. of RFC 8415. It discards the Confirm message if
    /// the message sent by the client contains no addresses, i.e. it has
    /// no IA_NA options or all IA_NA options contain no IAAddr options.
    ///
    /// If the Confirm message contains addresses this function will perform
    /// the following checks:
    /// - check if there is appropriate subnet configured for the client
    /// (e.g. subnet from which addresses are assigned for requests
    /// received on the particular interface).
    /// - check if all addresses sent in the Confirm message belong to the
    /// selected subnet.
    ///
    /// If any of the checks above fails, the Reply message with the status
    /// code NotOnLink is returned. Otherwise, the Reply message with the
    /// status code Success is returned.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message from the server or NULL pointer if Confirm
    /// message should be discarded by the server.
    Pkt6Ptr processConfirm(AllocEngine::ClientContext6& ctx);

    /// @brief Process incoming Release message.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message to be sent to the client.
    Pkt6Ptr processRelease(AllocEngine::ClientContext6& ctx);

    /// @brief Process incoming Decline message.
    ///
    /// This method processes Decline message. It conducts standard sanity
    /// checks, creates empty reply and copies the necessary options from
    /// the client's message. Finally, it calls @ref declineLeases, where
    /// the actual address processing takes place.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message to be sent to the client.
    Pkt6Ptr processDecline(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming Information-request message.
    ///
    /// @param ctx Reference to client context
    ///
    /// @return Reply message to be sent to the client.
    Pkt6Ptr processInfRequest(AllocEngine::ClientContext6& ctx);

    /// @brief Processes incoming DHCPv4-query message.
    ///
    /// It always returns NULL, as there is nothing to be sent back to the
    /// client at this time. The message was sent to DHCPv4 server using
    /// @ref isc::dhcp::Dhcp6to4Ipc::handler()). We will send back a response
    /// to the client once we get back DHCP4-REPLY from the DHCPv4 server.
    ///
    /// @param dhcp4_query message received from client
    /// Does not throw
    void processDhcp4Query(const Pkt6Ptr& dhcp4_query);

    /// @brief Selects a subnet for a given client's packet.
    ///
    /// @param question client's message
    /// @param drop if it is true the packet will be dropped
    /// @return selected subnet (or NULL if no suitable subnet was found)
    isc::dhcp::Subnet6Ptr selectSubnet(const Pkt6Ptr& question, bool& drop);

    /// @brief Processes IA_NA option (and assigns addresses if necessary).
    ///
    /// Generates response to IA_NA. This typically includes selecting (and
    /// allocating a lease in case of REQUEST) an address lease and creating
    /// IAADDR option. In case of allocation failure, it may contain
    /// status code option with non-zero status, denoting cause of the
    /// allocation failure.
    ///
    /// @param query client's message (typically SOLICIT or REQUEST)
    /// to the client (if the client sent this option to the server).
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param ia pointer to client's IA_NA option (client's request)
    ///
    /// @return IA_NA option (server's response)
    OptionPtr assignIA_NA(const isc::dhcp::Pkt6Ptr& query,
                          AllocEngine::ClientContext6& ctx,
                          Option6IAPtr ia);

    /// @brief Processes IA_PD option (and assigns prefixes if necessary).
    ///
    /// Generates response to IA_PD. This typically includes selecting (and
    /// allocating in the case of REQUEST) a prefix lease and creating an
    /// IAPREFIX option. In case of an allocation failure, it may contain a
    /// status code option with non-zero status denoting the cause of the
    /// allocation failure.
    ///
    /// @param query client's message (typically SOLICIT or REQUEST)
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param ia pointer to client's IA_PD option (client's request)
    /// @return IA_PD option (server's response)
    OptionPtr assignIA_PD(const Pkt6Ptr& query,
                          AllocEngine::ClientContext6& ctx,
                          boost::shared_ptr<Option6IA> ia);

    /// @brief Extends lifetime of the specific IA_NA option.
    ///
    /// Generates response to IA_NA in Renew or Rebind. This typically includes
    /// finding a lease that corresponds to the received address. If no such
    /// lease is found, an IA_NA response is generated with an appropriate
    /// status code.
    ///
    /// @param query client's message (Renew or Rebind)
    /// to the client (if the client sent this option to the server).
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param ia IA_NA option which carries address for which lease lifetime
    /// will be extended.
    /// @return IA_NA option (server's response)
    OptionPtr extendIA_NA(const Pkt6Ptr& query,
                          AllocEngine::ClientContext6& ctx,
                          Option6IAPtr ia);

    /// @brief Extends lifetime of the prefix.
    ///
    /// This function is called by the logic which processes Renew and Rebind
    /// messages to extend the lifetime of the existing prefix.
    ///
    /// The behavior of this function is different than @c extendIA_NA in that
    /// when there is no subnet found for the rebinding case, the Rebind message
    /// is discarded by the server. That behavior is based on the following
    /// statement from the RFC 8415, section 18.3.5:
    ///
    /// "If the server chooses to not include any IAs containing IA Address or
    /// IA Prefix options with lifetimes of 0 and the server does not include
    /// any other IAs with leases and/or status codes, the server does not send
    /// a Reply message.  In this situation, the server discards the Rebind
    /// message".
    ///
    /// @todo We should consider unification of the server behavior for address
    /// assignment and prefix delegation with respect to Rebind message
    /// processing. The RFC 8415, section 18.3.5 doesn't really differentiate
    /// between IA_NA and IA_PD in how they should be processed by the server.
    /// The intention of the spec is as follows:
    ///
    /// - If the server finds a lease but addresses and/or prefixes are not
    ///   appropriate anymore, it sends them with zero lifetimes.
    /// - If the server doesn't find a lease the server checks if the addresses
    ///   and/or prefixes the client sends are appropriate and sends them back
    ///   with zero lifetimes if they aren't.
    /// - The server may choose to not respond at all, if it cannot determine
    ///   whether the addresses and/or prefixes are appropriate and it doesn't
    ///   allocate any other addresses and/or prefixes.
    /// - If the server cannot find the leases included in the Rebind, the
    ///   server may either allocate the leases or simply return NoBinding.
    ///
    /// The @c extendIA_PD function drops the Rebind message if it cannot find
    /// the client entry (as a result of not finding a subnet for the client),
    /// the @c extendIA_NA function sends NoBinding status code in that case.
    /// Perhaps we should introduce an "Authoritative" configuration flag which,
    /// if enabled, would cause the server to always respond, either indicating
    /// that the address/prefix is inappropriate (with zero lifetimes) or that
    /// there is no binding (NoBinding status code) for both addresses and
    /// prefixes. When the "Authoritative" flag is disabled the server would
    /// drop the Rebind for which there is neither subnet selected nor client
    /// entry found (as it could be handled by another DHCP server). If nothing
    /// else we could consider unifying the behavior of @c extendIA_NA and
    /// @c extendIA_PD with respect to Rebind processing.
    ///
    /// @param query client's message
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param ia IA_PD option that is being renewed
    /// @return IA_PD option (server's response)
    /// @throw DHCPv6DiscardMessageError when the message being processed should
    /// be discarded by the server, i.e. there is no binding for the client doing
    /// Rebind.
    OptionPtr extendIA_PD(const Pkt6Ptr& query,
                          AllocEngine::ClientContext6& ctx,
                          Option6IAPtr ia);

    /// @brief Releases specific IA_NA option
    ///
    /// Generates response to IA_NA in Release message. This covers finding and
    /// removal of a lease that corresponds to the received address. If no such
    /// lease is found, an IA_NA response is generated with an appropriate
    /// status code.
    ///
    /// The server sends top-level Status Code option. This method may update the
    /// passed value of that option, i.e. general_status. It is set to SUCCESS when
    /// message processing begins, but may be updated to some error code if the
    /// release process fails.
    ///
    /// @param duid client's duid
    /// @param query client's message
    /// @param general_status a global status (it may be updated in case of errors)
    /// @param ia IA_NA option that is being released
    /// @param old_lease a pointer to the lease being released
    /// @return IA_NA option (server's response)
    OptionPtr releaseIA_NA(const DuidPtr& duid, const Pkt6Ptr& query,
                           int& general_status,
                           boost::shared_ptr<Option6IA> ia,
                           Lease6Ptr& old_lease);

    /// @brief Releases specific IA_PD option
    ///
    /// Generates response to IA_PD in Release message. This covers finding and
    /// removal of a lease that corresponds to the received prefix(es). If no such
    /// lease is found, an IA_PD response is generated with an appropriate
    /// status code.
    ///
    /// @param duid client's duid
    /// @param query client's message
    /// @param general_status a global status (it may be updated in case of errors)
    /// @param ia IA_PD option that is being released
    /// @param old_lease a pointer to the lease being released
    /// @return IA_PD option (server's response)
    OptionPtr releaseIA_PD(const DuidPtr& duid, const Pkt6Ptr& query,
                           int& general_status,
                           boost::shared_ptr<Option6IA> ia,
                           Lease6Ptr& old_lease);

    /// @brief Copies required options from client message to server answer.
    ///
    /// Copies options that must appear in any server response (ADVERTISE, REPLY)
    /// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
    /// One notable example is client-id. Other options may be copied as required.
    /// Relay information details are also copied here.
    ///
    /// @param question client's message (options will be copied from here)
    /// @param answer server's message (options will be copied here)
    void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);

    /// @brief Build the configured option list
    ///
    /// @note The configured option list is an *ordered* list of
    /// @c CfgOption objects used to append options to the response.
    ///
    /// @param question client's message
    /// @param ctx client context (for the subnet)
    /// @param co_list configured option list to build
    void buildCfgOptionList(const Pkt6Ptr& question,
                            AllocEngine::ClientContext6& ctx,
                            CfgOptionList& co_list);

    /// @brief Appends default options to server's answer.
    ///
    /// Adds required options to server's answer. In particular, server-id
    /// is added. Possibly other mandatory options will be added, depending
    /// on type (or content) of client message.
    ///
    /// @param question client's message
    /// @param answer server's message (options will be added here)
    /// @param co_list configured option list (currently unused)
    void appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
                              const CfgOptionList& co_list);

    /// @brief Appends requested options to server's answer.
    ///
    /// Appends options requested by client to the server's answer.
    ///
    /// @param question client's message
    /// @param answer server's message (options will be added here)
    ///
    /// @param co_list configured option list
    void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
                                const CfgOptionList& co_list);

    /// @brief Appends requested vendor options to server's answer.
    ///
    /// This is mostly useful for Cable Labs options for now, but the method
    /// is easily extensible to other vendors.
    ///
    /// @param question client's message
    /// @param answer server's message (vendor options will be added here)
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param co_list configured option list
    void appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
                                      AllocEngine::ClientContext6& ctx,
                                      const CfgOptionList& co_list);

    /// @brief Assigns leases.
    ///
    /// It supports non-temporary addresses (IA_NA) and prefixes (IA_PD). It
    /// does NOT support temporary addresses (IA_TA).
    ///
    /// @param question client's message (with requested IA options)
    /// @param answer server's message (IA options will be added here).
    ///   This message should contain Client FQDN option being sent by the server
    ///   to the client (if the client sent this option to the server).
    /// @param ctx client context (contains subnet, duid and other parameters)
    void assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
                      AllocEngine::ClientContext6& ctx);

    /// @brief Processes Client FQDN Option.
    ///
    /// This function retrieves DHCPv6 Client FQDN %Option (if any) from the
    /// packet sent by a client and takes necessary actions upon this option.
    /// Received option comprises flags field which controls what DNS updates
    /// server should do. Server may override client's preference based on
    /// the current configuration. Server indicates that it has overridden
    /// the preference by storing DHCPv6 Client FQDN option with the
    /// appropriate flags in the response to a client. This option is also
    /// used to communicate the client's domain-name which should be sent
    /// to the DNS in the update. Again, server may act upon the received
    /// domain-name, i.e. if the provided domain-name is partial it should
    /// generate the fully qualified domain-name.
    ///
    /// This function takes into account the host reservation if one is matched
    /// to this client when forming the FQDN to be used with DNS as well as the
    /// lease name to be stored with the lease. In the following the term
    /// "reserved hostname" means a host reservation which includes a
    /// non-blank hostname.
    ///
    /// - If there is no Client FQDN and no reserved hostname then there
    /// will no be DNS updates and the lease hostname will be empty.
    ///
    /// - If there is no Client FQDN but there is reserved hostname then
    /// there will be no DNS updates and the lease hostname will be equal
    /// to reserved hostname.
    ///
    /// - If there is a Client FQDN and a reserved hostname, then both the
    /// FQDN and lease hostname will be equal to reserved hostname with
    /// the qualifying suffix appended.
    ///
    /// - If there is a Client FQDN but no reserved hostname then both the
    /// FQDN and lease hostname will be equal to the name provided in the
    /// client FQDN adjusted according the DhcpDdns configuration
    /// parameters (e.g.replace-client-name, qualifying suffix...).
    ///
    /// All the logic required to form appropriate answer to the client is
    /// held in this function.
    ///
    /// @param question Client's message.
    /// @param answer Server's response to a client. If server generated
    /// Client FQDN option for the client, this option is stored in this
    /// object.
    /// @param ctx client context (includes subnet, client-id, hw-addr etc.)
    void processClientFqdn(const Pkt6Ptr& question, const Pkt6Ptr& answer,
                           AllocEngine::ClientContext6& ctx);

    /// @brief Creates a number of @c isc::dhcp_ddns::NameChangeRequest objects
    /// based on the DHCPv6 Client FQDN %Option.
    ///
    /// The @c isc::dhcp_ddns::NameChangeRequest class encapsulates the request
    /// from the DHCPv6 server to the DHCP-DDNS module to perform DNS Update.
    /// The FQDN option carries response to the client about DNS updates that
    /// server intends to perform for the DNS client. Based on this, the
    /// function will create zero or more @c isc::dhcp_ddns::NameChangeRequest
    /// objects and store them in the internal queue.  To catch lease renewals
    /// that alter the FQDN, the function first looks at the context's changed
    /// list of leases (if any) to determine if DNS entries need to be removed.
    /// It then looks at the valid leases to determine if any DNS entries need
    /// to be added. If DNS updates are disabled, this method returns immediately.
    ///
    /// @todo Add support for multiple IAADDR options in the IA_NA.
    ///
    /// @param answer A message begins sent to the Client. If it holds the
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// Client FQDN option, this option is used to create NameChangeRequests.
    void createNameChangeRequests(const Pkt6Ptr& answer,
                                  AllocEngine::ClientContext6& ctx);

    /// @brief Attempts to extend the lifetime of IAs.
    ///
    /// This function is called when a client sends Renew or Rebind message.
    /// It iterates through received IA options and attempts to extend
    /// corresponding lease lifetimes. Internally, it calls
    /// @c Dhcpv6Srv::extendIA_NA and @c Dhcpv6Srv::extendIA_PD to extend
    /// the lifetime of IA_NA and IA_PD leases accordingly.
    ///
    /// @param query client's Renew or Rebind message
    /// @param reply server's response
    /// @param ctx client context (contains subnet, duid and other parameters)
    void extendLeases(const Pkt6Ptr& query, Pkt6Ptr& reply,
                      AllocEngine::ClientContext6& ctx);

    /// @brief Sets the T1 and T2 timers in the outbound IA
    ///
    /// This method determines the values for both the T1 and T2
    /// timers for the given IA. It is influenced by the
    /// lease's subnet's values for renew-timer, rebind-timer,
    /// calculate-tee-times, t1-percent, and t2-percent as follows:
    ///
    /// T2:
    ///
    /// The value for T2 defaults to zero. If the rebind-timer value is
    /// specified then use it.  If not and calculate-tee-times is true, then
    /// use the value given by: preferred lease time * t2-percent.
    ///
    /// T1:
    ///
    /// The candidate value for T1 defaults to zero. If the renew-timer value
    /// is specified then use it. If not and calculate-tee-times is true, then
    /// use the value given by: preferred lease time * t1-percent.
    ///
    /// The T1 candidate will be used provided it less than to T2,
    /// otherwise it will be set T1 to zero.
    ///
    /// @param preferred_lft preferred lease time of the lease being assigned to the client
    /// @param subnet the subnet to which the lease belongs
    /// @param resp outbound IA option in which the timers are set.
    void setTeeTimes(uint32_t preferred_lft, const Subnet6Ptr& subnet, Option6IAPtr& resp);

    /// @brief Attempts to release received addresses
    ///
    /// It iterates through received IA_NA options and attempts to release
    /// received addresses. If no such leases are found, or the lease fails
    /// proper checks (e.g. belongs to someone else), a proper status
    /// code is added to reply message. Released addresses are not added
    /// to REPLY packet, just its IA_NA containers.
    /// @param release client's message asking to release
    /// @param reply server's response
    /// @param ctx client context (includes subnet, client-id, hw-addr etc.)
    void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply,
                       AllocEngine::ClientContext6& ctx);

    /// @brief converts DUID to text
    /// Converts content of DUID option to a text representation, e.g.
    /// 01:ff:02:03:06:80:90:ab:cd:ef
    ///
    /// @param opt option that contains DUID
    /// @return string representation
    static std::string duidToString(const OptionPtr& opt);

    /// @brief dummy wrapper around IfaceMgr::receive6
    ///
    /// This method is useful for testing purposes, where its replacement
    /// simulates reception of a packet. For that purpose it is protected.
    virtual Pkt6Ptr receivePacket(int timeout);

    /// @brief dummy wrapper around IfaceMgr::send()
    ///
    /// This method is useful for testing purposes, where its replacement
    /// simulates transmission of a packet. For that purpose it is protected.
    virtual void sendPacket(const Pkt6Ptr& pkt);

    /// @brief Assigns incoming packet to zero or more classes.
    ///
    /// @note This is done in two phases: first the content of the
    /// vendor-class-identifier options are used as classes, by
    /// calling (private) classifyByVendor. Second, the classification match
    /// expressions are evaluated. The resulting classes will be stored
    /// in the packet (see @ref isc::dhcp::Pkt6::classes_ and
    /// @ref isc::dhcp::Pkt6::inClass).
    ///
    /// @param pkt packet to be classified
    void classifyPacket(const Pkt6Ptr& pkt);

    /// @brief Evaluate classes.
    ///
    /// @note Second part of the classification.
    ///
    /// Evaluate expressions of client classes: if it returns true the class
    /// is added to the incoming packet.
    ///
    /// @param pkt packet to be classified.
    /// @param depend_on_known if false classes depending on the KNOWN or
    /// UNKNOWN classes are skipped, if true only these classes are evaluated.
    void evaluateClasses(const Pkt6Ptr& pkt, bool depend_on_known);

    /// @brief Removed evaluated client classes.
    ///
    /// @todo: keep the list of dependent evaluated classes so
    /// remove only them.
    ///
    /// @param pkt the packet.
    static void removeDependentEvaluatedClasses(const Pkt6Ptr& pkt);

    /// @brief Assigns classes retrieved from host reservation database.
    ///
    /// @param pkt Pointer to the packet to which classes will be assigned.
    /// @param ctx Reference to the client context.
    void setReservedClientClasses(const Pkt6Ptr& pkt,
                                  const AllocEngine::ClientContext6& ctx);

    /// @brief Assigns classes retrieved from host reservation database
    /// if they haven't been yet set.
    ///
    /// This function sets reserved client classes in case they haven't
    /// been set after fetching host reservations from the database.
    /// This is the case when the client has non-global host reservation
    /// and the selected subnet belongs to a shared network.
    ///
    /// @param pkt Pointer to the packet to which classes will be assigned.
    /// @param ctx Reference to the client context.
    void conditionallySetReservedClientClasses(const Pkt6Ptr& pkt,
                                               const AllocEngine::ClientContext6& ctx);

    /// @brief Assigns incoming packet to zero or more classes (required pass).
    ///
    /// @note This required classification evaluates all classes which
    /// were marked for required evaluation. Classes are collected so
    /// evaluated in the reversed order than output option processing.
    ///
    /// @note The only-if-required flag is related because it avoids
    /// double evaluation (which is not forbidden).
    ///
    /// @param pkt packet to be classified
    /// @param ctx allocation context where to get information
    void requiredClassify(const Pkt6Ptr& pkt, AllocEngine::ClientContext6& ctx);

    /// @brief Attempts to get a MAC/hardware address using configured sources
    ///
    /// Tries to extract MAC/hardware address information from the packet
    /// using MAC sources configured in 'mac-sources' configuration parameter.
    ///
    /// @param pkt will try to exact MAC address from this packet
    /// @return HWaddr pointer (or NULL if configured methods fail)
    static HWAddrPtr getMAC(const Pkt6Ptr& pkt);

    /// @brief Processes Relay-supplied options, if present
    ///
    /// This method implements RFC6422. It checks if there are any RSOO options
    /// inserted by the relay agents in the query message. If there are, they
    /// are copied over to the response if they meet the following criteria:
    /// - the option is marked as RSOO-enabled (see relay-supplied-options
    ///   configuration parameter)
    /// - there is no such option provided by the server)
    void processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp);

    /// @brief Initializes client context for specified packet
    ///
    /// This method:
    /// - Extracts the duid from the packet and saves it to the context
    /// - Extracts the hardware address from the packet and saves it to
    /// the context
    /// - Performs host reservation lookup and stores the result in the
    /// context
    ///
    /// Even though the incoming packet type is known to this method, it
    /// doesn't set the @c fake_allocation flag, because of a possibility
    /// that the Rapid Commit option is in use. The @c fake_allocation
    /// flag is set appropriately after it has been determined whether
    /// the Rapid Commit option was included and that the server respects
    /// it.
    ///
    /// @param [out] ctx reference to context object to be initialized.
    /// @param [out] drop if it is true the packet will be dropped.
    void initContext(AllocEngine::ClientContext6& ctx, bool& drop);

    /// @brief this is a prefix added to the content of vendor-class option
    ///
    /// If incoming packet has a vendor class option, its content is
    /// prepended with this prefix and then interpreted as a class.
    /// For example, a packet that sends vendor class with value of "FOO"
    /// will cause the packet to be assigned to class VENDOR_CLASS_FOO.
    static const std::string VENDOR_CLASS_PREFIX;

    /// @brief Attempts to decline all leases in specified Decline message.
    ///
    /// This method iterates over all IA_NA options and calls @ref declineIA on
    /// each of them.
    ///
    /// @param decline Decline message sent by a client
    /// @param reply Server's response (IA_NA with status will be added here)
    /// @param ctx context
    /// @return true when expected to continue, false when hooks told us to drop
    ///         the packet
    bool declineLeases(const Pkt6Ptr& decline, Pkt6Ptr& reply,
                       AllocEngine::ClientContext6& ctx);

    /// @brief Declines leases in a single IA_NA option
    ///
    /// This method iterates over all addresses in this IA_NA, verifies
    /// whether they belong to the client and calls @ref declineLease. If there's
    /// an error, general_status (a status put in the top level scope), will be
    /// updated.
    ///
    /// @param decline client's Decline message
    /// @param duid client's duid (used to verify if the client owns the lease)
    /// @param general_status [out] status in top-level message (may be updated)
    /// @param ia specific IA_NA option to process.
    /// @param new_leases a collection of leases being declined.
    /// @return IA_NA option with response (to be included in Reply message)
    OptionPtr
    declineIA(const Pkt6Ptr& decline, const DuidPtr& duid, int& general_status,
              boost::shared_ptr<Option6IA> ia, Lease6Collection& new_leases);

    /// @brief Declines specific IPv6 lease.
    ///
    /// This method performs the actual decline and all necessary operations:
    /// - cleans up DNS, if necessary
    /// - updates subnet[X].declined-addresses (per subnet stat)
    /// - updates declined-addresses (global stat)
    /// - disassociates client information from the lease
    /// - moves the lease to DECLINED state
    /// - sets lease expiration time to decline-probation-period
    /// - adds status-code success
    ///
    /// @param decline used for generating removal Name Change Request.
    /// @param lease lease to be declined
    /// @param ia_rsp response IA_NA.
    /// @return true when expected to continue, false when hooks told us to drop
    ///         the packet
    bool declineLease(const Pkt6Ptr& decline, const Lease6Ptr lease,
                      boost::shared_ptr<Option6IA> ia_rsp);

    /// @brief A simple utility method that sets the status code
    ///
    /// Removes old status code and sets a new one.
    /// @param container status code will be added here
    /// @param status status code option
    void setStatusCode(boost::shared_ptr<Option6IA>& container,
                       const OptionPtr& status);

    /// @brief Iterates over new leases, update stale DNS entries
    ///
    /// Checks the context's current subnet (most recently selected) against
    /// an original selected subnet.  If they are the same the function
    /// simply returns.
    ///
    /// If they differ, we treat this as a dynamic subnet change made by the
    /// allocation engine. It is possible that DDNS subnet parameters for
    /// the new subnet are different and this needs to handled. We first
    /// save the current DNS-related values from the context and then
    /// re-run processClientFqdn().  This will rebuild the FQDN option
    /// to send back to the client based on the new subnet as well as
    /// update the context.  If the new values are different from the
    /// previous values, we iterate over the leases and update the
    /// DNS values.
    ///
    /// @param question Client's message.
    /// @param answer Server's response to a client. If server generated
    /// @param ctx client context (contains subnet, duid and other parameters)
    /// @param orig_subnet the originally selected subnet
    ///
    /// @note
    /// Subnet may be modified by the allocation engine, if the initial subnet
    /// belongs to a shared network.  Note that this will only handle cases
    /// where all IA_xx's in a client request result in a subnet change.  It is
    /// possible, currently, for the last IA_xx in request to end up using the
    /// same subnet as originally selected, and we will miss a change incurred
    /// by preceding IA_xx's.  In general users should be strongly encouraged to
    /// avoid situations where all of the following are true:
    ///
    /// 1. clients send more than one IA_xx in a query
    /// 2. subnets in the shared-network are equally eligible (i.e don't have
    /// class guards etc)
    /// 3. subnets have differing options or DDNS parameters
    //
    void checkDynamicSubnetChange(const Pkt6Ptr& question, Pkt6Ptr& answer,
                                  AllocEngine::ClientContext6& ctx,
                                  const Subnet6Ptr orig_subnet);
public:

    /// Used for DHCPv4-over-DHCPv6 too.

    /// @brief Check if the last relay added a relay-source-port option.
    ///
    /// @param query DHCPv6 message to be checked.
    /// @return the port to use to join the relay or 0 for the default.
    static uint16_t checkRelaySourcePort(const Pkt6Ptr& query);

private:

    /// @brief Assign class using vendor-class-identifier options
    ///
    /// @note This is the first part of @ref classifyPacket
    ///
    /// @param pkt packet to be classified
    void classifyByVendor(const Pkt6Ptr& pkt);

    /// @brief Update FQDN based on the reservations in the current subnet.
    ///
    /// When shared networks are in use the allocation engine may switch to
    /// a different subnet than originally selected. If this new subnet has
    /// hostname reservations there is a need to update the FQDN option
    /// value.
    ///
    /// This method should be called after lease assignments to perform
    /// such update when required.
    ///
    /// @param ctx Client context.
    /// @param answer Message being sent to a client, which may hold an FQDN
    /// option to be updated.
    ///
    /// @throw isc::Unexpected if specified message is NULL. This is treated
    /// as a programmatic error.
    void updateReservedFqdn(AllocEngine::ClientContext6& ctx,
                            const Pkt6Ptr& answer);

    /// @private
    /// @brief Generate FQDN to be sent to a client if none exists.
    ///
    /// This function is meant to be called by the functions which process
    /// client's messages. The function should be called after a function
    /// which creates FQDN option for the client. This option must exist
    /// in the answer message specified as an argument. It must also be
    /// called after functions which assign leases for a client. The
    /// IA options being a result of lease acquisition must be appended
    /// to the message specified as a parameter.
    ///
    /// If the Client FQDN option being present in the message carries empty
    /// hostname, this function will attempt to generate hostname from the
    /// IPv6 address being acquired by the client. The IPv6 address is retrieved
    /// from the IA_NA option carried in the specified message. If multiple
    /// addresses are present in the particular IA_NA option or multiple IA_NA
    /// options exist, the first address found is selected.
    ///
    /// The IPv6 address is converted to the hostname using the following
    /// pattern:
    /// @code
    ///     prefix-converted-ip-address.domain-name-suffix.
    /// @endcode
    /// where:
    /// - prefix is a configurable prefix string appended to all auto-generated
    /// hostnames.
    /// - converted-ip-address is created by replacing all colons from the IPv6
    /// address with hyphens.
    /// - domain-name-suffix is a suffix for a domain name that, together with
    /// the other parts, constitute the fully qualified domain name.
    ///
    /// When hostname is successfully generated, it is either used to update
    /// FQDN-related fields in a lease database or to update the Client FQDN
    /// option being sent back to the client. The lease database update is
    /// NOT performed if Advertise message is being processed.
    ///
    /// @param answer Message being sent to a client, which may hold IA_NA
    /// and Client FQDN options to be used to generate name for a client.
    /// @param ctx Client context.
    ///
    /// @throw isc::Unexpected if specified message is NULL. This is treated
    /// as a programmatic error.
    void generateFqdn(const Pkt6Ptr& answer,
                      AllocEngine::ClientContext6& ctx);

    /// @brief Updates statistics for received packets
    /// @param query packet received
    static void processStatsReceived(const Pkt6Ptr& query);

    /// @brief Checks if the specified option code has been requested using
    /// the Option Request option.
    ///
    /// @param query Pointer to the client's query.
    /// @parma code Option code.
    ///
    /// @return true if option has been requested in the ORO.
    bool requestedInORO(const Pkt6Ptr& query, const uint16_t code) const;

    /// @brief Check if the parking limit has been exceeded for given hook label.
    ///
    /// @brief hook_label Hook point name.
    ///
    /// @return tuple with boolean value concluding whether the limit has been
    /// exceeded, and the integer limit as a second value.
    static std::tuple<bool, uint32_t> parkingLimitExceeded(std::string const& hook_label);

protected:
    /// UDP port number on which server listens.
    uint16_t server_port_;

    /// UDP port number to which server sends all responses.
    uint16_t client_port_;

public:

    /// @note used by DHCPv4-over-DHCPv6 so must be public and static

    /// @brief Updates statistics for transmitted packets
    /// @param response packet transmitted
    static void processStatsSent(const Pkt6Ptr& response);

    /// @brief Returns the index of the buffer6_send hook
    /// @return the index of the buffer6_send hook
    static int getHookIndexBuffer6Send();

    /// @brief Executes buffer6_send callout and sends the response.
    ///
    /// @param callout_handle pointer to the callout handle.
    /// @param rsp pointer to a response.
    void processPacketBufferSend(hooks::CalloutHandlePtr& callout_handle,
                                 Pkt6Ptr& rsp);

    /// @brief Return a list of all paths that contain passwords or secrets for
    /// kea-dhcp6.
    ///
    /// @return the list of lists of sequential JSON map keys needed to reach
    /// the passwords and secrets.
    std::list<std::list<std::string>> jsonPathsToRedact() const final override;

protected:

    /// Server DUID (to be sent in server-identifier option)
    OptionPtr serverid_;

    /// Indicates if shutdown is in progress. Setting it to true will
    /// initiate server shutdown procedure.
    volatile bool shutdown_;

    /// @brief Executes pkt6_send callout.
    ///
    /// @param callout_handle pointer to the callout handle.
    /// @param query Pointer to a query.
    /// @param rsp Pointer to a response.
    /// @param subnet A pointer to the selected subnet.
    void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
                              Pkt6Ptr& query, Pkt6Ptr& rsp, Subnet6Ptr& subnet);

    /// @brief Allocation Engine.
    /// Pointer to the allocation engine that we are currently using
    /// It must be a pointer, because we will support changing engines
    /// during normal operation (e.g. to use different allocators)
    boost::shared_ptr<AllocEngine> alloc_engine_;

    /// Holds a list of @c isc::dhcp_ddns::NameChangeRequest objects, which
    /// are waiting for sending to kea-dhcp-ddns module.
    std::queue<isc::dhcp_ddns::NameChangeRequest> name_change_reqs_;

    /// @brief Holds information about disabled DHCP service and/or
    /// disabled subnet/network scopes.
    NetworkStatePtr network_state_;

    /// @brief Controls access to the configuration backends.
    CBControlDHCPv6Ptr cb_control_;
};

}  // namespace dhcp
}  // namespace isc

#endif // DHCP6_SRV_H