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
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
// 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 DHCPV4_SRV_H
#define DHCPV4_SRV_H

#include <asiolink/io_service.h>
#include <dhcp/dhcp4.h>
#include <dhcp/option.h>
#include <dhcp/option_string.h>
#include <dhcp/option4_client_fqdn.h>
#include <dhcp/option_custom.h>
#include <dhcp/pkt4.h>
#include <dhcp_ddns/ncr_msg.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/callout_handle_store.h>
#include <dhcpsrv/cb_ctl_dhcp4.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 DHCPv4 message exchange.
///
/// This class represents the DHCPv4 message exchange. The message exchange
/// consists of the single client message, server response to this message
/// and the mechanisms to generate the server's response. The server creates
/// the instance of the @c Dhcpv4Exchange for each inbound message that it
/// accepts for processing.
///
/// The use of the @c Dhcpv4Exchange object as a central repository of
/// information about the message exchange simplifies the API of the
/// @c Dhcpv4Srv class.
///
/// Another benefit of using this class is that different methods of the
/// @c Dhcpv4Srv may share information. For example, the constructor of this
/// class selects the subnet and multiple methods of @c Dhcpv4Srv use this
/// subnet, without the need to select it again.
///
/// @todo This is the initial version of this class. In the future a lot of
/// code from the @c Dhcpv4Srv class will be migrated here.
class Dhcpv4Exchange {
public:
    /// @brief Constructor.
    ///
    /// The constructor selects the subnet for the query and checks for the
    /// static host reservations for the client which has sent the message.
    /// The information about the reservations is stored in the
    /// @c AllocEngine::ClientContext4 object, which can be obtained by
    /// calling the @c getContext.
    ///
    /// @param alloc_engine Pointer to the instance of the Allocation Engine
    /// used by the server.
    /// @param query Pointer to the client message.
    /// @param context Pointer to the client context.
    /// @param subnet Pointer to the subnet to which the client belongs.
    /// @param drop if it is true the packet will be dropped.
    Dhcpv4Exchange(const AllocEnginePtr& alloc_engine, const Pkt4Ptr& query,
                   AllocEngine::ClientContext4Ptr& context,
                   const Subnet4Ptr& subnet, bool& drop);

    /// @brief Initializes the instance of the response message.
    ///
    /// The type of the response depends on the type of the query message.
    /// For the DHCPDISCOVER the DHCPOFFER is created. For the DHCPREQUEST
    /// and DHCPINFORM the DHCPACK is created. For the DHCPRELEASE the
    /// response is not initialized.
    void initResponse();

    /// @brief Initializes the DHCPv6 part of the response message
    ///
    /// Called by initResponse() when the query is a DHCP4o6 message
    void initResponse4o6();

    /// @brief Returns the pointer to the query from the client.
    Pkt4Ptr getQuery() const {
        return (query_);
    }

    /// @brief Returns the pointer to the server's response.
    ///
    /// The returned pointer is null if the query type is DHCPRELEASE or DHCPDECLINE.
    Pkt4Ptr getResponse() const {
        return (resp_);
    }

    /// @brief Removes the response message by resetting the pointer to null.
    void deleteResponse() {
        resp_.reset();
    }

    /// @brief Returns the copy of the context for the Allocation engine.
    AllocEngine::ClientContext4Ptr getContext() const {
        return (context_);
    }

    /// @brief Returns the configured option list (non-const version)
    CfgOptionList& getCfgOptionList() {
        return (cfg_option_list_);
    }

    /// @brief Returns the configured option list (const version)
    const CfgOptionList& getCfgOptionList() const {
        return (cfg_option_list_);
    }

    /// @brief Sets reserved values of siaddr, sname and file in the
    /// server's response.
    void setReservedMessageFields();

    /// @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 context pointer to the context.
    static void setHostIdentifiers(AllocEngine::ClientContext4Ptr context);

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

    /// @brief Assigns classes retrieved from host reservation database.
    ///
    /// @param context pointer to the context.
    static void setReservedClientClasses(AllocEngine::ClientContext4Ptr context);

    /// @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.
    void conditionallySetReservedClientClasses();

