Parcourir la source

[master] Merge branch 'trac4320'

Marcin Siodelski il y a 9 ans
Parent
commit
3004ceae1c

+ 95 - 82
src/bin/dhcp6/dhcp6_srv.cc

@@ -284,12 +284,15 @@ Dhcpv6Srv::testUnicast(const Pkt6Ptr& pkt) const {
     return (true);
 }
 
-AllocEngine::ClientContext6
-Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
-    AllocEngine::ClientContext6 ctx;
+void
+Dhcpv6Srv::initContext(const Pkt6Ptr& pkt, AllocEngine::ClientContext6& ctx) {
     ctx.subnet_ = selectSubnet(pkt);
+    ctx.duid_ = pkt->getClientId(),
+    ctx.fwd_dns_update_ = false;
+    ctx.rev_dns_update_ = false;
+    ctx.hostname_ = "";
     ctx.query_ = pkt;
-    ctx.duid_ = pkt->getClientId();
+    ctx.callout_handle_ = getCalloutHandle(pkt);
     ctx.hwaddr_ = getMAC(pkt);
 
     // Collect host identifiers if host reservations enabled. The identifiers
@@ -322,8 +325,6 @@ Dhcpv6Srv::createContext(const Pkt6Ptr& pkt) {
         // Find host reservations using specified identifiers.
         alloc_engine_->findReservation(ctx);
     }
-
-    return (ctx);
 }
 
 bool Dhcpv6Srv::run() {
@@ -1282,7 +1283,7 @@ Dhcpv6Srv::getMAC(const Pkt6Ptr& pkt) {
 
 OptionPtr
 Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                       AllocEngine::ClientContext6& orig_ctx,
+                       AllocEngine::ClientContext6& ctx,
                        boost::shared_ptr<Option6IA> ia) {
 
     // Check if the client sent us a hint in his IA_NA. Clients may send an
@@ -1301,8 +1302,7 @@ Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         .arg(hint_opt ? hint.toText() : "(no hint)");
 
     // convenience values
-    const Subnet6Ptr& subnet = orig_ctx.subnet_;
-    const DuidPtr& duid = orig_ctx.duid_;
+    const Subnet6Ptr& subnet = ctx.subnet_;
 
     // If there is no subnet selected for handling this IA_NA, the only thing left to do is
     // to say that we are sorry, but the user won't get an address. As a convenience, we
@@ -1346,19 +1346,21 @@ Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
                                                                 do_rev);
     }
 
+    // Update per-packet context values.
+    ctx.fwd_dns_update_ = do_fwd;
+    ctx.rev_dns_update_ = do_rev;
+    ctx.fake_allocation_ = fake_allocation;
+
+    // Set per-IA context values.
+    ctx.createIAContext();
+    ctx.currentIA().iaid_ = ia->getIAID();
+    ctx.currentIA().addHint(hint);
+    ctx.currentIA().type_ = Lease::TYPE_NA;
+
     // Use allocation engine to pick a lease for this client. Allocation engine
     // will try to honor the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
-    AllocEngine::ClientContext6 ctx(subnet, duid, ia->getIAID(),
-                                    hint, Lease::TYPE_NA, do_fwd, do_rev,
-                                    orig_ctx.hostname_, fake_allocation);
-    ctx.callout_handle_ = getCalloutHandle(query);
-    ctx.hwaddr_ = orig_ctx.hwaddr_;
-    ctx.host_ = orig_ctx.host_;
-    ctx.query_ = orig_ctx.query_;
-    ctx.host_identifiers_ = orig_ctx.host_identifiers_;
-
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
 
     /// @todo: Handle more than one lease
@@ -1416,7 +1418,7 @@ Dhcpv6Srv::assignIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
 OptionPtr
 Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                       AllocEngine::ClientContext6& orig_ctx,
+                       AllocEngine::ClientContext6& ctx,
                        boost::shared_ptr<Option6IA> ia) {
 
     // Check if the client sent us a hint in his IA_PD. Clients may send an
@@ -1436,8 +1438,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         .arg(hint_opt ? hint.toText() : "(no hint)");
 
 
-    const Subnet6Ptr& subnet = orig_ctx.subnet_;
-    const DuidPtr& duid = orig_ctx.duid_;
+    const Subnet6Ptr& subnet = ctx.subnet_;
 
     // Create IA_PD that we will put in the response.
     // Do not use OptionDefinition to create option's instance so
@@ -1467,20 +1468,18 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
     // Reply message it means that it is committing leases. Other message
     // type (Advertise) means that server is not committing leases (fake
     // allocation).
-    bool fake_allocation = (answer->getType() != DHCPV6_REPLY);
+    ctx.fake_allocation_ = (answer->getType() != DHCPV6_REPLY);
+
+    // Set per-IA context values.
+    ctx.createIAContext();
+    ctx.currentIA().iaid_ = ia->getIAID();
+    ctx.currentIA().addHint(hint);
+    ctx.currentIA().type_ = Lease::TYPE_PD;
 
     // Use allocation engine to pick a lease for this client. Allocation engine
     // will try to honor the hint, but it is just a hint - some other address
     // may be used instead. If fake_allocation is set to false, the lease will
     // be inserted into the LeaseMgr as well.
-    AllocEngine::ClientContext6 ctx(subnet, duid, ia->getIAID(), hint, Lease::TYPE_PD,
-                                    false, false, string(), fake_allocation);
-    ctx.callout_handle_ = getCalloutHandle(query);
-    ctx.hwaddr_ = orig_ctx.hwaddr_;
-    ctx.host_ = orig_ctx.host_;
-    ctx.query_ = orig_ctx.query_;
-    ctx.host_identifiers_ = orig_ctx.host_identifiers_;
-
     Lease6Collection leases = alloc_engine_->allocateLeases6(ctx);
 
     if (!leases.empty()) {
@@ -1493,7 +1492,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
             // We have a lease! Let's wrap its content into IA_PD option
             // with IAADDR suboption.
-            LOG_INFO(lease6_logger, fake_allocation ?
+            LOG_INFO(lease6_logger, ctx.fake_allocation_ ?
                       DHCP6_PD_LEASE_ADVERT : DHCP6_PD_LEASE_ALLOC)
                 .arg(query->getLabel())
                 .arg((*l)->addr_.toText())
@@ -1516,7 +1515,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         // cause of that failure. The only thing left is to insert
         // status code to pass the sad news to the client.
 
-        LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
+        LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, ctx.fake_allocation_ ?
                   DHCP6_PD_LEASE_ADVERT_FAIL : DHCP6_PD_LEASE_ALLOC_FAIL)
             .arg(query->getLabel())
             .arg(ia->getIAID());
@@ -1531,7 +1530,7 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
 OptionPtr
 Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                       AllocEngine::ClientContext6& orig_ctx,
+                       AllocEngine::ClientContext6& ctx,
                        boost::shared_ptr<Option6IA> ia) {
 
     LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_NA_EXTEND)
@@ -1539,8 +1538,7 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
         .arg(ia->getIAID());
 
     // convenience values
-    const Subnet6Ptr& subnet = orig_ctx.subnet_;
-    const DuidPtr& duid = orig_ctx.duid_;
+    const Subnet6Ptr& subnet = ctx.subnet_;
 
     // Create empty IA_NA option with IAID matching the request.
     Option6IAPtr ia_rsp(new Option6IA(D6O_IA_NA, ia->getIAID()));
@@ -1574,16 +1572,15 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
                                                                 do_fwd, do_rev);
     }
 
-    // Create client context for this renewal
-    AllocEngine::ClientContext6 ctx(subnet, duid, ia->getIAID(),
-                                    IOAddress::IPV6_ZERO_ADDRESS(), Lease::TYPE_NA,
-                                    do_fwd, do_rev, orig_ctx.hostname_, false);
+    // Set per-packet context values.
+    ctx.fwd_dns_update_ = do_fwd;
+    ctx.rev_dns_update_ = do_rev;
 
-    ctx.callout_handle_ = getCalloutHandle(query);
-    ctx.query_ = query;
-    ctx.ia_rsp_ = ia_rsp;
-    ctx.hwaddr_ = orig_ctx.hwaddr_;
-    ctx.host_ = orig_ctx.host_;
+    // Set per-IA context values.
+    ctx.createIAContext();
+    ctx.currentIA().iaid_ = ia->getIAID();
+    ctx.currentIA().type_ = Lease::TYPE_NA;
+    ctx.currentIA().ia_rsp_ = ia_rsp;
 
     // Extract the addresses that the client is trying to obtain.
     OptionCollection addrs = ia->getOptions();
@@ -1600,7 +1597,7 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
             // There's no way to protect against this.
             continue;
         }
