generic_host_data_source_unittest.h 24.6 KB
Newer Older
1
// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// 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/.
6 7 8 9

#ifndef GENERIC_HOST_DATA_SOURCE_UNITTEST_H
#define GENERIC_HOST_DATA_SOURCE_UNITTEST_H

10
#include <asiolink/io_address.h>
11
#include <dhcpsrv/base_host_data_source.h>
12
#include <dhcpsrv/host.h>
13
#include <dhcp/classify.h>
14
#include <dhcp/option.h>
15
#include <boost/algorithm/string/join.hpp>
16
#include <boost/shared_ptr.hpp>
17
#include <gtest/gtest.h>
18
#include <sstream>
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include <vector>

namespace isc {
namespace dhcp {
namespace test {

/// @brief Test Fixture class with utility functions for HostDataSource backends
///
/// It contains utility functions for test purposes.
/// All concrete HostDataSource test classes should be derived from it.
class GenericHostDataSourceTest : public ::testing::Test {
public:

    /// @brief Universe (V4 or V6).
    enum Universe {
        V4,
        V6
    };

38 39 40 41 42 43 44 45 46 47
    /// @brief Options to be inserted into a host.
    ///
    /// Parameter of this type is passed to the @ref addTestOptions to
    /// control which option types should be inserted into a host.
    enum AddedOptions {
        DHCP4_ONLY,
        DHCP6_ONLY,
        DHCP4_AND_DHCP6
    };

48 49 50 51 52 53 54 55 56
    /// @brief Default constructor.
    GenericHostDataSourceTest();

    /// @brief Virtual destructor.
    virtual ~GenericHostDataSourceTest();

    /// @brief Creates a host reservation for specified IPv4 address.
    ///
    /// @param address IPv4 address to be set
57
    /// @param id Identifier type.
58 59
    ///
    /// @return generated Host object
60 61
    HostPtr initializeHost4(const std::string& address,
                            const Host::IdentifierType& id);
62 63 64 65

    /// @brief Creates a host reservation for specified IPv6 address.
    ///
    /// @param address IPv6 address to be reserved
66
    /// @param id type of identifier (IDENT_DUID or IDENT_HWADDR are supported)
67
    /// @param prefix reservation type (true = prefix, false = address)
68 69
    /// @param new_identifier Boolean value indicating if new host
    /// identifier should be generated or the same as previously.
70 71
    ///
    /// @return generated Host object
72
    HostPtr initializeHost6(std::string address, Host::IdentifierType id,
73
                            bool prefix, bool new_identifier = true);
74 75 76

    /// @brief Generates a hardware address in text version.
    ///
77 78
    /// @param increase A boolean value indicating if new address (increased)
    /// must be generated or the same address as previously.
79
    /// @return HW address in textual form acceptable by Host constructor
80
    std::vector<uint8_t> generateHWAddr(const bool new_identifier = true);
81

82
    /// @brief Generates a host identifier in a textual form..
83
    ///
84 85 86 87
    /// @param increase A boolean value indicating if new identifier (increased)
    /// must be generated or the same identifier as previously.
    /// @return Identifier in textual form acceptable by Host constructor
    std::vector<uint8_t> generateIdentifier(const bool new_identifier = true);
88

Andrei Pavel's avatar
Andrei Pavel committed
89 90 91 92 93 94 95
    /// @brief Checks if the reservation is in the range of reservations.
    ///
    /// @param resrv Reservation to be searched for.
    /// @param range Range of reservations returned by the @c Host object
    /// in which the reservation will be searched
    bool reservationExists(const IPv6Resrv& resrv, const IPv6ResrvRange& range);

96 97
    /// @brief Compares hardware addresses of the two hosts.
    ///
Andrei Pavel's avatar
Andrei Pavel committed
98
    /// This method compares two hardware address and uses gtest
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
    /// macros to signal unexpected (mismatch if expect_match is true;
    /// match if expect_match is false) values.
    ///
    /// @param host1 first host to be compared
    /// @param host2 second host to be compared
    /// @param expect_match true = HW addresses expected to be the same,
    ///                     false = HW addresses expected to be different
    void
    compareHwaddrs(const ConstHostPtr& host1, const ConstHostPtr& host2,
                   bool expect_match);