    /// @brief Assigns incoming packet to zero or more classes.
    ///
    /// @note This is done in two phases: first the content of the
    /// vendor-class-identifier option is used as a class, by
    /// calling @ref classifyByVendor(). Second, the classification match
    /// expressions are evaluated. The resulting classes will be stored
    /// in the packet (see @ref isc::dhcp::Pkt4::classes_ and
    /// @ref isc::dhcp::Pkt4::inClass).
    ///
    /// @param pkt packet to be classified
    static void classifyPacket(const Pkt4Ptr& 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.
    static void evaluateClasses(const Pkt4Ptr& pkt, bool depend_on_known);

private:

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

    /// @brief Copies default parameters from client's to server's message
    ///
    /// Some fields are copied from client's message into server's response,
    /// e.g. client HW address, number of hops, transaction-id etc.
    ///
    /// @warning This message is called internally by @c initResponse and
    /// thus it doesn't check if the resp_ value has been initialized. The
    /// calling method is responsible for making sure that @c resp_ is
    /// not null.
    void copyDefaultFields();

    /// @brief Copies default options from client's to server's message
    ///
    /// Some options are copied from client's message into server's response,
    /// e.g. Relay Agent Info option, Subnet Selection option etc.
    ///
    /// @warning This message is called internally by @c initResponse and
    /// thus it doesn't check if the resp_ value has been initialized. The
    /// calling method is responsible for making sure that @c resp_ is
    /// not null.
    void copyDefaultOptions();

    /// @brief Pointer to the allocation engine used by the server.
    AllocEnginePtr alloc_engine_;

    /// @brief Pointer to the DHCPv4 message sent by the client.
    Pkt4Ptr query_;

    /// @brief Pointer to the DHCPv4 message to be sent to the client.
    Pkt4Ptr resp_;

    /// @brief Context for use with allocation engine.
    AllocEngine::ClientContext4Ptr context_;

    /// @brief Configured option list.
    /// @note The configured option list is an *ordered* list of
    /// @c CfgOption objects used to append options to the response.
    CfgOptionList cfg_option_list_;
};

/// @brief Type representing the pointer to the @c Dhcpv4Exchange.
typedef boost::shared_ptr<Dhcpv4Exchange> Dhcpv4ExchangePtr;


/// @brief DHCPv4 server service.
///
/// This singleton class represents DHCPv4 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 ControlledDhcpv4Srv class for support for
/// command and configuration updates over msgq.
class Dhcpv4Srv : 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 Default constructor.
    ///
    /// Instantiates necessary services, required to run DHCPv4 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 DHCPv4 server will listen on and alternate port
    /// where DHCPv4 server sends all responses to. Those are mostly useful
    /// for testing purposes. The Last two arguments of the constructor
    /// should be left at default values for normal server operation.
    /// They should be set to 'false' when creating an instance of this
    /// class for unit testing because features they enable require
    /// root privileges.
    ///
    /// @param server_port specifies port number to listen on
    /// @param client_port specifies port number to send to
    /// @param use_bcast configure sockets to support broadcast messages.
    /// @param direct_response_desired specifies if it is desired to
    /// use direct V4 traffic.
    Dhcpv4Srv(uint16_t server_port = DHCP4_SERVER_PORT,
              uint16_t client_port = 0,
              const bool use_bcast = true,
              const bool direct_response_desired = true);

    /// @brief Destructor. Used during DHCPv4 service shutdown.
    virtual ~Dhcpv4Srv();

    /// @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.
    CBControlDHCPv4Ptr 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 'Dhcpv4Srv::getVersion'

    /// @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 DHCPv4 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(Pkt4Ptr query);

    /// @brief Process a single incoming DHCPv4 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(Pkt4Ptr query);

    /// @brief Process an unparked DHCPv4 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 selected subnet.
    void sendResponseNoThrow(hooks::CalloutHandlePtr& callout_handle,
                             Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet);

    /// @brief Process a single incoming DHCPv4 packet.
    ///
    /// It verifies correctness of the passed packet, calls per-type processXXX
    /// methods, generates appropriate answer.
    ///
    /// @param query A pointer to the packet to be processed.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    /// @return A pointer to the response.
    Pkt4Ptr processPacket(Pkt4Ptr query, bool allow_answer_park = true);

    /// @brief Process a single incoming DHCPv4 query.
    ///
    /// It localizes the query, calls per-type processXXX methods,
    /// generates appropriate answer.
    ///
    /// @param query A pointer to the packet to be processed.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    /// @return A pointer to the response.
    Pkt4Ptr processDhcp4Query(Pkt4Ptr query, bool allow_answer_park);