-        ctx.hints_.push_back(make_pair(iaaddr->getAddress(), 128));
+        ctx.currentIA().addHint(iaaddr->getAddress());
     }
 
     Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
@@ -1610,6 +1607,13 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
     // - what we actually assigned in leases
     // - old leases that are no longer valid in ctx.old_leases_
 
+    // For each IA inserted by the client we have to determine what to do
+    // about included addresses and notify the client. We will iterate over
+    // those prefixes and remove those that we have already processed. We
+    // don't want to remove them from the context, so we need to copy them
+    // into temporary container.
+    AllocEngine::HintContainer hints = ctx.currentIA().hints_;
+
     // For all leases we have now, add the IAADDR with non-zero lifetimes.
     for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
         Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
@@ -1621,22 +1625,21 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
             .arg(ia_rsp->getIAID());
 
         // Now remove this address from the hints list.
-        AllocEngine::HintType tmp((*l)->addr_, 128);
-        ctx.hints_.erase(std::remove(ctx.hints_.begin(), ctx.hints_.end(), tmp),
-                         ctx.hints_.end());
+        AllocEngine::ResourceType hint_type((*l)->addr_, 128);
+        hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
+                    hints.end());
     }
 
     // For the leases that we just retired, send the addresses with 0 lifetimes.
-    for (Lease6Collection::const_iterator l = ctx.old_leases_.begin();
-                                          l != ctx.old_leases_.end(); ++l) {
+    for (Lease6Collection::const_iterator l = ctx.currentIA().old_leases_.begin();
+                                          l != ctx.currentIA().old_leases_.end(); ++l) {
         Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
                                                   (*l)->addr_, 0, 0));
         ia_rsp->addOption(iaaddr);
 
         // Now remove this address from the hints list.
-        AllocEngine::HintType tmp((*l)->addr_, 128);
-        ctx.hints_.erase(std::remove(ctx.hints_.begin(), ctx.hints_.end(), tmp),
-                         ctx.hints_.end());
+        AllocEngine::ResourceType hint_type((*l)->addr_, 128);
+        hints.erase(std::remove(hints.begin(), hints.end(), hint_type), hints.end());
 
         // If the new FQDN settings have changed for the lease, we need to
         // delete any existing FQDN records for this lease.
@@ -1656,8 +1659,8 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
     // Finally, if there are any addresses requested that we haven't dealt with
     // already, inform the client that he can't have them.
-    for (AllocEngine::HintContainer::const_iterator hint = ctx.hints_.begin();
-         hint != ctx.hints_.end(); ++hint) {
+    for (AllocEngine::HintContainer::const_iterator hint = hints.begin();
+         hint != hints.end(); ++hint) {
         Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
                                                   hint->first, 0, 0));
         ia_rsp->addOption(iaaddr);
@@ -1679,15 +1682,15 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
 
 OptionPtr
 Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
-                       AllocEngine::ClientContext6& orig_ctx,
+                       AllocEngine::ClientContext6& ctx,
                        boost::shared_ptr<Option6IA> ia) {
 
     LOG_DEBUG(lease6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_EXTEND)
         .arg(query->getLabel())
         .arg(ia->getIAID());
 
-    const Subnet6Ptr& subnet = orig_ctx.subnet_;
-    const DuidPtr& duid = orig_ctx.duid_;
+    const Subnet6Ptr& subnet = ctx.subnet_;
+    const DuidPtr& duid = ctx.duid_;
 
     // Let's create a IA_PD response and fill it in later
     Option6IAPtr ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
@@ -1726,16 +1729,11 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
     ia_rsp->setT1(subnet->getT1());
     ia_rsp->setT2(subnet->getT2());
 
-    // Create client context for this renewal
-    static const IOAddress none("::");
-    AllocEngine::ClientContext6 ctx(subnet, duid, ia->getIAID(), none,
-                                    Lease::TYPE_PD, false, false, string(""),
-                                    false);
-    ctx.callout_handle_ = getCalloutHandle(query);
-    ctx.query_ = query;
-    ctx.ia_rsp_ = ia_rsp;
-    ctx.hwaddr_ = orig_ctx.hwaddr_;
-    ctx.host_ = orig_ctx.host_;
+    // Set per-IA context values.
+    ctx.createIAContext();
+    ctx.currentIA().iaid_ = ia->getIAID();
+    ctx.currentIA().type_ = Lease::TYPE_PD;
+    ctx.currentIA().ia_rsp_ = ia_rsp;
 
     // Extract prefixes that the client is trying to renew.
     OptionCollection addrs = ia->getOptions();
@@ -1754,7 +1752,7 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
         }
 
         // Put the client's prefix into the hints list.
-        ctx.hints_.push_back(make_pair(prf->getAddress(), prf->getLength()));
+        ctx.currentIA().addHint(prf->getAddress(), prf->getLength());
     }
 
     // Call Allocation Engine and attempt to renew leases. Number of things
@@ -1767,6 +1765,13 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
     //                    in PD context)
     Lease6Collection leases = alloc_engine_->renewLeases6(ctx);
 