    /// @brief Compares DUIDs of the two hosts.
    ///
    /// This method compares two DUIDs (client-ids) and uses gtest
    /// macros to signal unexpected (mismatch if expect_match is true;
    /// match if expect_match is false) values.
    ///
    /// @param host1 first host to be compared
    /// @param host2 second host to be compared
    /// @param expect_match true = DUIDs expected to be the same,
    ///                     false = DUIDs expected to be different
    void
    compareDuids(const ConstHostPtr& host1, const ConstHostPtr& host2,
                 bool expect_match);

    /// @brief Compares two hosts
    ///
    /// This method uses gtest macros to signal errors.
    ///
    /// @param host1 first host to compare
    /// @param host2 second host to compare
    void compareHosts(const ConstHostPtr& host1, const ConstHostPtr& host2);

Andrei Pavel's avatar
Andrei Pavel committed
132
    /// @brief Used to sort a host collection by IPv4 subnet id.
Andrei Pavel's avatar
Andrei Pavel committed
133 134 135
    static bool compareHostsForSort4(const ConstHostPtr& host1,
                                     const ConstHostPtr& host2);

Andrei Pavel's avatar
Andrei Pavel committed
136
    /// @brief Used to sort a host collection by IPv6 subnet id.
Andrei Pavel's avatar
Andrei Pavel committed
137 138 139
    static bool compareHostsForSort6(const ConstHostPtr& host1,
                                     const ConstHostPtr& host2);

140 141 142 143 144 145
    /// @brief Compares two IPv6 reservation lists.
    ///
    /// This method uses gtest macros to signal errors.
    ///
    /// @param resv1 first IPv6 reservations list
    /// @param resv2 second IPv6 reservations list
146
    void compareReservations6(IPv6ResrvRange resv1, IPv6ResrvRange resv2);
147 148 149 150 151 152 153 154

    /// @brief Compares two client classes
    ///
    /// This method uses gtest macros to signal errors.
    ///
    /// @param classes1 first list of client classes
    /// @param classes2 second list of client classes
    void compareClientClasses(const ClientClasses& classes1,
155
                              const ClientClasses& classes2);
156 157 158

