alloc_engine.h 80.8 KB
Newer Older
1
// Copyright (C) 2012-2019 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 ALLOC_ENGINE_H
#define ALLOC_ENGINE_H

10
#include <asiolink/io_address.h>
11
#include <dhcp/classify.h>
12
#include <dhcpsrv/d2_client_cfg.h>
13
#include <dhcp/duid.h>
14
#include <dhcp/hwaddr.h>
15
#include <dhcp/pkt4.h>
16 17
#include <dhcp/pkt6.h>
#include <dhcp/option6_ia.h>
18 19
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_iaprefix.h>
20
#include <dhcpsrv/host.h>
21 22
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease_mgr.h>
23
#include <hooks/callout_handle.h>
24

25
#include <boost/function.hpp>
Tomek Mrugalski's avatar
Tomek Mrugalski committed
26
#include <boost/shared_ptr.hpp>
27 28
#include <boost/noncopyable.hpp>

29
#include <list>
30
#include <map>
31
#include <set>
32
#include <utility>
33

34 35 36 37 38 39 40 41
namespace isc {
namespace dhcp {

/// An exception that is thrown when allocation module fails (e.g. due to
/// lack of available addresses)
class AllocFailed : public isc::Exception {
public:

Tomek Mrugalski's avatar
Tomek Mrugalski committed
42 43 44 45 46 47 48
    /// @brief constructor
    ///
    /// @param file name of the file, where exception occurred
    /// @param line line of the file, where exception occurred
    /// @param what text description of the issue that caused exception
    AllocFailed(const char* file, size_t line, const char* what)
        : isc::Exception(file, line, what) {}
49 50 51 52
};

/// @brief DHCPv4 and DHCPv6 allocation engine
///
53
/// This class represents a DHCP allocation engine. It is responsible
54 55
/// for picking subnets, choosing and allocating a lease, extending,
/// renewing, releasing and possibly expiring leases.
56 57 58
///
/// @todo: Does not handle out of leases well
/// @todo: Does not handle out of allocation attempts well
59 60 61
class AllocEngine : public boost::noncopyable {
protected:

62 63 64 65
    /// @brief base class for all address/prefix allocation algorithms
    ///
    /// This is an abstract class that should not be used directly, but rather
    /// specialized implementations should be used instead.
66 67
    class Allocator {
    public:
68 69 70 71 72 73 74 75

        /// @brief picks one address out of available pools in a given subnet
        ///
        /// This method returns one address from the available pools in the
        /// specified subnet. It should not check if the address is used or
        /// reserved - AllocEngine will check that and will call pickAddress
        /// again if necessary. The number of times this method is called will
        /// increase as the number of available leases will decrease.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
76
        ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
77 78 79 80 81 82 83 84
        /// This method can also be used to pick a prefix. We should not rename
        /// it to pickLease(), because at this early stage there is no concept
        /// of a lease yet. Here it is a matter of selecting one address or
        /// prefix from the defined pool, without going into details who it is
        /// for or who uses it. I thought that pickAddress() is less confusing
        /// than pickResource(), because nobody would immediately know what the
        /// resource means in this context.
        ///
85 86
        /// Pools which are not allowed for client classes are skipped.
        ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
87
        /// @param subnet next address will be returned from pool of that subnet
88
        /// @param client_classes list of classes client belongs to
Tomek Mrugalski's avatar
Tomek Mrugalski committed
89 90 91 92
        /// @param duid Client's DUID
        /// @param hint client's hint
        ///
        /// @return the next address
93
        virtual isc::asiolink::IOAddress
94 95 96
        pickAddress(const SubnetPtr& subnet,
                    const ClientClasses& client_classes,
                    const DuidPtr& duid,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
97
                    const isc::asiolink::IOAddress& hint) = 0;
98

99 100 101
        /// @brief Default constructor.
        ///
        /// Specifies which type of leases this allocator will assign
Tomek Mrugalski's avatar
Tomek Mrugalski committed
102
        /// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
103
        Allocator(Lease::Type pool_type)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
104
            :pool_type_(pool_type) {
105 106
        }

107 108 109
        /// @brief virtual destructor
        virtual ~Allocator() {
        }
110
    protected:
111

112 113
        /// @brief defines pool type allocation
        Lease::Type pool_type_;
114 115
    };

116 117 118
    /// defines a pointer to allocator
    typedef boost::shared_ptr<Allocator> AllocatorPtr;

119 120
    /// @brief Address/prefix allocator that iterates over all addresses
    ///
121
    /// This class implements an iterative algorithm that returns all addresses in
Tomek Mrugalski's avatar
Tomek Mrugalski committed
122 123 124
    /// a pool iteratively, one after another. Once the last address is reached,
    /// it starts allocating from the beginning of the first pool (i.e. it loops
    /// over).
125 126
    class IterativeAllocator : public Allocator {
    public:
127 128 129 130

        /// @brief default constructor
        ///
        /// Does not do anything
131
        /// @param type - specifies allocation type
132
        IterativeAllocator(Lease::Type type);
133 134 135 136

        /// @brief returns the next address from pools in a subnet
        ///
        /// @param subnet next address will be returned from pool of that subnet
137
        /// @param client_classes list of classes client belongs to
138 139 140
        /// @param duid Client's DUID (ignored)
        /// @param hint client's hint (ignored)
        /// @return the next address
141
        virtual isc::asiolink::IOAddress
142
            pickAddress(const SubnetPtr& subnet,
143
                        const ClientClasses& client_classes,
144 145
                        const DuidPtr& duid,
                        const isc::asiolink::IOAddress& hint);
146
    protected:
147

Tomek Mrugalski's avatar
Tomek Mrugalski committed
148
        /// @brief Returns the next prefix
149
        ///
150
        /// This method works for IPv6 addresses only. It increases the
151 152 153 154 155 156 157
        /// specified prefix by a given prefix_len. For example, 2001:db8::
        /// increased by prefix length /32 will become 2001:db9::. This method
        /// is used to iterate over IPv6 prefix pools
        ///
        /// @param prefix prefix to be increased
        /// @param prefix_len length of the prefix to be increased
        /// @return result prefix
Tomek Mrugalski's avatar
Tomek Mrugalski committed
158
        static isc::asiolink::IOAddress
159
        increasePrefix(const isc::asiolink::IOAddress& prefix,
Tomek Mrugalski's avatar
Tomek Mrugalski committed
160
                       const uint8_t prefix_len);
161 162 163 164 165 166 167 168 169 170 171 172 173 174