    /// @brief Process a single incoming DHCPv4 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.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    void processDhcp4QueryAndSendResponse(Pkt4Ptr query,
                                          bool allow_answer_park);

    /// @brief Process a localized incoming DHCPv4 query.
    ///
    /// It calls per-type processXXX methods, generates appropriate answer.
    ///
    /// @param ctx Pointer to The client context.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    /// @return A pointer to the response.
    Pkt4Ptr processLocalizedQuery4(AllocEngine::ClientContext4Ptr& ctx,
                                   bool allow_answer_park);

    /// @brief Process a localized incoming DHCPv4 query.
    ///
    /// It calls per-type processXXX methods, generates appropriate answer,
    /// sends the answer to the client.
    ///
    /// @param query A pointer to the unparked packet.
    /// @param ctx Pointer to The client context.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    void processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
                                               AllocEngine::ClientContext4Ptr& ctx,
                                               bool allow_answer_park);

    /// @brief Process a localized incoming DHCPv4 query.
    ///
    /// A variant of the precedent method used to resume processing
    /// for packets parked in the subnet4_select callout.
    ///
    /// @param query A pointer to the unparked packet.
    /// @param allow_answer_park Indicates if parking a packet is allowed.
    void processLocalizedQuery4AndSendResponse(Pkt4Ptr query,
                                               bool allow_answer_park);

    /// @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 67. Other ports are used
    /// for testing purposes only.
    ///
    /// @return UDP port on which server should listen.
    uint16_t getServerPort() const {
        return (server_port_);
    }

    /// @brief Return bool value indicating that broadcast flags should be set
    /// on sockets.
    ///
    /// @return A bool value indicating that broadcast should be used (if true).
    bool useBroadcast() const {
        return (use_bcast_);
    }
    //@}

    /// @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 Returns value of the test_send_responses_to_source_ flag.
    ///
    /// @return value of the test_send_responses_to_source_ flag.
    bool getSendResponsesToSource() const {
        return (test_send_responses_to_source_);
    }

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

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