    /// @brief Compares options within two configurations.
    ///
159 160
    /// This method uses gtest macros to signal errors.
    ///
161 162 163 164 165
    /// @param cfg1 First configuration.
    /// @param cfg2 Second configuration.
    void compareOptions(const ConstCfgOptionPtr& cfg1,
                        const ConstCfgOptionPtr& cfg2) const;

Josh Soref's avatar
Josh Soref committed
166
    /// @brief Creates an option descriptor holding an empty option.
167 168 169 170 171 172 173
    ///
    /// @param universe V4 or V6.
    /// @param option_type Option type.
    /// @param persist A boolean flag indicating if the option is always
    /// returned to the client or only when requested.
    ///
    /// @return Descriptor holding an empty option.
174 175 176 177
    OptionDescriptor createEmptyOption(const Option::Universe& universe,
                                       const uint16_t option_type,
                                       const bool persist) const;

178
    /// @brief Creates an instance of the option for which it is possible to
179 180
    /// specify universe, option type, persistence flag  and value in
    /// the constructor.
181 182 183 184 185
    ///
    /// Examples of options that can be created using this function are:
    /// - @ref OptionString
    /// - different variants of @ref OptionInt.
    ///
186 187 188 189 190 191 192 193 194 195 196
    /// @param universe V4 or V6.
    /// @param option_type Option type.
    /// @param persist A boolean flag indicating if the option is always
    /// returned to the client or only when requested.
    /// @param formatted A boolean value selecting if the formatted option
    /// value should be used (if true), or binary value (if false).
    /// @param value Option value to be assigned to the option.
    /// @tparam OptionType Class encapsulating the option.
    /// @tparam DataType Option value data type.
    ///
    /// @return Descriptor holding an instance of the option created.
197 198 199 200
    template<typename OptionType, typename DataType>
    OptionDescriptor createOption(const Option::Universe& universe,
                                  const uint16_t option_type,
                                  const bool persist,
201
                                  const bool formatted,
202 203 204
                                  const DataType& value) const {
        boost::shared_ptr<OptionType> option(new OptionType(universe, option_type,
                                                            value));
205 206
        std::ostringstream s;
        if (formatted) {
207 208
            // Using formatted option value. Convert option value to a
            // textual format.
209 210 211
            s << value;
        }
        OptionDescriptor desc(option, persist, s.str());
212 213 214
        return (desc);
    }

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    /// @brief Creates an instance of the option for which it is possible to
    /// specify option type, persistence flag  and value in the constructor.
    ///
    /// Examples of options that can be created using this function are:
    /// - @ref Option4AddrLst
    /// - @ref Option6AddrLst
    ///
    /// @param option_type Option type.
    /// @param persist A boolean flag indicating if the option is always
    /// returned to the client or only when requested.
    /// @param formatted A boolean value selecting if the formatted option
    /// value should be used (if true), or binary value (if false).
    /// @param value Option value to be assigned to the option.
    /// @tparam OptionType Class encapsulating the option.
    /// @tparam DataType Option value data type.
    ///
    /// @return Descriptor holding an instance of the option created.
232 233 234
    template<typename OptionType, typename DataType>
    OptionDescriptor createOption(const uint16_t option_type,
                                  const bool persist,
235
                                  const bool formatted,
236 237
                                  const DataType& value) const {
        boost::shared_ptr<OptionType> option(new OptionType(option_type, value));
238 239 240

        std::ostringstream s;
        if (formatted) {
241 242
            // Using formatted option value. Convert option value to a
            // textual format.
243 244 245 246
            s << value;
        }

        OptionDescriptor desc(option, persist, s.str());
247 248 249
        return (desc);
    }

250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    /// @brief Creates an instance of the option holding list of IP addresses.
    ///
    /// @param option_type Option type.
    /// @param persist A boolean flag indicating if the option is always
    /// returned to the client or only when requested.
    /// @param formatted A boolean value selecting if the formatted option
    /// value should be used (if true), or binary value (if false).
    /// @param address1 First address to be included. If address is empty, it is
    /// not included.
    /// @param address2 Second address to be included. If address is empty, it
    /// is not included.
    /// @param address3 Third address to be included. If address is empty, it
    /// is not included.
    /// @tparam OptionType Class encapsulating the option.
    ///
    /// @return Descriptor holding an instance of the option created.
266 267 268 269
    template<typename OptionType>
    OptionDescriptor
    createAddressOption(const uint16_t option_type,
                        const bool persist,
270
                        const bool formatted,
271 272 273
                        const std::string& address1 = "",
                        const std::string& address2 = "",
                        const std::string& address3 = "") const {
274
        std::ostringstream s;
275
        // First address.
276 277 278
        typename OptionType::AddressContainer addresses;
        if (!address1.empty()) {
            addresses.push_back(asiolink::IOAddress(address1));
279 280 281
            if (formatted) {
                s << address1;
            }
282
        }
283
        // Second address.
284 285
        if (!address2.empty()) {
            addresses.push_back(asiolink::IOAddress(address2));
286 287 288 289 290 291
            if (formatted) {
                if (s.tellp() != std::streampos(0)) {
                    s << ",";
                }
                s << address2;
            }
292
        }
293
        // Third address.
294 295
        if (!address3.empty()) {
            addresses.push_back(asiolink::IOAddress(address3));
296 297 298 299 300 301
            if (formatted) {
                if (s.tellp() != std::streampos(0)) {
                    s << ",";
                }
                s << address3;
            }
302
        }
303

304 305
        boost::shared_ptr<OptionType> option(new OptionType(option_type,
                                                            addresses));
306
        OptionDescriptor desc(option, persist, s.str());
307 308 309
        return (desc);
    }

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
    /// @brief Returns number of entries in the v4 options table.
    ///
    /// This utility method is expected to be implemented by specific backends.
    /// The code here is just a boilerplate for backends that do not store
    /// host options in a table.
    ///
    /// @param number of existing entries in options table
    virtual int countDBOptions4() {
        return (-1);
    }