        /// @brief Returns the next address or prefix
        ///
        /// This method works for IPv4 addresses, IPv6 addresses and
        /// IPv6 prefixes.
        ///
        /// @param address address or prefix to be increased
        /// @param prefix true when the previous argument is a prefix
        /// @param prefix_len length of the prefix
        /// @return result address or prefix
        static isc::asiolink::IOAddress
        increaseAddress(const isc::asiolink::IOAddress& address,
                        bool prefix, const uint8_t prefix_len);

175 176
    };

177 178
    /// @brief Address/prefix allocator that gets an address based on a hash
    ///
179
    /// @todo: This is a skeleton class for now and is missing an implementation.
180
    class HashedAllocator : public Allocator {
181 182
    public:

183
        /// @brief default constructor (does nothing)
184
        /// @param type - specifies allocation type
185
        HashedAllocator(Lease::Type type);
186 187 188 189 190 191

        /// @brief returns an address based on hash calculated from client's DUID.
        ///
        /// @todo: Implement this method
        ///
        /// @param subnet an address will be picked from pool of that subnet
192
        /// @param client_classes list of classes client belongs to
193 194 195
        /// @param duid Client's DUID
        /// @param hint a hint (last address that was picked)
        /// @return selected address
196 197 198 199 200
        virtual isc::asiolink::IOAddress
            pickAddress(const SubnetPtr& subnet,
                        const ClientClasses& client_classes,
                        const DuidPtr& duid,
                        const isc::asiolink::IOAddress& hint);
201 202 203 204
    };

    /// @brief Random allocator that picks address randomly
    ///
205
    /// @todo: This is a skeleton class for now and is missing an implementation.
206
    class RandomAllocator : public Allocator {
207
    public:
208 209

        /// @brief default constructor (does nothing)
210
        /// @param type - specifies allocation type
211
        RandomAllocator(Lease::Type type);
212

213
        /// @brief returns a random address from pool of specified subnet
214 215 216 217
        ///
        /// @todo: Implement this method
        ///
        /// @param subnet an address will be picked from pool of that subnet
218
        /// @param client_classes list of classes client belongs to
219 220 221
        /// @param duid Client's DUID (ignored)
        /// @param hint the last address that was picked (ignored)
        /// @return a random address from the pool
222
        virtual isc::asiolink::IOAddress
223 224 225
        pickAddress(const SubnetPtr& subnet,
                    const ClientClasses& client_classes,
                    const DuidPtr& duid,
226 227
                    const isc::asiolink::IOAddress& hint);
    };
228

229
public:
230 231 232 233 234 235 236 237

    /// @brief specifies allocation type
    typedef enum {
        ALLOC_ITERATIVE, // iterative - one address after another
        ALLOC_HASHED,    // hashed - client's DUID/client-id is hashed
        ALLOC_RANDOM     // random - an address is randomly selected
    } AllocType;

238
    /// @brief Constructor.
239
    ///
240 241 242 243
    /// Instantiates necessary services, required to run DHCP server.
    /// In particular, creates IfaceMgr that will be responsible for
    /// network interaction. Will instantiate lease manager, and load
    /// old or create new DUID.
244
    ///
245 246 247 248
    /// @param engine_type selects allocation algorithm
    /// @param attempts number of attempts for each lease allocation before
    ///        we give up (0 means unlimited)
    /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
249
    AllocEngine(AllocType engine_type, uint64_t attempts, bool ipv6 = true);
250

251 252
    /// @brief Destructor.
    virtual ~AllocEngine() { }
253

254 255 256 257
    /// @brief Returns allocator for a given pool type
    ///
    /// @param type type of pool (V4, IA, TA or PD)
    /// @throw BadValue if allocator for a given type is missing
258
    /// @return pointer to allocator handling a given resource types
259
    AllocatorPtr getAllocator(Lease::Type type);
260

261
private:
262

263 264 265 266 267
    /// @brief a pointer to currently used allocator
    ///
    /// For IPv4, there will be only one allocator: TYPE_V4
    /// For IPv6, there will be 3 allocators: TYPE_NA, TYPE_TA, TYPE_PD
    std::map<Lease::Type, AllocatorPtr> allocators_;
268

269
    /// @brief number of attempts before we give up lease allocation (0=unlimited)
270
    uint64_t attempts_;
271

272 273 274
    // hook name indexes (used in hooks callouts)
    int hook_index_lease4_select_; ///< index for lease4_select hook
    int hook_index_lease6_select_; ///< index for lease6_select hook
275

276
public:
277

278
    /// @brief Defines a single hint
279 280 281
    ///
    /// This is an entry that represents what the client had requested,
    /// either an address or a prefix. Prefix length is 128 for regular
282 283 284 285 286 287 288 289 290 291 292
    /// addresses. Optionally it provides wanted preferred and valid
    /// lifetimes.
    ///
    /// @note Seems to be used only for DHCPv6.
    class Resource {
    public:

        /// @brief Default constructor.
        ///
        /// @param address the address or prefix
        /// @param prefix_len the prefix length (128 for addresses)
293 294 295 296
        /// @param preferred the optional preferred lifetime,
        /// defaults to 0, meaning not specified
        /// @param valid the optional valid lifetime,
        /// defaults to 0, meaning not specified
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
        Resource(const isc::asiolink::IOAddress& address,
                 const uint8_t prefix_len,
                 const uint32_t preferred = 0,
                 const uint32_t valid = 0)
            : address_(address), prefix_len_(prefix_len),
              preferred_(preferred), valid_(valid) {
        }