protected:

    /// @name Functions filtering and sanity-checking received messages.
    ///
    /// @todo These functions are supposed to be moved to a new class which
    /// will manage different rules for accepting and rejecting messages.
    /// Perhaps ticket #3116 is a good opportunity to do it.
    ///
    //@{
    /// @brief Checks whether received message should be processed or discarded.
    ///
    /// This function checks whether received message should be processed or
    /// discarded. It should be called on the beginning of message processing
    /// (just after the message has been decoded). This message calls a number
    /// of other functions which check whether message should be processed,
    /// using different criteria.
    ///
    /// This function should be extended when new criteria for accepting
    /// received message have to be implemented. This function is meant to
    /// aggregate all early filtering checks on the received message. By having
    /// a single function like this, we are avoiding bloat of the server's main
    /// loop.
    ///
    /// @warning This function should remain exception safe.
    ///
    /// @param query Received message.
    ///
    /// @return true if the message should be further processed, or false if
    /// the message should be discarded.
    bool accept(const Pkt4Ptr& query);

    /// @brief Check if a message sent by directly connected client should be
    /// accepted or discarded.
    ///
    /// This function checks if the received message is from directly connected
    /// client. If it is, it checks that it should be processed or discarded.
    ///
    /// Note that this function doesn't validate all addresses being carried in
    /// the message. The primary purpose of this function is to filter out
    /// direct messages in the local network for which there is no suitable
    /// subnet configured. For example, this function accepts unicast messages
    /// because unicasts may be used by clients located in remote networks to
    /// to renew existing leases. If their notion of address is wrong, the
    /// server will have to sent a NAK, instead of dropping the message.
    /// Detailed validation of such messages is performed at later stage of
    /// processing.
    ///
    /// This function accepts the following messages:
    /// - all valid relayed messages,
    /// - all unicast messages,
    /// - all broadcast messages except DHCPINFORM received on the interface
    /// for which the suitable subnet exists (is configured).
    /// - all DHCPINFORM messages with source address or ciaddr set.
    ///
    /// @param query Message sent by a client.
    ///
    /// @return true if message is accepted for further processing, false
    /// otherwise.
    bool acceptDirectRequest(const Pkt4Ptr& query);

    /// @brief Check if received message type is valid for the server to
    /// process.
    ///
    /// This function checks that the received message type belongs to
    /// the range of types recognized by the server and that the
    /// message of this type should be processed by the server.
    ///
    /// The messages types accepted for processing are:
    /// - Discover
    /// - Request
    /// - Release
    /// - Decline
    /// - Inform
    ///
    /// @param query Message sent by a client.
    ///
    /// @return true if message is accepted for further processing, false
    /// otherwise.
    bool acceptMessageType(const Pkt4Ptr& query) const;

    /// @brief Verifies if the server id belongs to our server.
    ///
    /// This function checks if the server identifier carried in the specified
    /// DHCPv4 message belongs to this server. If the server identifier option
    /// is absent or the value carried by this option is equal to one of the
    /// server identifiers used by the server, the true is returned. If the
    /// server identifier option is present, but it doesn't match any server
    /// identifier used by this server, the false value is returned.
    ///
    /// @param pkt DHCPv4 message which server identifier is to be checked.
    ///
    /// @return true, if the server identifier is absent or matches one of the
    /// server identifiers that the server is using; false otherwise.
    bool acceptServerId(const Pkt4Ptr& 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.
    /// Calls the second method with the requirement level based on
    /// message type.
    ///
    /// @param query Pointer to the client's message.
    /// @throw RFCViolation if any issues are detected
    static void sanityCheck(const Pkt4Ptr& query);

    /// @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 query Pointer to the client's message.
    /// @param serverid expectation regarding server-id option
    /// @throw RFCViolation if any issues are detected
    static void sanityCheck(const Pkt4Ptr& query, RequirementLevel serverid);

    /// @brief Processes incoming DISCOVER and returns response.
    ///
    /// Processes received DISCOVER message and verifies that its sender
    /// should be served. In particular, a lease is selected and sent
    /// as an offer to a client if it should be served.
    ///
    /// @param discover DISCOVER message received from client
    /// @param context pointer to the client context
    ///
    /// @return OFFER message or null
    Pkt4Ptr processDiscover(Pkt4Ptr& discover, AllocEngine::ClientContext4Ptr& context);

    /// @brief Processes incoming REQUEST and returns REPLY response.
    ///
    /// Processes incoming REQUEST message and verifies that its sender
    /// should be served. In particular, verifies that requested lease
    /// is valid, not expired, not reserved, not used by other client and
    /// that requesting client is allowed to use it.
    ///
    /// Returns ACK message, NAK message, or null
    ///
    /// @param request a message received from client
    /// @param context pointer to the client context where allocated and
    /// deleted leases are stored.
    ///
    /// @return ACK or NAK message
    Pkt4Ptr processRequest(Pkt4Ptr& request, AllocEngine::ClientContext4Ptr& context);

    /// @brief Processes incoming DHCPRELEASE messages.
    ///
    /// In DHCPv4, server does not respond to RELEASE messages, therefore
    /// this function does not return anything.
    ///
    /// @param release message received from client
    /// @param context pointer to the client context where released lease is
    /// stored.
    void processRelease(Pkt4Ptr& release, AllocEngine::ClientContext4Ptr& context);

    /// @brief Process incoming DHCPDECLINE messages.
    ///
    /// This method processes incoming DHCPDECLINE. In particular, it extracts
    /// Requested IP Address option, checks that the address really belongs to
    /// the client and if it does, calls @ref declineLease.
    ///
    /// @param decline message received from client
    /// @param context pointer to the client context where declined lease is
    /// stored.
    void processDecline(Pkt4Ptr& decline, AllocEngine::ClientContext4Ptr& context);

    /// @brief Processes incoming DHCPINFORM messages.
    ///
    /// @param inform message received from client
    /// @param context pointer to the client context
    ///
    /// @return DHCPACK to be sent to the client.
    Pkt4Ptr processInform(Pkt4Ptr& inform, AllocEngine::ClientContext4Ptr& context);

    /// @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 ex The exchange where the configured option list is cached
    void buildCfgOptionList(Dhcpv4Exchange& ex);

    /// @brief Appends options requested by client.
    ///
    /// This method assigns options that were requested by client
    /// (sent in PRL) or are enforced by server.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void appendRequestedOptions(Dhcpv4Exchange& ex);

    /// @brief Appends requested vendor options as requested by client.
    ///
    /// This method is similar to \ref appendRequestedOptions(), but uses
    /// vendor options. The major difference is that vendor-options use
    /// its own option spaces (there may be more than one distinct set of vendor
    /// options, each with unique vendor-id). Vendor options are requested
    /// using separate options within their respective vendor-option spaces.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void appendRequestedVendorOptions(Dhcpv4Exchange& ex);

    /// @brief Assigns a lease and appends corresponding options
    ///
    /// This method chooses the most appropriate lease for requesting
    /// client and assigning it. Options corresponding to the lease
    /// are added to specific message.
    ///
    /// This method may reset the pointer to the response in the @c ex object
    /// to indicate that the response should not be sent to the client.
    /// The caller must check if the response is is null after calling
    /// this method.
    ///
    /// The response type in the @c ex object may be set to DHCPACK or DHCPNAK.
    ///
    /// @param ex DHCPv4 exchange holding the client's message to be checked.
    void assignLease(Dhcpv4Exchange& ex);

    /// @brief Update client name and DNS flags in the lease and response
    ///
    /// There are two cases when the client name (FQDN or hostname) and DNS
    /// flags need to updated after the lease has been allocated:
    /// 1. If the name is being generated from the lease address
    /// 2. If the allocation changed the chosen subnet
    ///
    /// In the first case this function will generate the name from the
    /// lease address.  In either case, the name and DNS flags are updated
    /// in the lease and in the response packet.
    ///
    /// @param ctx reference to the client context
    /// @param lease reference to the client lease
    /// @param query reference to the client query
    /// @param resp reference to the client response
    /// @param client_name_changed - true if the new values are already in
    /// the lease
    void postAllocateNameUpdate(const AllocEngine::ClientContext4Ptr& ctx,
                                const Lease4Ptr& lease, const Pkt4Ptr& query,
                                const Pkt4Ptr& resp, bool client_name_changed);

    /// @brief Adds the T1 and T2 timers to the outbound response as appropriate
    ///
    /// This method determines if either of the timers T1 (option 58) and T2
    /// (option 59) should be sent to the client.  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:
    ///
    /// By default neither T1 nor T2 will be sent.
    ///
    /// T2:
    ///
    /// If rebind-timer is set use its value, otherwise if calculate-tee-times
    /// is true use the value given by valid lease time * t2-percent.  Either
    /// way the value will only be sent if it is less than the valid lease time.
    ///
    /// T1:
    ///
    /// If renew-timer is set use its value, otherwise if calculate-tee-times
    /// is true use the value given by valid lease time * t1-percent.  Either
    /// way the value will only be sent if it is less than T2 when T2 is being
    /// sent, or less than the valid lease time if T2 is not being sent.
    ///
    /// @param lease lease being assigned to the client
    /// @param subnet the subnet to which the lease belongs
    /// @param resp outbound response for the client to which timers are added.
    void setTeeTimes(const Lease4Ptr& lease, const Subnet4Ptr& subnet, Pkt4Ptr resp);

    /// @brief Append basic options if they are not present.
    ///
    /// This function adds the following basic options if they
    /// are not yet added to the response message:
    /// - Subnet Mask,
    /// - Router,
    /// - Name Server,
    /// - Domain Name,
    /// - Server Identifier.
    ///
    /// @param ex DHCPv4 exchange holding the client's message to be checked.
    void appendBasicOptions(Dhcpv4Exchange& ex);

    /// @brief Sets fixed fields of the outgoing packet.
    ///
    /// If the incoming packets belongs to a class and that class defines
    /// next-server, server-hostname or boot-file-name, we need to set the
    /// siaddr, sname or filename fields in the outgoing packet. Also, those
    /// values can be defined for subnet or in reservations. The values
    /// defined in reservation takes precedence over class values, which
    /// in turn take precedence over subnet values.
    ///
    /// @param ex DHCPv4 exchange holding the client's message and the server's
    ///           response to be adjusted.
    void setFixedFields(Dhcpv4Exchange& ex);

    /// @brief Processes Client FQDN and Hostname Options sent by a client.
    ///
    /// Client may send Client FQDN or Hostname option to communicate its name
    /// to the server. Server may use this name to perform DNS update for the
    /// lease being assigned to a client. If server takes responsibility for
    /// updating DNS for a client it may communicate it by sending the Client
    /// FQDN or Hostname %Option back to the client. Server select a different
    /// name than requested by a client to update DNS. In such case, the server
    /// stores this different name in its response.
    ///
    /// Client should not send both Client FQDN and Hostname options. However,
    /// if client sends both options, server should prefer Client FQDN option
    /// and ignore the Hostname option. If Client FQDN option is not present,
    /// the Hostname option is processed.
    ///
    /// The Client FQDN %Option is processed by this function as described in
    /// RFC4702.
    ///
    /// In response to a Hostname %Option sent by a client, the server may send
    /// Hostname option with the same or different hostname. If different
    /// hostname is sent, it is an indication to the client that server has
    /// overridden the client's preferred name and will rather use this
    /// different name to update DNS. However, since Hostname option doesn't
    /// carry an information whether DNS update will be carried by the server
    /// or not, the client is responsible for checking whether DNS update
    /// has been performed.
    ///
    /// After successful processing options stored in the first parameter,
    /// this function may add Client FQDN or Hostname option to the response
    /// message. In some cases, server may cease to add any options to the
    /// response, i.e. when server doesn't support DNS updates.
    ///
    /// This function does not throw. It simply logs the debug message if the
    /// processing of the FQDN or Hostname failed.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void processClientName(Dhcpv4Exchange& ex);

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

    /// @brief Sets value of the test_send_responses_to_source_ flag.
    ///
    /// @param value new value of the test_send_responses_to_source_ flag.
    void setSendResponsesToSource(bool value) {
        test_send_responses_to_source_ = value;
    }

    /// @brief Renders a lease declined after the server has detected, via ping-check
    /// or other means, that its address is already in-use.
    ///
    /// This function is invoked during the unpark callback for the lease4_offer
    /// hook point, if a hook callout has set the handle status to NEXT_STEP_DROP.
    /// It will create/update the lease to DECLINED state in the lease store,
    /// update the appropriate stats, and @todo implement a new hook point,
    /// lease4_server_declined_lease (name subject to change).
    ///
    /// @param callout_handle - current callout handle.
    /// @param query - DHCPDISCOVER which instigated the declination.
    /// @param lease - lease to decline (i.e lease that would have been offered).
    /// @param lease_exists - true if the lease already exists in the lease store
    /// (as is the case when offer-lifetime is > 0).
    void serverDecline(hooks::CalloutHandlePtr& callout_handle, Pkt4Ptr& query,
                       Lease4Ptr lease, bool lease_exists);

    /// @brief Exception safe wrapper around serverDecline()
    ///
    /// In MT mode this wrapper is used to safely invoke serverDecline() as a
    /// DHCP worker thread task.
    ///
    /// @param callout_handle - current callout handle.
    /// @param query - DHCPDISCOVER which instigated the declination.
    /// @param lease - lease to decline (i.e lease that would have been offered).
    /// @param lease_exists - true if the lease already exists in the lease store
    /// (as is the case when offer-lifetime is > 0).
    void serverDeclineNoThrow(hooks::CalloutHandlePtr& callout_handle, Pkt4Ptr& query,
                              Lease4Ptr lease, bool lease_exists);

public:

    /// @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;

private:
    /// @brief Process Client FQDN %Option sent by a client.
    ///
    /// This function is called by the @c Dhcpv4Srv::processClientName when
    /// the client has sent the FQDN option in its message to the server.
    /// It comprises the actual logic to parse the FQDN option and prepare
    /// the FQDN option to be sent back to the client in the server's
    /// response.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void processClientFqdnOption(Dhcpv4Exchange& ex);

    /// @brief Process Hostname %Option sent by a client.
    ///
    /// This method is called by the @c Dhcpv4Srv::processClientName to
    /// create an instance of the Hostname option to be returned to the
    /// client. If this instance is created it is included in the response
    /// message within the @c Dhcpv4Exchange object passed as an argument.
    ///
    /// The Hostname option instance is created if the client has included
    /// Hostname option in its query to the server or if the client has
    /// included Hostname option code in the Parameter Request List option.
    /// In the former case, the server can use the Hostname supplied by the
    /// client or replace it with a new hostname, depending on the server's
    /// configuration. A reserved hostname takes precedence over a hostname
    /// supplied by the client or auto generated hostname.
    ///
    /// If the 'qualifying-suffix' parameter is specified, its value is used
    /// to qualify a hostname. For example, if the host reservation contains
    /// a hostname 'marcin-laptop', and the qualifying suffix is
    /// 'example.isc.org', the hostname returned to the client will be
    /// 'marcin-laptop.example.isc.org'. If the 'qualifying-suffix' is not
    /// specified (empty), the reserved hostname is returned to the client
    /// unqualified.
    ///
    /// The 'qualifying-suffix' value is also used to qualify the hostname
    /// supplied by the client, when this hostname is unqualified,
    /// e.g. 'laptop-x'. If the supplied hostname is qualified, e.g.
    /// 'laptop-x.example.org', the qualifying suffix will not be appended
    /// to it.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void processHostnameOption(Dhcpv4Exchange& ex);

    /// @public
    /// @brief Marks lease as declined.
    ///
    /// This method moves a lease to declined state with all the steps involved:
    /// - trigger DNS removal (if necessary)
    /// - disassociate the client information
    /// - update lease in the database (switch to DECLINED state)
    /// - increase necessary statistics
    /// - call lease4_decline hook
    ///
    /// @param lease lease to be declined
    /// @param decline client's message
    /// @param context reference to a client context
    void declineLease(const Lease4Ptr& lease, const Pkt4Ptr& decline,
                      AllocEngine::ClientContext4Ptr& context);

protected:

    /// @brief Creates NameChangeRequests which correspond to the lease
    /// which has been acquired.
    ///
    /// If this function is called when an existing lease is renewed, it
    /// may generate NameChangeRequest to remove existing DNS entries which
    /// correspond to the old lease instance. This function may cease to
    /// generate NameChangeRequests if the notion of the client's FQDN hasn't
    /// changed between an old and new lease.
    ///
    /// @param lease A pointer to the new lease which has been acquired.
    /// @param old_lease A pointer to the instance of the old lease which has
    /// @param ddns_params DDNS configuration parameters
    /// been replaced by the new lease passed in the first argument. The null
    /// value indicates that the new lease has been allocated, rather than
    /// lease being renewed.
    void createNameChangeRequests(const Lease4Ptr& lease,
                                  const Lease4Ptr& old_lease,
                                  const DdnsParams& ddns_params);

    /// @brief Attempts to renew received addresses
    ///
    /// Attempts to renew existing lease. This typically includes finding a lease that
    /// corresponds to the received address. If no such lease is found, a status code
    /// response is generated.
    ///
    /// @param renew client's message asking for renew
    /// @param reply server's response (ACK or NAK)
    void renewLease(const Pkt4Ptr& renew, Pkt4Ptr& reply);

    /// @brief Adds server identifier option to the server's response.
    ///
    /// This method adds a server identifier to the DHCPv4 message if it doesn't
    /// exist yet. This is set to the local address on which the client's query has
    /// been received with the exception of broadcast traffic and DHCPv4o6 query for
    /// which a socket on the particular interface is found and its address is used
    /// as server id.
    ///
    /// @note This method doesn't throw exceptions by itself but the underlying
    /// classes being used my throw. The reason for this method to not sanity
    /// check the specified message is that it is meant to be called internally
    /// by the @c Dhcpv4Srv class.
    ///
    /// @note This method is static because it is not dependent on the class
    /// state.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    static void appendServerID(Dhcpv4Exchange& ex);

    /// @brief Check if the relay port RAI sub-option was set in the query.
    ///
    /// @param ex The exchange holding the client's message
    /// @return the port to use to join the relay or 0 for the default
    static uint16_t checkRelayPort(const Dhcpv4Exchange& ex);

    /// @brief Set IP/UDP and interface parameters for the DHCPv4 response.
    ///
    /// This method sets the following parameters for the DHCPv4 message being
    /// sent to a client:
    /// - client unicast or a broadcast address,
    /// - client or relay port,
    /// - server address,
    /// - server port,
    /// - name and index of the interface which is to be used to send the
    /// message.
    ///
    /// Internally it calls the @c Dhcpv4Srv::adjustRemoteAddr to figure
    /// out the destination address (client unicast address or broadcast
    /// address).
    ///
    /// The destination port is always DHCPv4 client (68) or relay (67) port,
    /// depending if the response will be sent directly to a client, unless
    /// a client port was enforced from the command line.
    ///
    /// The source port is always set to DHCPv4 server port (67).
    ///
    /// The interface selected for the response is always the same as the
    /// one through which the query has been received.
    ///
    /// The source address for the response is the IPv4 address assigned to
    /// the interface being used to send the response. This function uses
    /// @c IfaceMgr to get the socket bound to the IPv4 address on the
    /// particular interface.
    ///
    /// @note This method is static because it is not dependent on the class
    /// state.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void adjustIfaceData(Dhcpv4Exchange& ex);

    /// @brief Sets remote addresses for outgoing packet.
    ///
    /// This method sets the local and remote addresses on outgoing packet.
    /// The addresses being set depend on the following conditions:
    /// - has incoming packet been relayed,
    /// - is direct response to a client without address supported,
    /// - type of the outgoing packet,
    /// - broadcast flag set in the incoming packet.
    ///
    /// @warning This method does not check whether provided packet pointers
    /// are valid. Make sure that pointers are correct before calling this
    /// function.
    ///
    /// @param ex The exchange holding both the client's message and the
    /// server's response.
    void adjustRemoteAddr(Dhcpv4Exchange& ex);

    /// @brief converts server-id to text
    /// Converts content of server-id option to a text representation, e.g.
    /// "192.0.2.1"
    ///
    /// @param opt option that contains server-id
    /// @return string representation
    static std::string srvidToString(const OptionPtr& opt);

    /// @brief Selects a subnet for a given client's packet.
    ///
    /// If selectSubnet is called to simply do sanity checks (check if a
    /// subnet would be selected), then there is no need to call hooks,
    /// as this will happen later (when selectSubnet is called again).
    /// In such case the sanity_only should be set to true.
    ///
    /// @param query client's message
    /// @param drop if it is true the packet will be dropped
    /// @param sanity_only if it is true the callout won't be called
    /// @param allow_answer_park Indicates if parking a packet is allowed
    /// @return selected subnet (or null if no suitable subnet was found)
    isc::dhcp::Subnet4Ptr selectSubnet(const Pkt4Ptr& query,
                                       bool& drop,
                                       bool sanity_only = false,
                                       bool allow_answer_park = true);

    /// @brief Selects a subnet for a given client's DHCP4o6 packet.
    ///
    /// If selectSubnet is called to simply do sanity checks (check if a
    /// subnet would be selected), then there is no need to call hooks,
    /// as this will happen later (when selectSubnet is called again).
    /// In such case the sanity_only should be set to true.
    ///
    /// @param query client's message
    /// @param drop if it is true the packet will be dropped
    /// @param sanity_only if it is true the callout won't be called
    /// @param allow_answer_park Indicates if parking a packet is allowed
    /// @return selected subnet (or null if no suitable subnet was found)
    isc::dhcp::Subnet4Ptr selectSubnet4o6(const Pkt4Ptr& query,
                                          bool& drop,
                                          bool sanity_only = false,
                                          bool allow_answer_park = true);

    /// @brief dummy wrapper around IfaceMgr::receive4
    ///
    /// This method is useful for testing purposes, where its replacement
    /// simulates reception of a packet. For that purpose it is protected.
    virtual Pkt4Ptr 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 Pkt4Ptr& 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 option is used as a class, by
    /// calling (private) classifyByVendor(). Second classification match
    /// expressions are evaluated. The resulting classes will be stored
    /// in the packet (see @ref isc::dhcp::Pkt4::classes_ and
    /// @ref isc::dhcp::Pkt4::inClass).
    ///
    /// @param pkt packet to be classified
    void classifyPacket(const Pkt4Ptr& pkt);

    /// @brief Recover stashed agent options from client address lease.
    ///
    /// This method checks:
    ///  - client address is not 0.0.0.0.
    ///  - relay address is 0.0.0.0.
    ///  - stash-agent-options is true (vs false, the default).
    ///  - the query is a DHCPREQUEST.
    ///  - there is no RAI or an empty RAI in the query.
    ///  - the query is not member of the STASH_AGENT_OPTIONS client class.
    ///  - there is a lease for the client address.
    ///  - the lease is not expired.
    ///  - the lease has a RAI in its extended info in its user context.
    ///  - the lease belongs to the client.
    ///  - a not empty RAI can be recovered from the lease.
    /// when all checks pass:
    ///  - add the recovered RAI to the query.
    ///  - put the query in the STASH_AGENT_OPTIONS client class.
    void recoverStashedAgentOption(const Pkt4Ptr& query);

protected:

    /// @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 ex The exchange holding needed information.
    void requiredClassify(Dhcpv4Exchange& ex);

    /// @brief Perform deferred option unpacking.
    ///
    /// @note Options 43 and 224-254 are processed after classification.
    /// If a class configures a definition it is applied, if none
    /// the global (user) definition is applied. For option 43
    /// a last resort definition (same definition as used in previous Kea
    /// versions) is applied when none is found.
    ///
    /// @param query Pointer to the client message.
    void deferredUnpack(Pkt4Ptr& query);

    /// @brief Executes pkt4_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 selected subnet.
    void processPacketPktSend(hooks::CalloutHandlePtr& callout_handle,
                              Pkt4Ptr& query, Pkt4Ptr& rsp, Subnet4Ptr& subnet);

    /// @brief Executes buffer4_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,
                                 Pkt4Ptr& rsp);