    /// @brief Returns number of entries in the v6 options table.
    ///
    /// This utility method is expected to be implemented by specific backends.
    /// The code here is just a boilerplate for backends that do not store
    /// host options in a table.
    ///
    /// @param number of existing entries in options table
    virtual int countDBOptions6() {
        return (-1);
    }

    /// @brief Returns number of entries in the v6 reservations table.
    ///
    /// This utility method is expected to be implemented by specific backends.
    /// The code here is just a boilerplate for backends that do not store
    /// v6 reservations in a table.
    ///
    /// @param number of existing entries in v6_reservations table
    virtual int countDBReservations6() {
        return (-1);
    }

343 344 345 346 347 348 349 350 351 352
    /// @brief Creates an instance of the vendor option.
    ///
    /// @param universe V4 or V6.
    /// @param persist A boolean flag indicating if the option is always
    /// returned to the client or only when requested.
    /// @param formatted A boolean value selecting if the formatted option
    /// value should be used (if true), or binary value (if false).
    /// @param vendor_id Vendor identifier.
    ///
    /// @return Descriptor holding an instance of the option created.
353 354
    OptionDescriptor createVendorOption(const Option::Universe& universe,
                                        const bool persist,
355
                                        const bool formatted,
356 357
                                        const uint32_t vendor_id) const;

358 359 360 361 362 363 364 365 366 367 368
    /// @brief Adds multiple options into the host.
    ///
    /// This method creates the following options into the host object:
    /// - DHCPv4 boot file name option,
    /// - DHCPv4 default ip ttl option,
    /// - DHCPv4 option 1 within vendor-encapsulated-options space,
    /// - DHCPv4 option 254 with a single IPv4 address,
    /// - DHCPv4 option 1 within isc option space,
    /// - DHCPv6 boot file url option,
    /// - DHCPv6 information refresh time option,
    /// - DHCPv6 vendor option with vendor id 2495,
Josh Soref's avatar
Josh Soref committed
369
    /// - DHCPv6 option 1024, with a single IPv6 address,
370 371 372 373 374 375 376 377 378 379
    /// - DHCPv6 empty option 1, within isc2 option space,
    /// - DHCPv6 option 2, within isc2 option space with 3 IPv6 addresses,
    ///
    /// This method also creates option definitions for the non-standard
    /// options and registers them in the LibDHCP as runtime option
    /// definitions.
    ///
    /// @param host Host object into which options should be added.
    /// @param formatted A boolean value selecting if the formatted option
    /// value should be used (if true), or binary value (if false).
380 381 382 383
    /// @param added_options Controls which options should be inserted into
    /// a host: DHCPv4, DHCPv6 options or both.
    void addTestOptions(const HostPtr& host, const bool formatted,
                        const AddedOptions& added_options) const;
384 385

    /// @brief Pointer to the host data source
386
    HostDataSourcePtr hdsptr_;
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
    /// @brief Test that backend can be started in read-only mode.
    ///
    /// Some backends can operate when the database is read only, e.g.
    /// host reservation tables are read only, or the database user has
    /// read only privileges on the entire database. In such cases, the
    /// Kea server administrator can specify in the backend configuration
    /// that the database should be opened in read only mode, i.e.
    /// INSERT, UPDATE, DELETE statements can't be issued. If any of the
    /// functions updating the database is called for the backend, the
    /// error is reported. The database running in read only mode can
    /// be merely used to retrieve existing host reservations from the
    /// database. This test verifies that this is the case.
    ///
    /// @param valid_db_type Parameter specifying type of backend to
    /// be used, e.g. type=mysql.
    void testReadOnlyDatabase(const char* valid_db_type);

405 406 407 408
    /// @brief Test that checks that simple host with IPv4 reservation
    ///        can be inserted and later retrieved.
    ///
    /// Uses gtest macros to report failures.
409 410
    /// @param id Identifier type.
    void testBasic4(const Host::IdentifierType& id);
411 412 413 414 415