        /// @brief Returns the address.
        ///
        /// @return the address or prefix
        isc::asiolink::IOAddress getAddress() const {
            return (address_);
        }

        /// @brief Returns the prefix length.
        ///
        /// @return the prefix length
        uint8_t getPrefixLength() const {
            return (prefix_len_);
        }

        /// @brief Returns the optional preferred lifetime.
        ///
        /// @return the preferred lifetime (0 if not set)
        uint32_t getPreferred() const {
            return (preferred_);
        }

        /// @brief Returns the optional valid lifetime.
        ///
        /// @return the valid lifetime (0 if not set)
        uint32_t getValid() const {
            return (valid_);
        }

        /// @brief Compares two @c AllocEngine::Resource objects for equality.
        ///
        /// @param other object to be compared with this object
        /// @return true if objects are equal, false otherwise.
        bool equals(const Resource& other) const {
            return (address_ == other.address_ &&
                    prefix_len_ == other.prefix_len_);
        }

        /// @brief Equality operator.
        ///
        /// @param other object to be compared with this object
        /// @return true if objects are equal, false otherwise.
        bool operator==(const Resource& other) const {
            return (equals(other));
        }

    protected:

        /// @brief The address or prefix.
        isc::asiolink::IOAddress address_;

        /// @brief The prefix length (128 for an address).
        uint8_t prefix_len_;

        /// @brief The preferred lifetime (0 when not set).
        uint32_t preferred_;

        /// @brief The valid lifetime (0 when not set).
        uint32_t valid_;
    };

    /// @brief Resource compare class.
    ///
    /// Needed for using sets of Resource objets.
    struct ResourceCompare {
        /// @brief Compare operator
        ///
        /// @note Only the address/prefix part of resources is used.
        /// @param lhr Left hand resource objet
        /// @param rhr Right hand resource objet
        /// @return true if lhr is less than rhr, false otherwise
        bool operator() (const Resource& lhr, const Resource& rhr) const {
            if (lhr.getAddress() == rhr.getAddress()) {
                return (lhr.getPrefixLength() < rhr.getPrefixLength());
            } else {
                return (lhr.getAddress() < rhr.getAddress());
            }
        }
    };
383 384

    /// @brief Container for client's hints.
385
    typedef std::vector<Resource> HintContainer;
386

387
    /// @brief Container holding allocated prefixes or addresses.
388
    typedef std::set<Resource, ResourceCompare> ResourceContainer;
389

390 391 392 393
    /// @brief A tuple holding host identifier type and value.
    typedef std::pair<Host::IdentifierType, std::vector<uint8_t> > IdentifierPair;

    /// @brief Map holding values to be used as host identifiers.
394
    typedef std::list<IdentifierPair> IdentifierList;
395

396 397 398 399 400 401 402 403
    /// @brief Context information for the DHCPv6 leases allocation.
    ///
    /// This structure holds a set of information provided by the DHCPv6
    /// server to the allocation engine. In particular, it holds the
    /// client identifying information, such as HW address or client
    /// identifier. It also holds the information about the subnet that
    /// the client is connected to.
    ///
404
    /// This structure is also used to pass some information from
405 406 407 408 409 410 411 412 413 414 415 416 417
    /// the allocation engine back to the server, i.e. the old leases
    /// which the client had before the allocation.
    ///
    /// This structure is expected to be common for a single client, even
    /// if multiple IAs are used. Some of the fields will need to be
    /// updated for every call (there's a separate call to the allocation
    /// engine for each IA option).
    ///
    /// This structure is meant to be extended in the future, if more
    /// information should be passed to the allocation engine. Note
    /// that the big advantage of using the context structure to pass
    /// information to the allocation engine methods is that adding
    /// new information doesn't modify the API of the allocation engine.
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
    struct ClientContext6 : public boost::noncopyable {

        /// @name Parameters pertaining to DHCPv6 message
        //@{

        /// @brief A pointer to the client's message
        ///
        /// This is used exclusively for hook purposes.
        Pkt6Ptr query_;

        /// @brief Indicates if this is a real or fake allocation.
        ///
        /// The real allocation is when the allocation engine is supposed
        /// to make an update in a lease database: create new lease, or
        /// update existing lease.
        bool fake_allocation_;

435 436 437
        /// @brief Subnet selected for the client by the server.
        Subnet6Ptr subnet_;

438 439 440 441 442
        /// @brief Subnet from which host reservations should be retrieved.
        ///
        /// It can be NULL, in which case @c subnet_ value is used.
        Subnet6Ptr host_subnet_;

443 444 445 446 447 448
        /// @brief Client identifier
        DuidPtr duid_;

        /// @brief Hardware/MAC address (if available, may be NULL)
        HWAddrPtr hwaddr_;

449 450 451 452
        /// @brief A list holding host identifiers extracted from a message
        /// received by the server.
        IdentifierList host_identifiers_;

453 454
        /// @brief Holds a map of hosts belonging to the client within different
        /// subnets.
455
        ///
456 457 458
        /// Multiple hosts may appear when the client belongs to a shared
        /// network.
        std::map<SubnetID, ConstHostPtr> hosts_;
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

        /// @brief A boolean value which indicates that server takes
        ///        responsibility for the forward DNS Update for this lease
        ///        (if true).
        bool fwd_dns_update_;

        /// @brief A boolean value which indicates that server takes
        ///        responsibility for the reverse DNS Update for this lease
        ///        (if true).
        bool rev_dns_update_;

        /// @brief Hostname.
        ///
        /// The server retrieves the hostname from the Client FQDN option,
        /// Hostname option or the host reservation record for the client.
        std::string hostname_;