+    // For each IA inserted by the client we have to determine what to do
+    // about included prefixes and notify the client. We will iterate over
+    // those prefixes and remove those that we have already processed. We
+    // don't want to remove them from the context, so we need to copy them
+    // into temporary container.
+    AllocEngine::HintContainer hints = ctx.currentIA().hints_;
+
     // For all the leases we have now, add the IAPPREFIX with non-zero lifetimes
     for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
         Option6IAPrefixPtr prf(new Option6IAPrefix(D6O_IAPREFIX,
@@ -1780,9 +1785,9 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
             .arg(ia->getIAID());
 
         // Now remove this address from the hints list.
-        AllocEngine::HintType tmp((*l)->addr_, (*l)->prefixlen_);
-        ctx.hints_.erase(std::remove(ctx.hints_.begin(), ctx.hints_.end(), tmp),
-                                     ctx.hints_.end());
+        AllocEngine::ResourceType hint_type((*l)->addr_, (*l)->prefixlen_);
+        hints.erase(std::remove(hints.begin(), hints.end(), hint_type),
+                    hints.end());
     }
 
     /// @todo: Maybe we should iterate over ctx.old_leases_, i.e. the leases
@@ -1792,8 +1797,8 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
     // zero lifetimes
     // Finally, if there are any addresses requested that we haven't dealt with
     // already, inform the client that he can't have them.
-    for (AllocEngine::HintContainer::const_iterator prefix = ctx.hints_.begin();
-         prefix != ctx.hints_.end(); ++prefix) {
+    for (AllocEngine::HintContainer::const_iterator prefix = hints.begin();
+         prefix != hints.end(); ++prefix) {
         // Send the prefix with the zero lifetimes only if the prefix
         // contains non-zero value. A zero value indicates that the hint was
         // for the prefix length.
@@ -2233,7 +2238,8 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
     sanityCheck(solicit, MANDATORY, FORBIDDEN);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(solicit);
+    AllocEngine::ClientContext6 ctx;
+    initContext(solicit, ctx);
 
     Pkt6Ptr response(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
 
@@ -2277,7 +2283,8 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
     sanityCheck(request, MANDATORY, MANDATORY);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(request);
+    AllocEngine::ClientContext6 ctx;
+    initContext(request, ctx);
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
 
@@ -2302,7 +2309,8 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
     sanityCheck(renew, MANDATORY, MANDATORY);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(renew);
+    AllocEngine::ClientContext6 ctx;
+    initContext(renew, ctx);
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
 
@@ -2326,7 +2334,8 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
     sanityCheck(rebind, MANDATORY, FORBIDDEN);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(rebind);
+    AllocEngine::ClientContext6 ctx;
+    initContext(rebind, ctx);
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
 
@@ -2350,7 +2359,8 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
     sanityCheck(confirm, MANDATORY, FORBIDDEN);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(confirm);
+    AllocEngine::ClientContext6 ctx;
+    initContext(confirm, ctx);
 
     // Get IA_NAs from the Confirm. If there are none, the message is
     // invalid and must be discarded. There is nothing more to do.
@@ -2440,7 +2450,8 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
     sanityCheck(release, MANDATORY, MANDATORY);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(release);
+    AllocEngine::ClientContext6 ctx;
+    initContext(release, ctx);
 
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
 
@@ -2467,7 +2478,8 @@ Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(decline);
+    AllocEngine::ClientContext6 ctx;
+    initContext(decline, ctx);
 
     // Copy client options (client-id, also relay information if present)
     copyClientOptions(decline, reply);
@@ -2743,7 +2755,8 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) {
     sanityCheck(inf_request, OPTIONAL, OPTIONAL);
 
     // Let's create a simplified client context here.
-    AllocEngine::ClientContext6 ctx = createContext(inf_request);
+    AllocEngine::ClientContext6 ctx;
+    initContext(inf_request, ctx);
 
     // Create a Reply packet, with the same trans-id as the client's.
     Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, inf_request->getTransid()));

+ 20 - 12
src/bin/dhcp6/dhcp6_srv.h

@@ -307,13 +307,13 @@ protected:
     /// @param answer server's response to the client's message. This
     /// message should contain Client FQDN option being sent by the server
     /// to the client (if the client sent this option to the server).
-    /// @param orig_ctx client context (contains subnet, duid and other parameters)
+    /// @param ctx client context (contains subnet, duid and other parameters)
     /// @param ia pointer to client's IA_NA option (client's request)
     ///
     /// @return IA_NA option (server's response)
     OptionPtr assignIA_NA(const isc::dhcp::Pkt6Ptr& query,
                           const isc::dhcp::Pkt6Ptr& answer,
-                          AllocEngine::ClientContext6& orig_ctx,
+                          AllocEngine::ClientContext6& ctx,
                           Option6IAPtr ia);
 
     /// @brief Processes IA_PD option (and assigns prefixes if necessary).
@@ -326,12 +326,12 @@ protected:
     ///
     /// @param query client's message (typically SOLICIT or REQUEST)
     /// @param answer server's response to the client's message.
-    /// @param orig_ctx client context (contains subnet, duid and other parameters)
+    /// @param ctx client context (contains subnet, duid and other parameters)
     /// @param ia pointer to client's IA_PD option (client's request)
     /// @return IA_PD option (server's response)
     OptionPtr assignIA_PD(const Pkt6Ptr& query,
                           const isc::dhcp::Pkt6Ptr& answer,
-                          AllocEngine::ClientContext6& orig_ctx,
+                          AllocEngine::ClientContext6& ctx,
                           boost::shared_ptr<Option6IA> ia);
 
     /// @brief Extends lifetime of the specific IA_NA option.
@@ -357,12 +357,12 @@ protected:
     /// @param answer server's response to the client's message. This
     /// message should contain Client FQDN option being sent by the server
     /// to the client (if the client sent this option to the server).
-    /// @param orig_ctx client context (contains subnet, duid and other parameters)
+    /// @param ctx client context (contains subnet, duid and other parameters)
     /// @param ia IA_NA option which carries address for which lease lifetime
     /// will be extended.
     /// @return IA_NA option (server's response)
     OptionPtr extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
-                          AllocEngine::ClientContext6& orig_ctx,
+                          AllocEngine::ClientContext6& ctx,
                           Option6IAPtr ia);
 
     /// @brief Extends lifetime of the prefix.
@@ -377,14 +377,14 @@ protected:
     /// (see RFC3633, section 12.2. for details).
     ///
     /// @param query client's message
-    /// @param orig_ctx client context (contains subnet, duid and other parameters)
+    /// @param ctx client context (contains subnet, duid and other parameters)
     /// @param ia IA_PD option that is being renewed
     /// @return IA_PD option (server's response)
     /// @throw DHCPv6DiscardMessageError when the message being processed should
     /// be discarded by the server, i.e. there is no binding for the client doing
     /// Rebind.
     OptionPtr extendIA_PD(const Pkt6Ptr& query,
-                          AllocEngine::ClientContext6& orig_ctx,
+                          AllocEngine::ClientContext6& ctx,
                           Option6IAPtr ia);
 
     /// @brief Releases specific IA_NA option
@@ -638,9 +638,9 @@ protected:
     /// - there is no such option provided by the server)
     void processRSOO(const Pkt6Ptr& query, const Pkt6Ptr& rsp);
 
-    /// @brief Creates client context for specified packet
+    /// @brief Initializes client context for specified packet
     ///
-    /// Instantiates the ClientContext6 and then:
+    /// This method:
     /// - Performs the subnet selection and stores the result in context
     /// - Extracts the duid from the packet and saves it to the context
     /// - Extracts the hardware address from the packet and saves it to
@@ -648,8 +648,16 @@ protected:
     /// - Performs host reservation lookup and stores the result in the
     /// context
     ///
-    /// @return client context
-    AllocEngine::ClientContext6 createContext(const Pkt6Ptr& pkt);
+    /// Even though the incoming packet type is known to this method, it
+    /// doesn't set the @c fake_allocation flag, because of a possibility
+    /// that the Rapid Commit option is in use. The @c fake_allocation
+    /// flag is set appropriately after it has been determined whether
+    /// the Rapid Commit option was included and that the server respects
+    /// it.
+    ///
+    /// @param pkt pointer to a packet for which context will be created.
+    /// @param [out] ctx reference to context object to be initialized.
+    void initContext(const Pkt6Ptr& pkt, AllocEngine::ClientContext6& ctx);
 
     /// @brief this is a prefix added to the contend of vendor-class option
     ///

+ 1 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -130,7 +130,7 @@ public:
     using Dhcpv6Srv::shutdown_;
     using Dhcpv6Srv::name_change_reqs_;
     using Dhcpv6Srv::VENDOR_CLASS_PREFIX;
-    using Dhcpv6Srv::createContext;
+    using Dhcpv6Srv::initContext;
 
     /// @brief packets we pretend to receive
     ///

+ 124 - 89
src/lib/dhcpsrv/alloc_engine.cc

@@ -35,6 +35,7 @@
 #include <vector>
 #include <stdint.h>
 #include <string.h>
+#include <utility>
 
 using namespace isc::asiolink;
 using namespace isc::dhcp;
@@ -331,31 +332,25 @@ AllocEngine::findReservationInternal(ContextType& ctx,
 // ##########################################################################
 
 AllocEngine::ClientContext6::ClientContext6()
-    : subnet_(), duid_(), iaid_(0), type_(Lease::TYPE_NA), hwaddr_(),
-      hints_(), fwd_dns_update_(false), rev_dns_update_(false), hostname_(""),
-      callout_handle_(), fake_allocation_(false), old_leases_(), host_(),
-      query_(), ia_rsp_(), host_identifiers_() {
+    : query_(), fake_allocation_(false), subnet_(), duid_(),
+      hwaddr_(), host_identifiers_(), host_(), fwd_dns_update_(false),
+      rev_dns_update_(false), hostname_(), callout_handle_(),
+      ias_() {
 }
 
-AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                                            const uint32_t iaid,
-                                            const isc::asiolink::IOAddress& hint,
-                                            const Lease::Type type, const bool fwd_dns,
+AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
+                                            const DuidPtr& duid,
+                                            const bool fwd_dns,
                                             const bool rev_dns,
                                             const std::string& hostname,
-                                            const bool fake_allocation):
-    subnet_(subnet), duid_(duid), iaid_(iaid), type_(type), hwaddr_(),
-    hints_(), fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
-    hostname_(hostname), fake_allocation_(fake_allocation),
-    old_leases_(), host_(), query_(), ia_rsp_(), host_identifiers_() {
-
-    static asiolink::IOAddress any("::");
-
-    if (hint != any) {
-        hints_.push_back(std::make_pair(hint, 128));
-    }
-    // callout_handle, host pointers initiated to NULL by their
-    // respective constructors.
+                                            const bool fake_allocation,
+                                            const Pkt6Ptr& query,
+                                            const CalloutHandlePtr& callout_handle)
+    : query_(query), fake_allocation_(fake_allocation), subnet_(subnet),
+      duid_(duid), hwaddr_(), host_identifiers_(), host_(),
+      fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
+      hostname_(hostname), callout_handle_(callout_handle),
+      allocated_resources_(), ias_() {
 
     // Initialize host identifiers.
     if (duid) {
@@ -363,6 +358,33 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet, const Duid
     }
 }
 
+AllocEngine::ClientContext6::IAContext::IAContext()
+    : iaid_(0), type_(Lease::TYPE_NA), hints_(), old_leases_(),
+      changed_leases_(), ia_rsp_() {
+}
+
+void
+AllocEngine::ClientContext6::
+IAContext::addHint(const asiolink::IOAddress& prefix,
+                   const uint8_t prefix_len) {
+    hints_.push_back(std::make_pair(prefix, prefix_len));
+}
+
+void
+AllocEngine::ClientContext6::
+addAllocatedResource(const asiolink::IOAddress& prefix,
+                     const uint8_t prefix_len) {
+    static_cast<void>(allocated_resources_.insert(std::make_pair(prefix,
+                                                                 prefix_len)));
+}
+
+bool
+AllocEngine::ClientContext6::
+isAllocated(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const {
+    return (static_cast<bool>
+            (allocated_resources_.count(std::make_pair(prefix, prefix_len))));
+}
+
 
 void AllocEngine::findReservation(ClientContext6& ctx) {
     findReservationInternal(ctx, boost::bind(&HostMgr::get6,
@@ -384,7 +406,9 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
         // Check if there are existing leases for that subnet/duid/iaid
         // combination.
         Lease6Collection leases =
-            LeaseMgrFactory::instance().getLeases6(ctx.type_, *ctx.duid_, ctx.iaid_,
+            LeaseMgrFactory::instance().getLeases6(ctx.currentIA().type_,
+                                                   *ctx.duid_,
+                                                   ctx.currentIA().iaid_,
                                                    ctx.subnet_->getID());
 
         // Now do the checks:
@@ -419,11 +443,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
             // someone else.
             allocateReservedLeases6(ctx, leases);
 
-            // If we got at least one lease, we're good to go.
-            if (!leases.empty()) {
-                return (leases);
-            }
-
             // If not, we'll need to continue and will eventually fall into case 4:
             // getting a regular lease. That could happen when we're processing
             // request from client X, there's a reserved address A for X, but
@@ -447,10 +466,7 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
             // If they're not, we're ok to keep using them.
             removeNonmatchingReservedLeases6(ctx, leases);
 
-            if (!leases.empty()) {
-                // Return old leases so the server can see what has changed.
-                return (updateLeaseData(ctx, leases));
-            }
+            leases = updateLeaseData(ctx, leases);
 
             // If leases are empty at this stage, it means that we used to have
             // leases for this client, but we checked and those leases are reserved
@@ -492,11 +508,6 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
 
             // All checks are done. Let's hope we have some leases left.
 
-            // If we have any leases left, let's return them and we're done.
-            if (!leases.empty()) {
-                return (leases);
-            }
-
             // If we don't have any leases at this stage, it means that we hit
             // one of the following cases:
             // - we have a reservation, but it's not for this IAID/ia-type and
@@ -508,24 +519,32 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
             //   someone else, so we released it.
         }
 
-        // Case 4/catch-all: One of the following is true:
-        // - we don't have leases and there are no reservations
-        // - we used to have leases, but we lost them, because they are now
-        //   reserved for someone else
-        // - we have a reservation, but it is not usable yet, because the address
-        //   is still used by someone else
-        //
-        // In any case, we need to go through normal lease assignment process
-        // for now. This is also a catch-all or last resort approach, when we
-        // couldn't find any reservations (or couldn't use them).
+        if (leases.empty()) {
+            // Case 4/catch-all: One of the following is true:
+            // - we don't have leases and there are no reservations
+            // - we used to have leases, but we lost them, because they are now
+            //   reserved for someone else
+            // - we have a reservation, but it is not usable yet, because the address
+            //   is still used by someone else
+            //
+            // In any case, we need to go through normal lease assignment process
+            // for now. This is also a catch-all or last resort approach, when we
+            // couldn't find any reservations (or couldn't use them).
 
-        LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
-                  ALLOC_ENGINE_V6_ALLOC_UNRESERVED)
-            .arg(ctx.query_->getLabel());
+            LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
+                      ALLOC_ENGINE_V6_ALLOC_UNRESERVED)
+                .arg(ctx.query_->getLabel());
 
-        leases = allocateUnreservedLeases6(ctx);
+            leases = allocateUnreservedLeases6(ctx);
+        }
 
         if (!leases.empty()) {
+            // If there are any leases allocated, let's store in them in the
+            // IA context so as they are available when we process subsequent
+            // IAs.
+            BOOST_FOREACH(Lease6Ptr lease, leases) {
+                ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
+            }
             return (leases);
         }
 
@@ -544,11 +563,11 @@ AllocEngine::allocateLeases6(ClientContext6& ctx) {
 Lease6Collection
 AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
 
-    AllocatorPtr allocator = getAllocator(ctx.type_);
+    AllocatorPtr allocator = getAllocator(ctx.currentIA().type_);
 
     if (!allocator) {
         isc_throw(InvalidOperation, "No allocator specified for "
-                  << Lease6::typeToText(ctx.type_));
+                  << Lease6::typeToText(ctx.currentIA().type_));
     }
 
     // Check which host reservation mode is supported in this subnet.
@@ -557,19 +576,20 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
     Lease6Collection leases;
 
     IOAddress hint = IOAddress::IPV6_ZERO_ADDRESS();
-    if (!ctx.hints_.empty()) {
+    if (!ctx.currentIA().hints_.empty()) {
         /// @todo: We support only one hint for now
-        hint = ctx.hints_[0].first;
+        hint = ctx.currentIA().hints_[0].first;
     }
 
     // check if the hint is in pool and is available
     // This is equivalent of subnet->inPool(hint), but returns the pool
     Pool6Ptr pool = boost::dynamic_pointer_cast<
-        Pool6>(ctx.subnet_->getPool(ctx.type_, hint, false));
+        Pool6>(ctx.subnet_->getPool(ctx.currentIA().type_, hint, false));
 
     if (pool) {
         /// @todo: We support only one hint for now
-        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(ctx.type_, hint);
+        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
+                                                                hint);
         if (!lease) {
 
             // In-pool reservations: Check if this address is reserved for someone
@@ -623,7 +643,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
                     // Copy an existing, expired lease so as it can be returned
                     // to the caller.
                     Lease6Ptr old_lease(new Lease6(*lease));
-                    ctx.old_leases_.push_back(old_lease);
+                    ctx.currentIA().old_leases_.push_back(old_lease);
 
                     /// We found a lease and it is expired, so we can reuse it
                     lease = reuseExpiredLease(lease, ctx, pool->getLength());
@@ -649,7 +669,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
     // - we find an address for which the lease has expired
     // - we exhaust number of tries
     uint64_t max_attempts = (attempts_ > 0 ? attempts_  :
-                             ctx.subnet_->getPoolCapacity(ctx.type_));
+                             ctx.subnet_->getPoolCapacity(ctx.currentIA().type_));
     for (uint64_t i = 0; i < max_attempts; ++i)
     {
         IOAddress candidate = allocator->pickAddress(ctx.subnet_, ctx.duid_, hint);
@@ -667,14 +687,14 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
         // The first step is to find out prefix length. It is 128 for
         // non-PD leases.
         uint8_t prefix_len = 128;
-        if (ctx.type_ == Lease::TYPE_PD) {
+        if (ctx.currentIA().type_ == Lease::TYPE_PD) {
             Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
-                ctx.subnet_->getPool(ctx.type_, candidate, false));
+                ctx.subnet_->getPool(ctx.currentIA().type_, candidate, false));
             /// @todo: verify that the pool is non-null
             prefix_len = pool->getLength();
         }
 
-        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.type_,
+        Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
                                                                    candidate);
         if (!existing) {
 
@@ -685,7 +705,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
             if (lease) {
                 // We are allocating a new lease (not renewing). So, the
                 // old lease should be NULL.
-                ctx.old_leases_.clear();
+                ctx.currentIA().old_leases_.clear();
 
                 leases.push_back(lease);
                 return (leases);
@@ -699,7 +719,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
                 // Copy an existing, expired lease so as it can be returned
                 // to the caller.
                 Lease6Ptr old_lease(new Lease6(*existing));
-                ctx.old_leases_.push_back(old_lease);
+                ctx.currentIA().old_leases_.push_back(old_lease);
 
                 existing = reuseExpiredLease(existing,
                                              ctx,
@@ -723,7 +743,8 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
 }
 
 void
-AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases) {
+AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
+                                     Lease6Collection& existing_leases) {
 
     // If there are no reservations or the reservation is v4, there's nothing to do.
     if (!ctx.host_ || !ctx.host_->hasIPv6Reservation()) {
@@ -734,7 +755,8 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& exis
     }
 
     // Let's convert this from Lease::Type to IPv6Reserv::Type
-    IPv6Resrv::Type type = ctx.type_ == Lease::TYPE_NA ? IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
+    IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
+        IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
 
     // Get the IPv6 reservations of specified type.
     const IPv6ResrvRange& reservs = ctx.host_->getIPv6Reservations(type);
@@ -767,14 +789,15 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx, Lease6Collection& exis
 
         // If there's a lease for this address, let's not create it.
         // It doesn't matter whether it is for this client or for someone else.
-        if (!LeaseMgrFactory::instance().getLease6(ctx.type_, addr)) {
+        if (!LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
+                                                   addr)) {
             // Ok, let's create a new lease...
             Lease6Ptr lease = createLease6(ctx, addr, prefix_len);
 
             // ... and add it to the existing leases list.
             existing_leases.push_back(lease);
 
-            if (ctx.type_ == Lease::TYPE_NA) {
+            if (ctx.currentIA().type_ == Lease::TYPE_NA) {
                 LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_HR_ADDR_GRANTED)
                     .arg(addr.toText())
                     .arg(ctx.query_->getLabel());
@@ -829,7 +852,7 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
 
         // Ok, we have a problem. This host has a lease that is reserved
         // for someone else. We need to recover from this.
-        if (ctx.type_ == Lease::TYPE_NA) {
+        if (ctx.currentIA().type_ == Lease::TYPE_NA) {
             LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE)
                 .arg((*candidate)->addr_.toText()).arg(ctx.duid_->toText())
                 .arg(host->getIdentifierAsText());
@@ -850,8 +873,8 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
         // Need to decrease statistic for assigned addresses.
         StatsMgr::instance().addValue(
             StatsMgr::generateName("subnet", ctx.subnet_->getID(),
-                                   ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
-                                                                 "assigned-pds"),
+                                   ctx.currentIA().type_ == Lease::TYPE_NA ?
+                                   "assigned-nas" : "assigned-pds"),
             static_cast<int64_t>(-1));
 
         // In principle, we could trigger a hook here, but we will do this
@@ -860,7 +883,7 @@ AllocEngine::removeNonmatchingReservedLeases6(ClientContext6& ctx,
         // should not interfere with it.
 
         // Add this to the list of removed leases.
-        ctx.old_leases_.push_back(*candidate);
+        ctx.currentIA().old_leases_.push_back(*candidate);
 
         // Let's remove this candidate from existing leases
         removeLeases(existing_leases, (*candidate)->addr_);
@@ -902,7 +925,8 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
     // leases for deletion, by setting appropriate pointers to NULL.
     for (Lease6Collection::iterator lease = existing_leases.begin();
          lease != existing_leases.end(); ++lease) {
-        IPv6Resrv resv(ctx.type_ == Lease::TYPE_NA ? IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD,
+        IPv6Resrv resv(ctx.currentIA().type_ == Lease::TYPE_NA ?
+                       IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD,
                        (*lease)->addr_, (*lease)->prefixlen_);
         if (!ctx.host_->hasReservation(resv)) {
             // We have reservations, but not for this lease. Release it.
@@ -916,14 +940,14 @@ AllocEngine::removeNonreservedLeases6(ClientContext6& ctx,
             // Need to decrease statistic for assigned addresses.
             StatsMgr::instance().addValue(
                 StatsMgr::generateName("subnet", ctx.subnet_->getID(),
-                                       ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
-                                                                     "assigned-pds"),
+                                       ctx.currentIA().type_ == Lease::TYPE_NA ?
+                                       "assigned-nas" : "assigned-pds"),
                 static_cast<int64_t>(-1));
 
             /// @todo: Probably trigger a hook here
 
             // Add this to the list of removed leases.
-            ctx.old_leases_.push_back(*lease);
+            ctx.currentIA().old_leases_.push_back(*lease);
 
             // Set this pointer to NULL. The pointer is still valid. We're just
             // setting the Lease6Ptr to NULL value. We'll remove all NULL
@@ -964,7 +988,7 @@ AllocEngine::reuseExpiredLease(Lease6Ptr& expired, ClientContext6& ctx,
     }
 
     // address, lease type and prefixlen (0) stay the same
-    expired->iaid_ = ctx.iaid_;
+    expired->iaid_ = ctx.currentIA().iaid_;
     expired->duid_ = ctx.duid_;
     expired->preferred_lft_ = ctx.subnet_->getPreferred();
     expired->valid_lft_ = ctx.subnet_->getValid();
@@ -1039,14 +1063,15 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
                                     const IOAddress& addr,
                                     uint8_t prefix_len) {
 
-    if (ctx.type_ != Lease::TYPE_PD) {
+    if (ctx.currentIA().type_ != Lease::TYPE_PD) {
         prefix_len = 128; // non-PD lease types must be always /128
     }
 
-    Lease6Ptr lease(new Lease6(ctx.type_, addr, ctx.duid_, ctx.iaid_,
-                               ctx.subnet_->getPreferred(), ctx.subnet_->getValid(),
-                               ctx.subnet_->getT1(), ctx.subnet_->getT2(),
-                               ctx.subnet_->getID(), ctx.hwaddr_, prefix_len));
+    Lease6Ptr lease(new Lease6(ctx.currentIA().type_, addr, ctx.duid_,
+                               ctx.currentIA().iaid_, ctx.subnet_->getPreferred(),
+                               ctx.subnet_->getValid(), ctx.subnet_->getT1(),
+                               ctx.subnet_->getT2(), ctx.subnet_->getID(),
+                               ctx.hwaddr_, prefix_len));
 
     lease->fqdn_fwd_ = ctx.fwd_dns_update_;
     lease->fqdn_rev_ = ctx.rev_dns_update_;
@@ -1094,11 +1119,11 @@ Lease6Ptr AllocEngine::createLease6(ClientContext6& ctx,
         if (status) {
             // The lease insertion succeeded - if the lease is in the
             // current subnet lets bump up the statistic.
-            if (ctx.subnet_->inPool(ctx.type_, addr)) {
+            if (ctx.subnet_->inPool(ctx.currentIA().type_, addr)) {
                 StatsMgr::instance().addValue(
                     StatsMgr::generateName("subnet", ctx.subnet_->getID(),
-                                           ctx.type_ == Lease::TYPE_NA ? "assigned-nas" :
-                                                                         "assigned-pds"),
+                                           ctx.currentIA().type_ == Lease::TYPE_NA ?
+                                           "assigned-nas" : "assigned-pds"),
                     static_cast<int64_t>(1));
             }
 
@@ -1138,7 +1163,8 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
 
         // Check if there are any leases for this client.
         Lease6Collection leases = LeaseMgrFactory::instance()
-            .getLeases6(ctx.type_, *ctx.duid_, ctx.iaid_, ctx.subnet_->getID());
+            .getLeases6(ctx.currentIA().type_, *ctx.duid_,
+                        ctx.currentIA().iaid_, ctx.subnet_->getID());
 
         if (!leases.empty()) {
             LOG_DEBUG(alloc_engine_logger, ALLOC_ENGINE_DBG_TRACE,
@@ -1189,6 +1215,15 @@ AllocEngine::renewLeases6(ClientContext6& ctx) {
             extendLease6(ctx, *l);
         }
 
+        if (!leases.empty()) {
+            // If there are any leases allocated, let's store in them in the
+            // IA context so as they are available when we process subsequent
+            // IAs.
+            BOOST_FOREACH(Lease6Ptr lease, leases) {
+                ctx.addAllocatedResource(lease->addr_, lease->prefixlen_);
+            }
+        }
+
         return (leases);
 
     } catch (const isc::Exception& e) {
@@ -1226,7 +1261,7 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
             static_cast<int64_t>(-1));
 
         // Add it to the removed leases list.
-        ctx.old_leases_.push_back(lease);
+        ctx.currentIA().old_leases_.push_back(lease);
 
         return;
     }
@@ -1275,9 +1310,9 @@ AllocEngine::extendLease6(ClientContext6& ctx, Lease6Ptr lease) {
 
         // Pass the IA option to be sent in response
         if (lease->type_ == Lease::TYPE_NA) {
-            callout_handle->setArgument("ia_na", ctx.ia_rsp_);
+            callout_handle->setArgument("ia_na", ctx.currentIA().ia_rsp_);
         } else {
-            callout_handle->setArgument("ia_pd", ctx.ia_rsp_);
+            callout_handle->setArgument("ia_pd", ctx.currentIA().ia_rsp_);
         }
 
         // Call all installed callouts
@@ -1334,7 +1369,7 @@ AllocEngine::updateLeaseData(ClientContext6& ctx, const Lease6Collection& leases
              (lease->fqdn_fwd_ != (*lease_it)->fqdn_fwd_) ||
              (lease->fqdn_rev_ != (*lease_it)->fqdn_rev_) ||
              (lease->hostname_ != (*lease_it)->hostname_))) {
-            ctx.changed_leases_.push_back(*lease_it);
+            ctx.currentIA().changed_leases_.push_back(*lease_it);
             LeaseMgrFactory::instance().updateLease6(lease);
         }
         updated_leases.push_back(lease);

+ 140 - 66
src/lib/dhcpsrv/alloc_engine.h

@@ -24,6 +24,7 @@
 
 #include <list>
 #include <map>
+#include <set>
 #include <utility>
 
 namespace isc {
@@ -248,10 +249,13 @@ public:
     /// This is an entry that represents what the client had requested,
     /// either an address or a prefix. Prefix length is 128 for regular
     /// addresses.
-    typedef std::pair<isc::asiolink::IOAddress, uint8_t> HintType;
+    typedef std::pair<isc::asiolink::IOAddress, uint8_t> ResourceType;
 
     /// @brief Container for client's hints.
-    typedef std::vector<HintType> HintContainer;
+    typedef std::vector<ResourceType> HintContainer;
+
+    /// @brief Container holding allocated prefixes or addresses.
+    typedef std::set<ResourceType> ResourceContainer;
 
     /// @brief A tuple holding host identifier type and value.
     typedef std::pair<Host::IdentifierType, std::vector<uint8_t> > IdentifierPair;
@@ -281,27 +285,40 @@ public:
     /// 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.
-    struct ClientContext6 {
+    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_;
+
         /// @brief Subnet selected for the client by the server.
         Subnet6Ptr subnet_;
 
         /// @brief Client identifier
         DuidPtr duid_;
 
-        /// @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 Hardware/MAC address (if available, may be NULL)
         HWAddrPtr hwaddr_;
 
-        /// @brief client's hints
+        /// @brief A list holding host identifiers extracted from a message
+        /// received by the server.
+        IdentifierList host_identifiers_;
+
+        /// @brief A pointer to the object identifying host reservations.
         ///
-        /// There will typically be just one address, but the protocol allows
-        /// more than one address or prefix for each IA container.
-        HintContainer hints_;
+        /// May be NULL if there are no reservations.
+        ConstHostPtr host_;
 
         /// @brief A boolean value which indicates that server takes
         ///        responsibility for the forward DNS Update for this lease
@@ -322,45 +339,77 @@ public:
         /// @brief Callout handle associated with the client's message.
         hooks::CalloutHandlePtr callout_handle_;
 
-        /// @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_;
-
-        /// @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.
+        /// @brief Holds addresses and prefixes allocated for all IAs.
+        ResourceContainer allocated_resources_;
+
+        //@}
+
+        /// @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.
+            HintContainer hints_;
+
+            /// @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.
+            void addHint(const asiolink::IOAddress& prefix,
+                         const uint8_t prefix_len = 128);
+        };
+
+        /// @brief Container holding IA specific contexts.
+        std::vector<IAContext> ias_;
+
+        /// @brief Convenience method adding allocated prefix or address.
         ///
-        /// 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_;
+        /// @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 A pointer to the object identifying host reservations.
-        ///
-        /// May be NULL if there are no reservations.
-        ConstHostPtr host_;
-
-        /// @brief A pointer to the client's message
+        /// @brief Checks if specified address or prefix was allocated.
         ///
-        /// This is used exclusively for hook purposes.
-        Pkt6Ptr query_;
-
-        /// @brief A pointer to the IA_NA/IA_PD option to be sent in response
-        Option6IAPtr ia_rsp_;
-
-        /// @brief A list holding host identifiers extracted from a message
-        /// received by the server.
-        IdentifierList host_identifiers_;
+        /// @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;
 
         /// @brief Conveniece function adding host identifier into
         /// @ref host_identifiers_ list.
@@ -372,6 +421,26 @@ public:
             host_identifiers_.push_back(IdentifierPair(id_type, identifier));
         }
 
+        /// @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.
+        IAContext& currentIA() {
+            if (ias_.empty()) {
+                createIAContext();
+            }
+            return (ias_.back());
+        }
+
+        /// @brief Creates new IA context.
+        ///
+        /// This method should be invoked prior to processing a next IA included
+        /// in the client's message.
+        void createIAContext() {
+            ias_.push_back(IAContext());
+        };
+
         /// @brief Default constructor.
         ClientContext6();
 
@@ -383,9 +452,6 @@ public:
         ///
         /// @param subnet subnet the allocation should come from
         /// @param duid Client's DUID
-        /// @param iaid iaid field from the IA_NA container that client sent
-        /// @param hint a hint that the client provided
-        /// @param type lease type (IA, TA or PD)
         /// @param fwd_dns A boolean value which indicates that server takes
         ///        responsibility for the forward DNS Update for this lease
         ///        (if true).
@@ -393,13 +459,18 @@ public:
         ///        responsibility for the reverse DNS Update for this lease
         ///        (if true).
         /// @param hostname A fully qualified domain-name of the client.
-        /// @param fake_allocation is this real i.e. REQUEST (false) or just picking
-        ///        an address for SOLICIT that is not really allocated (true)
+        /// @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
         ClientContext6(const Subnet6Ptr& subnet, const DuidPtr& duid,
-                       const uint32_t iaid, const isc::asiolink::IOAddress& hint,
-                       const Lease::Type type, const bool fwd_dns, const bool
-                       rev_dns, const std::string& hostname, const bool
-                       fake_allocation);
+                       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());
     };
 
     /// @brief Allocates IPv6 leases for a given IA container
@@ -452,12 +523,13 @@ public:
     ///
     /// The following fields of ClientContext6 are used:
     ///
-    /// @ref ClientContext6::subnet_ subnet the allocation should come from<br/>
+    /// @ref ClientContext6::subnet_ subnet the allocation should
+    ///        come from<br/>
     /// @ref ClientContext6::duid_ Client's DUID<br/>
-    /// @ref ClientContext6::iaid_ iaid field from the IA_NA container
+    /// @ref ClientContext6::IAContext::iaid_ iaid field from the IA_NA container
     ///        that client sent<br/>
-    /// @ref ClientContext6::hints_ a hint that the client provided<br/>
-    /// @ref ClientContext6::type_ lease type (IA, TA or PD)<br/>
+    /// @ref ClientContext6::IAContext::hints_ a hint that the client provided<br/>
+    /// @ref ClientContext6::IAContext::type_ lease type (IA, TA or PD)<br/>
     /// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates
     ///        that server takes responsibility for the forward DNS Update
     ///        for this lease (if true).<br/>
@@ -470,7 +542,8 @@ public:
     ///        allocated (true)<br/>
     /// @ref ClientContext6::callout_handle_ a callout handle (used in hooks). A
     ///        lease callouts will be executed if this parameter is passed.<br/>
-    /// @ref ClientContext6::old_leases_ [out] Collection to which this function
+    /// @ref ClientContext6::IAContext::old_leases_ [out] Collection to which this
+    ///        function
     ///        will append old leases. Leases are stored in the same order as in
     ///        the collection of new leases, being returned. For newly allocated
     ///        leases (not renewed) the NULL pointers are stored in this
@@ -498,9 +571,10 @@ public:
     ///
     /// @param ctx Message processing context. It holds various information
     /// extracted from the client's message and required to allocate a lease.
-    /// In particular, @ref ClientContext6::hints_ provides list of addresses or
-    /// prefixes the client had sent. @ref ClientContext6::old_leases_ will
-    /// contain removed leases in this case.
+    /// 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.
     ///
     /// @return Returns renewed lease.
     Lease6Collection renewLeases6(ClientContext6& ctx);

+ 1 - 4
src/lib/dhcpsrv/pgsql_connection.h

@@ -203,10 +203,7 @@ public:
     /// Creates a prepared statement from the text given and adds it to the
     /// statements_ vector at the given index.
     ///
-    /// @param index Index into the statements_ vector into which the text
-    ///        should be placed.  The vector must be big enough for the index
-    ///        to be valid, else an exception will be thrown.
-    /// @param text Text of the SQL statement to be prepared.
+    /// @param statement SQL statement to be prepared.
     ///
     /// @throw isc::dhcp::DbOperationError An operation on the open database has
     ///        failed.

+ 95 - 53
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -20,6 +20,29 @@ namespace isc {
 namespace dhcp {
 namespace test {
 
+// Test convenience method adding hints to IA context.
+TEST(ClientContext6Test, addHint) {
+   AllocEngine::ClientContext6 ctx;
+   ctx.currentIA().addHint(IOAddress("2001:db8:1::1"));
+   ctx.currentIA().addHint(IOAddress("3000:1::"), 64);
+
+   ASSERT_EQ(2, ctx.currentIA().hints_.size());
+   EXPECT_EQ("2001:db8:1::1", ctx.currentIA().hints_[0].first.toText());
+   EXPECT_EQ("3000:1::", ctx.currentIA().hints_[1].first.toText());
+}
+
+// Test convenience method adding allocated prefixes and addresses to
+// a context.
+TEST(ClientContext6Test, addAllocatedResource) {
+   AllocEngine::ClientContext6 ctx;
+   ctx.addAllocatedResource(IOAddress("2001:db8:1::1"));
+   ctx.addAllocatedResource(IOAddress("3000:1::"), 64);
+
+   ASSERT_EQ(2, ctx.allocated_resources_.size());
+   EXPECT_TRUE(ctx.isAllocated(IOAddress("2001:db8:1::1")));
+   EXPECT_TRUE(ctx.isAllocated(IOAddress("3000:1::"), 64));
+}
+
 // This test checks if the v6 Allocation Engine can be instantiated, parses
 // parameters string and allocators are created.
 TEST_F(AllocEngine6Test, constructor) {
@@ -147,16 +170,17 @@ TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
 
     // Allocations without subnet are not allowed
     Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx1(Subnet6Ptr(), duid_, iaid_, IOAddress("::"),
-                                     Lease::TYPE_NA, false, false, "", false);
-    ctx1.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx1(Subnet6Ptr(), duid_, false, false, "", false,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx1.currentIA().iaid_ = iaid_;
+
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
     ASSERT_FALSE(lease);
 
     // Allocations without DUID are not allowed either
-    AllocEngine::ClientContext6 ctx2(subnet_, DuidPtr(), iaid_, IOAddress("::"),
-                                     Lease::TYPE_NA, false, false, "", false);
-    ctx2.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx2(subnet_, DuidPtr(), false, false, "", false,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx2.currentIA().iaid_ = iaid_;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
     ASSERT_FALSE(lease);
 }
@@ -403,10 +427,10 @@ TEST_F(AllocEngine6Test, smallPool6) {
     initFqdn("myhost.example.com", true, true);
 
     Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA, fqdn_fwd_, fqdn_rev_,
-                                    hostname_, false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, fqdn_fwd_, fqdn_rev_,
+                                    hostname_, false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx.currentIA().iaid_ = iaid_;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
 
     // Check that we got that single lease
@@ -427,7 +451,7 @@ TEST_F(AllocEngine6Test, smallPool6) {
 
     // This is a new lease allocation. The old lease corresponding to a newly
     // allocated lease should be NULL.
-    ASSERT_TRUE(ctx.old_leases_.empty());
+    ASSERT_TRUE(ctx.currentIA().old_leases_.empty());
 }
 
 // This test checks if all addresses in a pool are currently used, the attempt
@@ -458,11 +482,12 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
 
     // There is just a single address in the pool and allocated it to someone
     // else, so the allocation should fail
-    Lease6Ptr lease2;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA, false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    query);
+    ctx.currentIA().iaid_ = iaid_;
 
+    Lease6Ptr lease2;
     EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(ctx)));
     EXPECT_FALSE(lease2);
 
@@ -497,9 +522,9 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     ASSERT_TRUE(lease->expired());
 
     // CASE 1: Asking for any address
-    AllocEngine::ClientContext6 ctx1(subnet_, duid_, iaid_, IOAddress("::"),
-                                     Lease::TYPE_NA, fqdn_fwd_, fqdn_rev_, hostname_, true);
-    ctx1.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx1(subnet_, duid_, fqdn_fwd_, fqdn_rev_, hostname_,
+                                     true, Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+    ctx1.currentIA().iaid_ = iaid_;
 
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
     // Check that we got that single lease
@@ -510,9 +535,11 @@ TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
     checkLease6(lease, Lease::TYPE_NA, 128);
 
     // CASE 2: Asking specifically for this address
-    AllocEngine::ClientContext6 ctx2(subnet_, duid_, iaid_, addr, Lease::TYPE_NA,
-                                     false, false, "", true);
-    ctx2.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx2.currentIA().iaid_ = iaid_;
+    ctx2.currentIA().addHint(addr);
+
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
 
     // Check that we got that single lease
@@ -556,9 +583,11 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     StatsMgr::instance().setValue(name, static_cast<int64_t>(100));
 
     // A client comes along, asking specifically for this address
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, addr, Lease::TYPE_NA,
-                                    false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().addHint(addr);
+
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
 
     // Check that he got that single lease
@@ -570,7 +599,7 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
     EXPECT_FALSE(lease->fqdn_rev_);
 
     // Check that the old lease has been returned.
-    Lease6Ptr old_lease = expectOneLease(ctx.old_leases_);
+    Lease6Ptr old_lease = expectOneLease(ctx.currentIA().old_leases_);
     // It should at least have the same IPv6 address.
     EXPECT_EQ(lease->addr_, old_lease->addr_);
     // Check that it carries not updated FQDN data.
@@ -1170,9 +1199,11 @@ TEST_F(AllocEngine6Test, reservedAddress) {
     int success = 0;
     int failure = 0;
     for (int i = 0; i < 30; i++) {
-        AllocEngine::ClientContext6 ctx(subnet_, clients[i], iaid_, IOAddress("::"),
-                                        Lease::TYPE_NA,  false, false, "", false);
-        ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+        Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+        AllocEngine::ClientContext6 ctx(subnet_, clients[i], false, false, "",
+                                        false, query);
+        ctx.currentIA().iaid_ = iaid_;
+
         findReservation(engine, ctx);
         Lease6Collection leases = engine.allocateLeases6(ctx);
         if (leases.empty()) {
@@ -1194,9 +1225,9 @@ TEST_F(AllocEngine6Test, reservedAddress) {
     // We're now pretty sure that any clients other than the reserved address
     // will not get any service. Now let's check if the client that has the
     // address reserved, will get it (despite the pool being depleted).
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA,  false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx.currentIA().iaid_ = iaid_;
 
     findReservation(engine, ctx);
     Lease6Collection leases = engine.allocateLeases6(ctx);
@@ -1209,9 +1240,10 @@ TEST_F(AllocEngine6Test, allocateLeasesInvalidData) {
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, true);
 
     // That looks like a valid context.
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA,  false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx.currentIA().iaid_ = iaid_;
+
     Lease6Collection leases;
 
     // Let's break it!
@@ -1304,9 +1336,11 @@ TEST_F(AllocEngine6Test, DISABLED_reserved2AddressesSolicit) {
 
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100);
 
-    AllocEngine::ClientContext6 ctx1(subnet_, duid_, iaid_, IOAddress("::"),
-                                    pool_->getType(), false, false, "", true);
-    ctx1.query_.reset(new Pkt6(DHCPV6_SOLICIT, 1234));
+    AllocEngine::ClientContext6 ctx1(subnet_, duid_, false, false, "", true,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+    ctx1.currentIA().iaid_ = iaid_;
+    ctx1.currentIA().type_ = pool_->getType();
+
     Lease6Collection leases1;
     findReservation(engine, ctx1);
     EXPECT_NO_THROW(leases1 = engine.allocateLeases6(ctx1));
@@ -1315,9 +1349,11 @@ TEST_F(AllocEngine6Test, DISABLED_reserved2AddressesSolicit) {
 
     // Double check that repeating the same duid/type/iaid will end up with
     // the same address.
-    AllocEngine::ClientContext6 ctx2(subnet_, duid_, iaid_, IOAddress("::"),
-                                    pool_->getType(), false, false, "", true);
-    ctx2.query_.reset(new Pkt6(DHCPV6_SOLICIT, 1234));
+    AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+    ctx2.currentIA().iaid_ = iaid_;
+    ctx2.currentIA().type_ = pool_->getType();
+
     Lease6Collection leases2;
     findReservation(engine, ctx2);
     EXPECT_NO_THROW(leases2 = engine.allocateLeases6(ctx2));
@@ -1326,9 +1362,11 @@ TEST_F(AllocEngine6Test, DISABLED_reserved2AddressesSolicit) {
 
     // Ok, now the tricky part. Request allocation for the same duid and type, but
     // different iaid. The second address should be assigned.
-    AllocEngine::ClientContext6 ctx3(subnet_, duid_, iaid_ + 1, IOAddress("::"),
-                                    pool_->getType(), false, false, "", true);
-    ctx3.query_.reset(new Pkt6(DHCPV6_SOLICIT, 1234));
+    AllocEngine::ClientContext6 ctx3(subnet_, duid_, false, false, "", true,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
+    ctx3.currentIA().iaid_ = iaid_ + 1;
+    ctx3.currentIA().type_ = pool_->getType();
+
     Lease6Collection leases3;
     findReservation(engine, ctx3);
     EXPECT_NO_THROW(leases3 = engine.allocateLeases6(ctx3));
@@ -1351,9 +1389,11 @@ TEST_F(AllocEngine6Test, reserved2Addresses) {
 
     AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100);
 
-    AllocEngine::ClientContext6 ctx1(subnet_, duid_, iaid_, IOAddress("::"),
-                                    pool_->getType(), false, false, "", false);
-    ctx1.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx1(subnet_, duid_, false, false, "", false,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx1.currentIA().iaid_ = iaid_;
+    ctx1.currentIA().type_ = pool_->getType();
+
     Lease6Collection leases1;
     findReservation(engine, ctx1);
     EXPECT_NO_THROW(leases1 = engine.allocateLeases6(ctx1));
@@ -1362,9 +1402,10 @@ TEST_F(AllocEngine6Test, reserved2Addresses) {
 
     // Double check that repeating the same duid/type/iaid will end up with
     // the same address.
-    AllocEngine::ClientContext6 ctx2(subnet_, duid_, iaid_, IOAddress("::"),
-                                    pool_->getType(), false, false, "", false);
-    ctx2.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", false,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx2.currentIA().iaid_ = iaid_;
+    ctx2.currentIA().type_ = pool_->getType();
 
     Lease6Collection leases2;
     findReservation(engine, ctx2);
@@ -1374,9 +1415,10 @@ TEST_F(AllocEngine6Test, reserved2Addresses) {
 
     // Ok, now the tricky part. Request allocation for the same duid and type, but
     // different iaid. The second address should be assigned.
-    AllocEngine::ClientContext6 ctx3(subnet_, duid_, iaid_ + 1, IOAddress("::"),
-                                    pool_->getType(), false, false, "", false);
-    ctx3.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx3(subnet_, duid_, false, false, "", false,
+                                     Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
+    ctx3.currentIA().iaid_ = iaid_ + 1;
+    ctx3.currentIA().type_ = pool_->getType();
 
     Lease6Collection leases3;
     findReservation(engine, ctx3);

+ 5 - 6
src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc

@@ -1415,14 +1415,13 @@ ExpirationAllocEngine6Test::testReclaimReusedLeases(const uint16_t msg_type,
 
     for (unsigned int i = 0; i < TEST_LEASES_NUM; ++i) {
         // Build the context.
-        AllocEngine::ClientContext6 ctx(subnet, leases_[i]->duid_, 1,
-                                        leases_[i]->addr_,
-                                        Lease::TYPE_NA,
+        AllocEngine::ClientContext6 ctx(subnet, leases_[i]->duid_,
                                         false, false,
                                         leases_[i]->hostname_,
-                                        msg_type == DHCPV6_SOLICIT);
-        // Query is needed for logging purposes.
-        ctx.query_.reset(new Pkt6(msg_type, 0x1234));
+                                        msg_type == DHCPV6_SOLICIT,
+                                        Pkt6Ptr(new Pkt6(msg_type, 0x1234)));
+        ctx.currentIA().iaid_ = 1;
+        ctx.currentIA().hints_.push_back(std::make_pair(leases_[i]->addr_, 128));
 
         // Depending on the message type, we will call a different function.
         if (msg_type == DHCPV6_RENEW) {

+ 9 - 14
src/lib/dhcpsrv/tests/alloc_engine_hooks_unittest.cc

@@ -148,13 +148,12 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease6_select", lease6_select_callout));
 
-    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
-
     Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA, false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
-    ctx.callout_handle_ = callout_handle;
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)),
+                                    HooksManager::createCalloutHandle());
+    ctx.currentIA().iaid_ = iaid_;
+
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
     // Check that we got a lease
     ASSERT_TRUE(lease);
@@ -223,16 +222,12 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
                         "lease6_select", lease6_select_different_callout));
 
-    // Normally, dhcpv6_srv would passed the handle when calling allocateLeases6,
-    // but in tests we need to create it on our own.
-    CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
-
     // Call allocateLeases6. Callouts should be triggered here.
     Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    Lease::TYPE_NA, false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
-    ctx.callout_handle_ = callout_handle;
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)),
+                                    HooksManager::createCalloutHandle());
+    ctx.currentIA().iaid_ = iaid_;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
     // Check that we got a lease
     ASSERT_TRUE(lease);

+ 50 - 22
src/lib/dhcpsrv/tests/alloc_engine_utils.cc

@@ -208,9 +208,13 @@ AllocEngine6Test::allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
     Lease::Type type = pool->getType();
     uint8_t expected_len = pool->getLength();
 
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type,
-                                    false, false, "", fake);
-    ctx.query_.reset(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+    Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
+                                    fake, query);
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().type_ = type;
+    ctx.currentIA().addHint(hint);
 
     Lease6Collection leases;
 
@@ -222,6 +226,10 @@ AllocEngine6Test::allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
         // Do all checks on the lease
         checkLease6(*it, type, expected_len, in_pool, in_pool);
 
+        // Check that context has been updated with allocated addresses or
+        // prefixes.
+        checkAllocatedResources(*it, ctx);
+
         // Check that the lease is indeed in LeaseMgr
         Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
                                                                    (*it)->addr_);
@@ -266,14 +274,17 @@ AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
         return (Lease6Ptr());
     }
 
-    Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type,
-                                    false, false, "", fake);
+    Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", fake, query);
     ctx.hwaddr_ = hwaddr_;
     ctx.addHostIdentifier(Host::IDENT_HWADDR, hwaddr_->hwaddr_);
-    ctx.query_.reset(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().type_ = type;
+    ctx.currentIA().addHint(hint);
 
     findReservation(*engine, ctx);
+    Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
 
     // Check that we got a lease
@@ -315,11 +326,12 @@ AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
     Lease::Type type = pool->getType();
     uint8_t expected_len = pool->getLength();
 
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress("::"),
-                                    type, false, false, "", false);
-    ctx.hints_ = hints;
-    ctx.query_.reset(new Pkt6(DHCPV6_RENEW, 123));
-    ctx.query_.reset(new Pkt6(DHCPV6_RENEW, 1234));
+    Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
+                                    false, query);
+    ctx.currentIA().hints_ = hints;
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().type_ = type;
 
     findReservation(engine, ctx);
     Lease6Collection leases = engine.renewLeases6(ctx);
@@ -329,6 +341,10 @@ AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
         // Do all checks on the lease
         checkLease6(*it, type, expected_len, in_pool, in_pool);
 
+        // Check that context has been updated with allocated addresses or
+        // prefixes.
+        checkAllocatedResources(*it, ctx);
+
         // Check that the lease is indeed in LeaseMgr
         Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
                                                                    (*it)->addr_);
@@ -366,10 +382,15 @@ AllocEngine6Test::allocWithUsedHintTest(Lease::Type type, IOAddress used_addr,
     // Another client comes in and request an address that is in pool, but
     // unfortunately it is used already. The same address must not be allocated
     // twice.
+
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    query);
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().type_ = type;
+    ctx.currentIA().addHint(requested);
+
     Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, requested, type,
-                                    false, false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
 
     // Check that we got a lease
@@ -403,11 +424,15 @@ AllocEngine6Test::allocBogusHint6(Lease::Type type, asiolink::IOAddress hint,
     // Client would like to get a 3000::abc lease, which does not belong to any
     // supported lease. Allocation engine should ignore it and carry on
     // with the normal allocation
-    Lease6Ptr lease;
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, hint, type, false,
-                                    false, "", false);
-    ctx.query_.reset(new Pkt6(DHCPV6_REQUEST, 1234));
 
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+                                    query);
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().type_ = type;
+    ctx.currentIA().addHint(hint);
+
+    Lease6Ptr lease;
     EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
 
     // Check that we got a lease
@@ -448,9 +473,12 @@ AllocEngine6Test::testReuseLease6(const AllocEnginePtr& engine,
     }
 
     // A client comes along, asking specifically for a given address
-    AllocEngine::ClientContext6 ctx(subnet_, duid_, iaid_, IOAddress(addr),
-                                    Lease::TYPE_NA, false, false, "", fake_allocation);
-    ctx.query_.reset(new Pkt6(fake_allocation ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+
+    Pkt6Ptr query(new Pkt6(fake_allocation ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
+                                    fake_allocation, query);
+    ctx.currentIA().iaid_ = iaid_;
+    ctx.currentIA().addHint(IOAddress(addr));
 
     Lease6Collection leases;
 

+ 12 - 1
src/lib/dhcpsrv/tests/alloc_engine_utils.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 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
@@ -190,6 +190,17 @@ public:
         /// @todo: check cltt
     }
 
+    /// @brief Checks if specified address or prefix has been recorded as
+    /// allocated to the client.
+    ///
+    /// @param lease Allocated lease.
+    /// @param ctx Context structure in which this function should check if
+    /// leased address is stored as allocated resource.
+    void checkAllocatedResources(const Lease6Ptr& lease,
+                                 AllocEngine::ClientContext6& ctx) {
+        EXPECT_TRUE(ctx.isAllocated(lease->addr_, lease->prefixlen_));
+    }
+
     /// @brief Checks if specified address is increased properly
     ///
     /// Method uses gtest macros to mark check failure. This is a proxy