    /// @brief Test inserts several hosts with unique IPv4 address and
    ///        checks that they can be retrieved properly.
    ///
    /// Uses gtest macros to report failures.
416 417
    /// @param id Identifier type.
    void testGetByIPv4(const Host::IdentifierType& id);
418

419
    /// @brief Test that hosts can be retrieved by host identifier.
420 421
    ///
    /// Uses gtest macros to report failures.
422
    void testGet4ByIdentifier(const Host::IdentifierType& identifier_type);
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

    /// @brief Test that clients with stored HW address can't be retrieved
    ///        by DUID with the same value.
    ///
    /// Test procedure: add host reservation with hardware address X, try to retrieve
    /// host by client-identifier X, verify that the reservation is not returned.
    ///
    /// Uses gtest macros to report failures.
    void testHWAddrNotClientId();

    /// @brief Test that clients with stored DUID can't be retrieved
    ///        by HW address of the same value.
    ///
    /// Test procedure: add host reservation with client identifier X, try to
    /// retrieve host by hardware address X, verify that the reservation is not
    /// returned.
    ///
    /// Uses gtest macros to report failures.
    void testClientIdNotHWAddr();

    /// @brief Test adds specified number of hosts with unique hostnames, then
Andrei Pavel's avatar
Andrei Pavel committed
444
    /// retrives them and checks that the hostnames are set properly.
445 446 447 448 449 450 451 452 453 454 455 456 457
    ///
    /// Uses gtest macros to report failures.
    ///
    /// @param name hostname to be used (if n>1, numbers will be appended)
    /// @param num number of hostnames to be added.
    void testHostname(std::string name, int num);

    /// @brief Test inserts multiple reservations for the same host for different
    /// subnets and check that they can be retrieved properly.
    ///
    /// Uses gtest macros to report failures.
    ///
    /// @param subnets number of subnets to test
458 459
    /// @param id Host identifier type.
    void testMultipleSubnets(int subnets, const Host::IdentifierType& id);
460 461 462 463 464

    /// @brief Test inserts several hosts with unique IPv6 addresses and
    ///        checks that they can be retrieved properly.
    ///
    /// Uses gtest macros to report failures.
465
    /// @param id type of the identifier to be used (IDENT_HWADDR or IDENT_DUID)
466
    /// @param prefix true - reserve IPv6 prefix, false - reserve IPv6 address
467
    void testGetByIPv6(Host::IdentifierType id, bool prefix);
468

469 470 471 472 473
    /// @brief Test inserts several hosts with unique prefixes and checks
    ///        that the can be retrieved by subnet id and prefix value.
    void testGetBySubnetIPv6();


474 475 476 477 478 479 480 481 482 483
    /// @brief Test that hosts can be retrieved by hardware address.
    ///
    /// Uses gtest macros to report failures.
    void testGet6ByHWAddr();

    /// @brief Test that hosts can be retrieved by client-id
    ///
    /// Uses gtest macros to report failures.
    void testGet6ByClientId();

484 485 486 487 488 489
    /// @brief Test verifies if a host reservation can be stored with both
    ///         IPv6 address and prefix.
    /// Uses gtest macros to report failures.
    void testAddr6AndPrefix();

    /// @brief Tests if host with multiple IPv6 reservations can be added and then
490 491
    ///        retrieved correctly.
    void testMultipleReservations();
492

493
    /// @brief Tests if compareIPv6Reservations() method treats same pool of
494 495
    ///        reservations but added in different order as equal.
    void testMultipleReservationsDifferentOrder();
496

497 498 499 500 501 502
    /// @brief Test if host reservations made for different IPv6 subnets
    ///        are handled correctly.
    ///
    /// Uses gtest macros to report failures.
    ///
    /// @param subnets number of subnets to test
503 504
    /// @param id identifier type (IDENT_HWADDR or IDENT_DUID)
    void testSubnetId6(int subnets, Host::IdentifierType id);
505

506 507 508 509 510 511 512 513 514 515 516
    /// @brief Test if the duplicate host with same DUID can't be inserted.
    ///
    /// Uses gtest macros to report failures.
    void testAddDuplicate6WithSameDUID();

    /// @brief Test if the duplicate host with same HWAddr can't be inserted.
    ///
    /// Uses gtest macros to report failures.
    void testAddDuplicate6WithSameHWAddr();