        /// @brief Callout handle associated with the client's message.
        hooks::CalloutHandlePtr callout_handle_;

479 480 481
        /// @brief Holds addresses and prefixes allocated for all IAs.
        ResourceContainer allocated_resources_;

482 483 484
        /// @brief A collection of newly allocated leases.
        Lease6Collection new_leases_;

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
        //@}

        /// @brief Parameters pertaining to individual IAs.
        struct IAContext {

            /// @brief iaid IAID field from IA_NA or IA_PD that is being
            /// processed
            uint32_t iaid_;

            /// @brief Lease type (IA or PD)
            Lease::Type type_;

            /// @brief client's hints
            ///
            /// There will typically be just one address, but the protocol
            /// allows more than one address or prefix for each IA container.
501
            HintContainer hints_;
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
            /// @brief A pointer to any old leases that the client had before
            /// update but are no longer valid after the update/allocation.
            ///
            /// This collection is typically empty, except cases when we are
            /// doing address reassignment, e.g. because there is a host
            /// reservation that gives this address to someone else, so we had
            /// to return the address, and give a new one to this client.
            Lease6Collection old_leases_;

            /// @brief A pointer to any leases that have changed FQDN
            /// information.
            ///
            /// This list may contain old versions of the leases that are still
            /// valid. In particular, it will contain a lease if the client's
            /// FQDN has changed.
            Lease6Collection changed_leases_;

            /// @brief A pointer to the IA_NA/IA_PD option to be sent in
            /// response
            Option6IAPtr ia_rsp_;

            /// @brief Default constructor.
            ///
            /// Initializes @ref type_ to @c Lease::TYPE_NA and @ref iaid_ to 0.
            IAContext();

            /// @brief Convenience method adding new hint.
            ///
            /// @param prefix Prefix or address.
            /// @param prefix_len Prefix length. Default is 128 for addresses.
533 534
            /// @param preferred Wanted preferred lifetime. Default 0.
            /// @param valid Wanted valid lifetime. Default 0.
535
            void addHint(const asiolink::IOAddress& prefix,
536 537 538 539 540 541 542
                         const uint8_t prefix_len = 128,
                         const uint32_t preferred = 0,
                         const uint32_t valid = 0);

            /// @brief Convenience method adding new hint from IAADDR option.
            ///
            /// @param iaaddr Pointer to IAADDR.
543
            /// @throw BadValue if iaaddr is null.
544 545 546 547 548
            void addHint(const Option6IAAddrPtr& iaaddr);

            /// @brief Convenience method adding new hint from IAPREFIX option.
            ///
            /// @param iaprefix Pointer to IAPREFIX.
549
            /// @throw BadValue if iaprefix is null.
550
            void addHint(const Option6IAPrefixPtr& iaprefix);
551 552
        };

553
        /// @brief Container holding IA specific contexts.
554
        std::vector<IAContext> ias_;
555

556 557 558 559 560 561 562 563
        /// @brief Returns the set of DDNS behavioral parameters based on
        /// the selected subnet.
        ///
        /// If there is no selected subnet (i.e. subnet_ is empty), the
        /// returned set will cotain default values.
        ///
        /// @return pointer to a DdnsParams instance
        DdnsParamsPtr getDdnsParams();
564

565 566 567 568 569 570 571 572 573 574 575 576 577 578
        /// @brief Convenience method adding allocated prefix or address.
        ///
        /// @param prefix Prefix or address.
        /// @param prefix_len Prefix length. Default is 128 for addresses.
        void addAllocatedResource(const asiolink::IOAddress& prefix,
                                  const uint8_t prefix_len = 128);

        /// @brief Checks if specified address or prefix was allocated.
        ///
        /// @param prefix Prefix or address.
        /// @param prefix_len Prefix length. Default is 128 for addresses.
        bool isAllocated(const asiolink::IOAddress& prefix,
                         const uint8_t prefix_len = 128) const;

Francis Dupont's avatar
Francis Dupont committed
579
        /// @brief Convenience function adding host identifier into
580 581 582 583 584 585 586 587
        /// @ref host_identifiers_ list.
        ///
        /// @param id_type Identifier type.
        /// @param identifier Identifier value.
        void addHostIdentifier(const Host::IdentifierType& id_type,
                               const std::vector<uint8_t>& identifier) {
            host_identifiers_.push_back(IdentifierPair(id_type, identifier));
        }
588

589 590 591 592 593
        /// @brief Returns IA specific context for the currently processed IA.
        ///
        /// If IA specific context doesn't exist, it is created.
        ///
        /// @return Reference to IA specific context.
594 595 596 597 598 599 600
        IAContext& currentIA() {
            if (ias_.empty()) {
                createIAContext();
            }
            return (ias_.back());
        }

601 602 603 604
        /// @brief Creates new IA context.
        ///
        /// This method should be invoked prior to processing a next IA included
        /// in the client's message.
605 606 607 608
        void createIAContext() {
            ias_.push_back(IAContext());
        };

609
        /// @brief Returns host from the most preferred subnet.
610 611 612 613
        ///
        /// @return Pointer to the host object.
        ConstHostPtr currentHost() const;

614 615 616 617
        /// @brief Returns global host reservation if there is one
        ///
        /// If the current subnet's reservation mode is global and
        /// there is a global host (i.e. reservation belonging to
618
        /// the global subnet), return it.  Otherwise return an
619 620 621 622 623 624 625 626 627 628 629 630
        /// empty pointer.
        ///
        /// @return Pointer to the host object.
        ConstHostPtr globalHost() const;

        /// @brief Determines if a global reservation exists
        ///
        /// @return true if there current subnet's reservation mode is
        /// global and there is global host containing the given
        /// lease reservation, false otherwise
        bool hasGlobalReservation(const IPv6Resrv& resv) const;

631
        /// @brief Default constructor.
632
        ClientContext6();
633 634 635