private:

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

    /// @brief Constructs netmask option based on subnet4
    /// @param subnet subnet for which the netmask will be calculated
    ///
    /// @return Option that contains netmask information
    static OptionPtr getNetmaskOption(const Subnet4Ptr& subnet);

    /// @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_;

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

    /// @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_;

    /// Should broadcast be enabled on sockets (if true).
    bool use_bcast_;

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

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

private:

    /// @brief store value that defines if kea will send responses
    /// to a source address of incoming packet. Only for testing.
    bool test_send_responses_to_source_;

public:

    /// Class methods for DHCPv4-over-DHCPv6 handler

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

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

    /// @brief Returns the index for "buffer4_receive" hook point
    /// @return the index for "buffer4_receive" hook point
    static int getHookIndexBuffer4Receive();

    /// @brief Returns the index for "pkt4_receive" hook point
    /// @return the index for "pkt4_receive" hook point
    static int getHookIndexPkt4Receive();

    /// @brief Returns the index for "subnet4_select" hook point
    /// @return the index for "subnet4_select" hook point
    static int getHookIndexSubnet4Select();

    /// @brief Returns the index for "lease4_release" hook point
    /// @return the index for "lease4_release" hook point
    static int getHookIndexLease4Release();

    /// @brief Returns the index for "pkt4_send" hook point
    /// @return the index for "pkt4_send" hook point
    static int getHookIndexPkt4Send();

    /// @brief Returns the index for "buffer4_send" hook point
    /// @return the index for "buffer4_send" hook point
    static int getHookIndexBuffer4Send();

    /// @brief Returns the index for "lease4_decline" hook point
    /// @return the index for "lease4_decline" hook point
    static int getHookIndexLease4Decline();

    /// @brief Return a list of all paths that contain passwords or secrets for
    /// kea-dhcp4.
    ///
    /// @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;
};

}  // namespace dhcp
}  // namespace isc

#endif // DHCP4_SRV_H