    /// @brief Test if the duplicate IPv4 host with can't be inserted.
517 518
    ///
    /// Uses gtest macros to report failures.
519
    void testAddDuplicate4();
520

521 522 523 524
    /// @brief Test that DHCPv4 options can be inserted and retrieved from
    /// the database.
    ///
    /// Uses gtest macros to report failures.
525 526 527 528
    ///
    /// @param formatted Boolean value indicating if the option values
    /// should be stored in the textual format in the database.
    void testOptionsReservations4(const bool formatted);
529 530 531 532 533

    /// @brief Test that DHCPv6 options can be inserted and retrieved from
    /// the database.
    ///
    /// Uses gtest macros to report failures.
534 535 536 537
    ///
    /// @param formatted Boolean value indicating if the option values
    /// should be stored in the textual format in the database.
    void testOptionsReservations6(const bool formatted);
538 539 540 541 542

    /// @brief Test that DHCPv4 and DHCPv6 options can be inserted and retrieved
    /// with a single query to the database.
    ///
    /// Uses gtest macros to report failures.
543 544 545 546
    ///
    /// @param formatted Boolean value indicating if the option values
    /// should be stored in the textual format in the database.
    void testOptionsReservations46(const bool formatted);
547

548 549 550 551 552 553 554 555 556 557 558 559 560 561
    /// @brief Test that multiple client classes for IPv4 can be inserted and
    /// retrieved for a given host reservation.
    ///
    /// Uses gtest macros to report failures.
    ///
    void testMultipleClientClasses4();

    /// @brief Test that multiple client classes for IPv6 can be inserted and
    /// retrieved for a given host reservation.
    ///
    /// Uses gtest macros to report failures.
    ///
    void testMultipleClientClasses6();

562
    /// @brief Test that multiple client classes for both IPv4 and IPv6 can
563 564 565 566 567 568
    /// be inserted and retrieved for a given host reservation.
    ///
    /// Uses gtest macros to report failures.
    ///
    void testMultipleClientClassesBoth();

569 570 571 572 573 574
    /// @brief Test that siaddr, sname, file fields can be retrieved
    /// from a database for a host.
    ///
    /// Uses gtest macros to report failures.
    void testMessageFields4();

575 576 577 578 579 580 581 582 583 584
    /// @brief Tests that delete(subnet-id, addr4) call works.
    ///
    /// Uses gtest macros to report failures.
    void testDeleteByAddr4();

    /// @brief Tests that delete(subnet4-id, identifier-type, identifier) works.
    ///
    /// Uses gtest macros to report failures.
    void testDeleteById4();

585 586 587
    /// @brief Tests that delete(subnet4-id, id-type, id) also deletes options.
    void testDeleteById4Options();

588 589 590 591 592
    /// @brief Tests that delete(subnet6-id, identifier-type, identifier) works.
    ///
    /// Uses gtest macros to report failures.
    void testDeleteById6();

593 594 595 596 597
    /// @brief Tests that delete(subnet6-id, id-type, id) also deletes options.
    ///
    /// Uses gtest macros to report failures.
    void testDeleteById6Options();

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    /// @brief Returns DUID with identical content as specified HW address
    ///
    /// This method does not have any sense in real life and is only useful
    /// in testing corner cases in the database backends (e.g. whether the DB
    /// is able to tell the difference between hwaddr and duid)
    ///
    /// @param hwaddr hardware address to be copied
    /// @return duid with the same value as specified HW address
    DuidPtr HWAddrToDuid(const HWAddrPtr& hwaddr);

    /// @brief Returns HW address with identical content as specified DUID
    ///
    /// This method does not have any sense in real life and is only useful
    /// in testing corner cases in the database backends (e.g. whether the DB
    /// is able to tell the difference between hwaddr and duid)
    ///
    /// @param duid DUID to be copied
    /// @return HW address with the same value as specified DUID
    HWAddrPtr DuidToHWAddr(const DuidPtr& duid);
617

618 619
};

620 621 622
}  // namespace test
}  // namespace dhcp
}  // namespace isc
623 624

#endif