        /// @brief Constructor with parameters.
        ///
636
        /// Note that several less frequently used parameters (callout_handle,
637 638 639 640 641 642 643 644 645 646 647 648
        /// old_leases, host) fields are not set. They should be set explicitly,
        /// if needed.
        ///
        /// @param subnet subnet the allocation should come from
        /// @param duid Client's DUID
        /// @param fwd_dns A boolean value which indicates that server takes
        ///        responsibility for the forward DNS Update for this lease
        ///        (if true).
        /// @param rev_dns A boolean value which indicates that server takes
        ///        responsibility for the reverse DNS Update for this lease
        ///        (if true).
        /// @param hostname A fully qualified domain-name of the client.
649 650 651 652 653 654
        /// @param fake_allocation is this real i.e. REQUEST (false) or just
        ///        picking an address for SOLICIT that is not really allocated
        ///        (true)
        /// @param query Pointer to the DHCPv6 message being processed.
        /// @param callout_handle Callout handle associated with a client's
        ///        message
655
        ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
656 657 658 659 660
                       const bool fwd_dns, const bool rev_dns,
                       const std::string& hostname, const bool fake_allocation,
                       const Pkt6Ptr& query,
                       const hooks::CalloutHandlePtr& callout_handle =
                       hooks::CalloutHandlePtr());
661 662 663 664 665 666

        private:
            /// @brief Contains a pointer to the DDNS parameters for selected
            /// subnet.  Set by the first call to getDdnsParams() made when
            /// the context has a selected subnet (i.e. subnet_ is not empty).
            DdnsParamsPtr ddns_params_;
667 668 669 670
    };

    /// @brief Allocates IPv6 leases for a given IA container
    ///
Francis Dupont's avatar
Francis Dupont committed
671
    /// This method uses the currently selected allocator to pick allocable
672 673 674 675 676
    /// resources (i.e. addresses or prefixes) from specified subnet, creates
    /// a lease (one or more, if needed) for that resources and then inserts
    /// it into LeaseMgr (if this allocation is not fake, i.e. this is not a
    /// response to SOLICIT).
    ///
Francis Dupont's avatar
Francis Dupont committed
677
    /// This method uses host reservation if ctx.hosts_ is set. The easy way to
678
    /// set it is to call @ref findReservationDecl.
679 680
    /// The host reservation is convenient, but incurs performance penalty,
    /// so it can be tweaked on a per subnet basis. There are three possible modes:
681 682 683 684 685 686 687 688 689 690 691 692 693
    /// 1. disabled (no host reservation at all). This is the most performant one
    /// as the code can skip all checks;
    /// 2. out-of-pool (only reservations that are outside
    /// of the dynamic pools are allowed. This is a compromise - it requires
    /// a sysadmin to be more careful with the reservations, but the code
    /// can skip reservation checks while managing in-pool addresses);
    /// 3. in-pool (which also allow out-of-pool; this is the most flexible
    /// mode, but it means that the allocation engine has to do reservation
    /// checks on every lease, even those dynamically assigned, which degrades
    /// performance).
    ///
    /// The logic in this method is as follows:
    /// -# Case 1. if there are no leases, and there are reservations...
694
    ///    Are the reserved addresses/prefixes used by someone else?
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
    ///   -# yes: we have a problem. We can't assign the reserved address yet,
    ///       because it is used by someone else. We can't immediately release
    ///       the lease as there is some other client that is currently using it.
    ///       We will temporarily assign a different, unreserved lease for this
    ///       client. In the mean time, the other client will hopefully get back
    ///       to us, so we could revoke his lease.
    ///   -# no: assign them => done
    /// -# Case 2. if there are leases and there are no reservations...
    ///    Are the leases reserved for someone else?
    ///    -# yes: release them, assign something else
    ///    -#  no: renew them => done
    /// -# Case 3. if there are leases and there are reservations...
    ///    Are the leases matching reservations?
    ///   -# yes: renew them => done
    ///   -#  no: release existing leases, assign new ones based on reservations
    /// -# Case 4. if there are no leases and no reservations...
    ///       assign new leases (this is the "normal" case when the reservations
    ///       are disabled).
    ///
714 715 716 717 718
    /// @param ctx client context that passes all necessary information. See
    ///        @ref ClientContext6 for details.
    ///
    /// The following fields of ClientContext6 are used:
    ///
719 720
    /// @ref ClientContext6::subnet_ subnet the allocation should
    ///        come from<br/>
721
    /// @ref ClientContext6::duid_ Client's DUID<br/>
722
    /// @ref ClientContext6::IAContext::iaid_ iaid field from the IA_NA container
723
    ///        that client sent<br/>
724 725
    /// @ref ClientContext6::IAContext::hints_ a hint that the client provided<br/>
    /// @ref ClientContext6::IAContext::type_ lease type (IA, TA or PD)<br/>
726 727
    /// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates
    ///        that server takes responsibility for the forward DNS Update
728
    ///        for this lease (if true).<br/>
729 730
    /// @ref ClientContext6::rev_dns_update_ A boolean value which indicates
    ///        that server takes responsibility for the reverse DNS Update for
731 732
    ///        this lease (if true).<br/>
    /// @ref ClientContext6::hostname_ A fully qualified domain-name of the client.<br/>
733 734
    /// @ref ClientContext6::fake_allocation_ is this real i.e. REQUEST (false)
    ///        or just picking an address for SOLICIT that is not really
735
    ///        allocated (true)<br/>
736
    /// @ref ClientContext6::callout_handle_ a callout handle (used in hooks). A
737
    ///        lease callouts will be executed if this parameter is passed.<br/>
738 739
    /// @ref ClientContext6::IAContext::old_leases_ [out] Collection to which this
    ///        function
740 741
    ///        will append old leases. Leases are stored in the same order as in
    ///        the collection of new leases, being returned. For newly allocated
742
    ///        leases (not renewed) the NULL pointers are stored in this
743
    ///        collection as old leases.<br/>
744
    /// @ref ClientContext6::hwaddr_ Hardware address (optional, may be null if
745
    ///        not available)<br/>
Francis Dupont's avatar
Francis Dupont committed
746 747
    /// @ref ClientContext6::hosts_ Host reservations. allocateLeases6 will set
    ///        this field, if appropriate reservations are found.
748
    ///
749 750
    /// @return Allocated IPv6 leases (may be empty if allocation failed)
    Lease6Collection
751
    allocateLeases6(ClientContext6& ctx);
752

753 754 755 756 757 758 759 760 761 762
    /// @brief Renews existing DHCPv6 leases for a given IA.
    ///
    /// This method updates the leases associated with a specified IA container.
    /// It will extend the leases under normal circumstances, but sometimes
    /// there may be reasons why not to do so. Such a reasons may be:
    /// - client attempts to renew an address that is not valid
    /// - client attempts to renew an address that is now reserved for someone
    ///   else (see host reservation)
    /// - client's leases does not match his reservations
    ///
763
    /// This method will call the lease6_renew callout.
764 765 766
    ///
    /// @param ctx Message processing context. It holds various information
    /// extracted from the client's message and required to allocate a lease.
767 768 769 770
    /// In particular, @ref ClientContext6::IAContext::hints_ provides list
    /// of addresses or
    /// prefixes the client had sent. @ref ClientContext6::IAContext::old_leases_
    /// will contain removed leases in this case.
771 772
    ///
    /// @return Returns renewed lease.
773
    Lease6Collection renewLeases6(ClientContext6& ctx);
774

775
    /// @brief Reclaims expired IPv6 leases.
776 777
    ///
    /// This method retrieves a collection of expired leases and reclaims them.
778 779
    /// See
    /// https://gitlab.isc.org/isc-projects/kea/wikis/designs/lease-expiration#leases-reclamation-routine
780 781
    /// for the details.
    ///
782 783 784 785 786
    /// This method is executed periodically to act upon expired leases. This
    /// includes for each lease:
    /// - executing "lease_expire6" hook,
    /// - removing DNS record for a lease,
    /// - reclaiming a lease in the database, i.e. setting its state to
Francis Dupont's avatar
Francis Dupont committed
787
    ///   "expired-reclaimed" or removing it from the lease database,
788 789
    /// - updating statistics of assigned and reclaimed leases
    ///
Francis Dupont's avatar
Francis Dupont committed
790
    /// Note: declined leases fall under the same expiration/reclamation
791
    /// processing as normal leases. In principle, it would be more elegant
Francis Dupont's avatar
Francis Dupont committed
792
    /// to have a separate processing for declined leases reclamation. However,
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
    /// due to performance reasons we decided to use them together. Several
    /// aspects were taken into consideration. First, normal leases are expected
    /// to expire frequently, so in a typical deployment this method will have
    /// some leases to process. Second, declined leases are expected to be very
    /// rare event, so in most cases there won't be any declined expired leases.
    /// Third, the calls to LeaseMgr to obtain all leases of specific expiration
    /// criteria are expensive, so it is better to have one call rather than
    /// two, especially if one of those calls is expected to usually return no
    /// leases.
    ///
    /// It doesn't make sense to retain declined leases that are reclaimed,
    /// because those leases don't contain any useful information (all client
    /// identifying information was stripped when the leave was moved to the
    /// declined state). Therefore remove_leases parameter is ignored for
    /// declined leases. They are always removed.
    ///
809
    /// Also, for declined leases @ref reclaimDeclinedLease6 is
810 811
    /// called. It conducts several declined specific operation (extra log
    /// entry, stats dump, hooks).
812
    ///
813
    /// @param max_leases Maximum number of leases to be reclaimed.
Francis Dupont's avatar
Francis Dupont committed
814
    /// @param timeout Maximum amount of time that the reclamation routine
815
    /// may be processing expired leases, expressed in milliseconds.
816 817 818
    /// @param remove_lease A boolean value indicating if the lease should
    /// be removed when it is reclaimed (if true) or it should be left in the
    /// database in the "expired-reclaimed" state (if false).
819 820 821 822
    /// @param max_unwarned_cycles A number of consecutive processing cycles
    /// of expired leases, after which the system issues a warning if there
    /// are still expired leases in the database. If this value is 0, the
    /// warning is never issued.
823
    void reclaimExpiredLeases6(const size_t max_leases, const uint16_t timeout,
824 825
                               const bool remove_lease,
                               const uint16_t max_unwarned_cycles = 0);
826

827 828 829 830 831 832
    /// @brief Deletes reclaimed leases expired more than specified amount
    /// of time ago.
    ///
    /// @param secs Minimum number of seconds after which the lease can be
    /// deleted.
    void deleteExpiredReclaimedLeases6(const uint32_t secs);
833 834 835 836

    /// @brief Reclaims expired IPv4 leases.
    ///
    /// This method retrieves a collection of expired leases and reclaims them.
837 838
    /// See
    /// https://gitlab.isc.org/isc-projects/kea/wikis/designs/lease-expiration#leases-reclamation-routine
839 840
    /// for the details.
    ///
841 842
    /// This method is executed periodically to act upon expired leases. This
    /// includes for each lease:
843
    /// - executing "lease_expire4" hook,
844 845
    /// - removing DNS record for a lease,
    /// - reclaiming a lease in the database, i.e. setting its state to
Francis Dupont's avatar
Francis Dupont committed
846
    ///   "expired-reclaimed" or removing it from the lease database,
847 848
    /// - updating statistics of assigned and reclaimed leases
    ///
Francis Dupont's avatar
Francis Dupont committed
849
    /// Note: declined leases fall under the same expiration/reclamation
850
    /// processing as normal leases. In principle, it would be more elegant
Francis Dupont's avatar
Francis Dupont committed
851
    /// to have a separate processing for declined leases reclamation. However,
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
    /// due to performance reasons we decided to use them together. Several
    /// aspects were taken into consideration. First, normal leases are expected
    /// to expire frequently, so in a typical deployment this method will have
    /// some leases to process. Second, declined leases are expected to be very
    /// rare event, so in most cases there won't be any declined expired leases.
    /// Third, the calls to LeaseMgr to obtain all leases of specific expiration
    /// criteria are expensive, so it is better to have one call rather than
    /// two, especially if one of those calls is expected to usually return no
    /// leases.
    ///
    /// It doesn't make sense to retain declined leases that are reclaimed,
    /// because those leases don't contain any useful information (all client
    /// identifying information was stripped when the leave was moved to the
    /// declined state). Therefore remove_leases parameter is ignored for
    /// declined leases. They are always removed.
    ///
868
    /// Also, for declined leases @ref reclaimDeclinedLease4 is
Francis Dupont's avatar
Francis Dupont committed
869
    /// called. It conducts several declined specific operation (extra log
870
    /// entry, stats dump, hooks).
871
    ///
872
    /// @param max_leases Maximum number of leases to be reclaimed.
Francis Dupont's avatar
Francis Dupont committed
873
    /// @param timeout Maximum amount of time that the reclamation routine
874
    /// may be processing expired leases, expressed in milliseconds.
875 876 877
    /// @param remove_lease A boolean value indicating if the lease should
    /// be removed when it is reclaimed (if true) or it should be left in the
    /// database in the "expired-reclaimed" state (if false).
878 879 880 881
    /// @param max_unwarned_cycles A number of consecutive processing cycles
    /// of expired leases, after which the system issues a warning if there
    /// are still expired leases in the database. If this value is 0, the
    /// warning is never issued.
882
    void reclaimExpiredLeases4(const size_t max_leases, const uint16_t timeout,
883 884
                               const bool remove_lease,
                               const uint16_t max_unwarned_cycles = 0);
885

886 887 888 889 890 891 892
    /// @brief Deletes reclaimed leases expired more than specified amount
    /// of time ago.
    ///
    /// @param secs Minimum number of seconds after which the lease can be
    /// deleted.
    void deleteExpiredReclaimedLeases4(const uint32_t secs);

893

894
    /// @anchor findReservationDecl
895 896 897 898 899
    /// @brief Attempts to find appropriate host reservation.
    ///
    /// Attempts to find appropriate host reservation in HostMgr. If found, it
    /// will be set in ctx.host_.
    /// @param ctx Client context that contains all necessary information.
900
    static void findReservation(ClientContext6& ctx);
901

902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920
    /// @brief Attempts to find the host reservation for the client.
    ///
    /// This method attempts to find a "global" host reservation matching the
    /// client identifier.  It will return the first global reservation that
    /// matches per the configured list of host identifiers, or an empty
    /// pointer if no matches are found.
    ///
    /// @param ctx Client context holding various information about the client.
    /// @return Pointer to the reservation found, or an empty pointer.
    static ConstHostPtr findGlobalReservation(ClientContext6& ctx);

    /// @brief Creates an IPv6Resrv instance from a Lease6
    ///
    /// @param lease Reference to the Lease6
    /// @return The newly formed IPv6Resrv instance
    static IPv6Resrv makeIPv6Resrv(const Lease6& lease) {
        if (lease.type_ == Lease::TYPE_NA) {
            return (IPv6Resrv(IPv6Resrv::TYPE_NA, lease.addr_,
                              (lease.prefixlen_ ? lease.prefixlen_ : 128)));
921
        }
922 923 924 925

        return (IPv6Resrv(IPv6Resrv::TYPE_PD, lease.addr_, lease.prefixlen_));
    }

926 927
private:

928
    /// @brief creates a lease and inserts it in LeaseMgr if necessary
929 930
    ///
    /// Creates a lease based on specified parameters and tries to insert it
931
    /// into the database. That may fail in some cases, i.e. when there is another
932 933
    /// allocation process and we lost a race to a specific lease.
    ///
934 935
    /// @param ctx client context that passes all necessary information. See
    ///        @ref ClientContext6 for details.
936
    /// @param addr an address that was selected and is confirmed to be
937
    ///        available
938
    /// @param prefix_len length of the prefix (for PD only)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
939
    ///        should be 128 for other lease types
940
    /// @param [out] callout_status callout returned by the lease6_select
941 942 943 944 945 946 947
    ///
    /// The following fields of the ctx structure are used:
    /// @ref ClientContext6::subnet_ subnet the lease is allocated from
    /// @ref ClientContext6::duid_ client's DUID
    /// @ref ClientContext6::iaid_ IAID from the IA_NA container the client sent to us
    /// @ref ClientContext6::type_ lease type (IA, TA or PD)
    /// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates that server takes
948 949
    ///        responsibility for the forward DNS Update for this lease
    ///        (if true).
950
    /// @ref ClientContext6::rev_dns_update_ A boolean value which indicates that server takes
951 952
    ///        responsibility for the reverse DNS Update for this lease
    ///        (if true).
953 954 955
    /// @ref ClientContext6::hostname_ A fully qualified domain-name of the client.
    /// @ref ClientContext6::hwaddr_ Hardware address (optional, may be null for Lease6)
    /// @ref ClientContext6::callout_handle_ a callout handle (used in hooks). A lease callouts
Tomek Mrugalski's avatar
Tomek Mrugalski committed
956 957
    ///        will be executed if this parameter is passed (and there are callouts
    ///        registered)
958
    /// @ref ClientContext6::fake_allocation_ is this real i.e. REQUEST (false) or just picking
Tomek Mrugalski's avatar
Tomek Mrugalski committed
959
    ///        an address for SOLICIT that is not really allocated (true)
960
    /// @return allocated lease (or NULL in the unlikely case of the lease just
961
    ///         became unavailable)
962 963
    Lease6Ptr createLease6(ClientContext6& ctx,
                           const isc::asiolink::IOAddress& addr,
964 965
                           const uint8_t prefix_len,
                           hooks::CalloutHandle::CalloutNextStep& callout_status);
966

967 968 969 970 971 972 973 974 975 976 977 978 979
    /// @brief Allocates a normal, in-pool, unreserved lease from the pool.
    ///
    /// It attempts to pick a hint first, then uses allocator iteratively until
    /// an available (not used, not reserved) lease is found. In principle, it
    /// may return more than one lease, but we currently handle only one.
    /// This may change in the future.
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @return collection of newly allocated leases
    Lease6Collection allocateUnreservedLeases6(ClientContext6& ctx);

    /// @brief Creates new leases based on reservations.
    ///
980 981 982 983 984 985 986 987
    /// This method allcoates new leases,  based on host reservations.
    /// Existing leases are specified in the existing_leases parameter.
    /// It first calls @c allocateGlobalReservedLeases6 to accomodate
    /// subnets using global reservations.  If that method allocates
    /// addresses, we return, otherwise we continue and check for non-global
    /// reservations.  A new lease is not created, if there is a lease for
    /// specified address on existing_leases list or there is a lease used by
    /// someone else.
988 989 990 991 992 993
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @param existing_leases leases that are already associated with the client
    void
    allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases);

994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
    /// @brief Creates new leases based on global reservations.
    ///
    /// This method is used by @allocateReservedLeases6, to allocate new leases based
    /// on global reservation if one exists and global reservations are enabled for
    /// the selected subnet. It differs from it's caller by looking only at the global
    /// reservation and therefore has no need to iterate over the selected subnet or it's
    /// siblings looking for host reservations.  Like it's caller, existing leases are
    /// specified in existing_leases parameter. A new lease is not created, if there is
    /// a lease for specified address on existing_leases list or there is a lease used by
    /// someone else.
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @param existing_leases leases that are already associated with the client
1007 1008 1009
    bool
    allocateGlobalReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases);

1010 1011 1012
    /// @brief Removes leases that are reserved for someone else.
    ///
    /// Goes through the list specified in existing_leases and removes those that
1013 1014
    /// are reserved by someone else or do not belong to an allowed pool.
    /// The removed leases are added to the ctx.removed_leases_ collection.
1015 1016 1017 1018 1019 1020 1021
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @param existing_leases [in/out] leases that should be checked
    void
    removeNonmatchingReservedLeases6(ClientContext6& ctx,
                                     Lease6Collection& existing_leases);

1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
    /// @brief Removes leases that are reserved for someone else.
    ///
    /// Simplified version of removeNonmatchingReservedLeases6 to be
    /// used when host reservations are disabled.
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @param existing_leases [in/out] leases that should be checked
    void
    removeNonmatchingReservedNoHostLeases6(ClientContext6& ctx,
                                           Lease6Collection& existing_leases);

1033 1034 1035 1036 1037 1038
    /// @brief Removed leases that are not reserved for this client
    ///
    /// This method iterates over existing_leases and will remove leases that are
    /// not reserved for this client. It will leave at least one lease on the list,
    /// if possible. The reason to run this method is that if there is a reservation
    /// for address A for client X and client X already has a lease for a
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1039
    /// different address B, we should assign A and release B. However,
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
    /// if for some reason we can't assign A, keeping B would be better than
    /// not having a lease at all. Hence we may keep B if that's the only lease
    /// left.
    ///
    /// @param ctx client context that contains all details (subnet, client-id, etc.)
    /// @param existing_leases [in/out] leases that should be checked
    void
    removeNonreservedLeases6(ClientContext6& ctx,
                             Lease6Collection& existing_leases);

Tomek Mrugalski's avatar
Tomek Mrugalski committed
1050
    /// @brief Reuses expired IPv6 lease
1051 1052 1053 1054 1055 1056
    ///
    /// Updates existing expired lease with new information. Lease database
    /// is updated if this is real (i.e. REQUEST, fake_allocation = false), not
    /// dummy allocation request (i.e. SOLICIT, fake_allocation = true).
    ///
    /// @param expired old, expired lease
1057
    /// @param ctx client context that contains all details.
1058
    /// @param prefix_len prefix length (for PD leases)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
1059
    ///        Should be 128 for other lease types
1060
    /// @param [out] callout_status callout returned by the lease6_select
1061 1062 1063 1064 1065 1066
    ///
    /// The following parameters are used from the ctx structure:
    /// @ref ClientContext6::subnet_ subnet the lease is allocated from
    /// @ref ClientContext6::duid_ client's DUID
    /// @ref ClientContext6::iaid_ IAID from the IA_NA container the client sent to us
    /// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates that server takes
1067 1068
    ///        responsibility for the forward DNS Update for this lease
    ///        (if true).
1069
    /// @ref ClientContext6::rev_dns_update_ A boolean value which indicates that server takes
1070 1071
    ///        responsibility for the reverse DNS Update for this lease
    ///        (if true).
1072
    /// @ref ClientContext6::hostname_ A fully qualified domain-name of the client.
1073 1074 1075 1076 1077
    /// @ref ClientContext6::callout_handle_ a callout handle (used in hooks). A
    ///        lease callouts will be executed if this parameter is passed.
    /// @ref ClientContext6::fake_allocation_ is this real i.e. REQUEST (false)
    ///        or just picking an address for SOLICIT that is not really
    ///        allocated (true)
1078
    ///
1079 1080
    /// @return refreshed lease
    /// @throw BadValue if trying to recycle lease that is still valid
1081 1082 1083 1084 1085
    Lease6Ptr
    reuseExpiredLease(Lease6Ptr& expired,
                      ClientContext6& ctx,
                      uint8_t prefix_len,
                      hooks::CalloutHandle::CalloutNextStep& callout_status);
1086

Francis Dupont's avatar
Francis Dupont committed
1087 1088
    /// @brief Updates FQDN and Client's Last Transmission Time
    /// for a collection of leases.