Browse Source

[3152] Dhcpv6Srv is now able to allocate prefixes

Tomek Mrugalski 11 years ago
parent
commit
1ef8a393e8

+ 38 - 11
src/bin/dhcp6/dhcp6_messages.mes

@@ -166,26 +166,48 @@ A "libreload" command was issued to reload the hooks libraries but for
 some reason the reload failed.  Other error messages issued from the
 some reason the reload failed.  Other error messages issued from the
 hooks framework will indicate the nature of the problem.
 hooks framework will indicate the nature of the problem.
 
 
-% DHCP6_LEASE_ADVERT lease %1 advertised (client duid=%2, iaid=%3)
+% DHCP6_LEASE_ADVERT address lease %1 advertised (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully advertised
 This debug message indicates that the server successfully advertised
-a lease. It is up to the client to choose one server out of the
+an address lease. It is up to the client to choose one server out of the
 advertised servers and continue allocation with that server. This
 advertised servers and continue allocation with that server. This
 is a normal behavior and indicates successful operation.
 is a normal behavior and indicates successful operation.
 
 
-% DHCP6_LEASE_ADVERT_FAIL failed to advertise a lease for client duid=%1, iaid=%2
+% DHCP6_PD_LEASE_ADVERT prefix lease %1/%2 advertised (client duid=%3, iaid=%4)
+This debug message indicates that the server successfully advertised
+a prefix lease. It is up to the client to choose one server out of the
+advertised servers and continue allocation with that server. This
+is a normal behavior and indicates successful operation.
+
+% DHCP6_LEASE_ADVERT_FAIL failed to advertise an address lease for client duid=%1, iaid=%2
+This message indicates that the server failed to advertise (in response to
+received SOLICIT) a non-temporary lease for a given client. There may be many
+reasons for such failure. Each specific failure is logged in a separate log entry.
+
+% DHCP6_PD_LEASE_ADVERT_FAIL failed to advertise a prefix lease for client duid=%1, iaid=%2
 This message indicates that the server failed to advertise (in response to
 This message indicates that the server failed to advertise (in response to
-received SOLICIT) a lease for a given client. There may be many reasons for
+received SOLICIT) a prefix lease for a given client. There may be many reasons
-such failure. Each specific failure is logged in a separate log entry.
+for such failure. Each specific failure is logged in a separate log entry.
 
 
-% DHCP6_LEASE_ALLOC lease %1 has been allocated (client duid=%2, iaid=%3)
+% DHCP6_LEASE_ALLOC address lease %1 has been allocated (client duid=%2, iaid=%3)
 This debug message indicates that the server successfully granted (in
 This debug message indicates that the server successfully granted (in
-response to client's REQUEST message) a lease. This is a normal behavior
+response to client's REQUEST message) an non-temporary address lease. This is a
-and indicates successful operation.
+normal behavior and indicates successful operation.
+
+% DHCP6_PD_LEASE_ALLOC prefix lease %1/%2 has been allocated (client duid=%3, iaid=%4)
+This debug message indicates that the server successfully granted (in response
+to client's REQUEST message) an prefix delegation lease. This is a normal
+behavior and indicates successful operation.
 
 
-% DHCP6_LEASE_ALLOC_FAIL failed to grant a lease for client duid=%1, iaid=%2
+% DHCP6_LEASE_ALLOC_FAIL failed to grant an address lease for client duid=%1, iaid=%2
 This message indicates that the server failed to grant (in response to
 This message indicates that the server failed to grant (in response to
-received REQUEST) a lease for a given client. There may be many reasons for
+received REQUEST) a non-temporary address lease for a given client. There may be
-such failure. Each specific failure is logged in a separate log entry.
+many reasons for such failure. Each specific failure is logged in a separate
+log entry.
+
+% DHCP6_PD_LEASE_ALLOC_FAIL failed to grant a prefix lease for client duid=%1, iaid=%2
+This message indicates that the server failed to grant (in response to
+received REQUEST) a prefix lease for a given client. There may be many reasons
+for such failure. Each specific failure is logged in a separate log entry.
 
 
 % DHCP6_LEASE_WITHOUT_DUID lease for address %1 does not have a DUID
 % DHCP6_LEASE_WITHOUT_DUID lease for address %1 does not have a DUID
 This error message indicates a database consistency failure. The lease
 This error message indicates a database consistency failure. The lease
@@ -282,6 +304,11 @@ This is a debug message that indicates a processing of received IA_NA
 option. It may optionally contain an address that may be used by the server
 option. It may optionally contain an address that may be used by the server
 as a hint for possible requested address.
 as a hint for possible requested address.
 
 
+% DHCP6_PROCESS_IA_PD_REQUEST server is processing IA_PD option (duid=%1, iaid=%2, hint=%3)
+This is a debug message that indicates a processing of received IA_PD
+option. It may optionally contain an prefix that may be used by the server
+as a hint for possible requested prefix.
+
 % DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 % DHCP6_QUERY_DATA received packet length %1, data length %2, data is %3
 A debug message listing the data received from the client or relay.
 A debug message listing the data received from the client or relay.
 
 

+ 123 - 4
src/bin/dhcp6/dhcp6_srv.cc

@@ -24,7 +24,7 @@
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
@@ -433,10 +433,10 @@ bool Dhcpv6Srv::run() {
 
 
                     // Pass incoming packet as argument
                     // Pass incoming packet as argument
                     callout_handle->setArgument("response6", rsp);
                     callout_handle->setArgument("response6", rsp);
-                    
+
                     // Call callouts
                     // Call callouts
                     HooksManager::callCallouts(Hooks.hook_index_buffer6_send_, *callout_handle);
                     HooksManager::callCallouts(Hooks.hook_index_buffer6_send_, *callout_handle);
-                    
+
                     // Callouts decided to skip the next processing step. The next
                     // Callouts decided to skip the next processing step. The next
                     // processing step would to parse the packet, so skip at this
                     // processing step would to parse the packet, so skip at this
                     // stage means drop.
                     // stage means drop.
@@ -444,7 +444,7 @@ bool Dhcpv6Srv::run() {
                         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP);
                         LOG_DEBUG(dhcp6_logger, DBG_DHCP6_HOOKS, DHCP6_HOOK_BUFFER_SEND_SKIP);
                         continue;
                         continue;
                     }
                     }
-                    
+
                     callout_handle->getArgument("response6", rsp);
                     callout_handle->getArgument("response6", rsp);
                 }
                 }
 
 
@@ -875,6 +875,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
             }
             }
             break;
             break;
         }
         }
+        case D6O_IA_PD: {
+            OptionPtr answer_opt = assignIA_PD(subnet, duid, question,
+                                               boost::dynamic_pointer_cast<
+                                               Option6IA>(opt->second));
+            if (answer_opt) {
+                answer->addOption(answer_opt);
+            }
+        }
         default:
         default:
             break;
             break;
         }
         }
@@ -1303,6 +1311,117 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 }
 
 
 OptionPtr
 OptionPtr
+Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
+    // If there is no subnet selected for handling this IA_PD, the only thing to
+    // do left is to say that we are sorry, but the user won't get an address.
+    // As a convenience, we use a different status text to indicate that
+    // (compare to the same status code, but different wording below)
+    if (!subnet) {
+        // Create empty IA_PD option with IAID matching the request.
+        // Note that we don't use OptionDefinition class to create this option.
+        // This is because we prefer using a constructor of Option6IA that
+        // initializes IAID. Otherwise we would have to use setIAID() after
+        // creation of the option which has some performance implications.
+        boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD,
+                                                          ia->getIAID()));
+
+        // Insert status code NoAddrsAvail.
+        ia_rsp->addOption(createStatusCode(STATUS_NoPrefixAvail,
+                                           "Sorry, no subnet available."));
+        return (ia_rsp);
+    }
+
+    // Check if the client sent us a hint in his IA_PD. Clients may send an
+    // address in their IA_NA options as a suggestion (e.g. the last address
+    // they used before).
+    boost::shared_ptr<Option6IAPrefix> hintOpt =
+      boost::dynamic_pointer_cast<Option6IAPrefix>(ia->getOption(D6O_IAPREFIX));
+    IOAddress hint("::");
+    if (hintOpt) {
+        hint = hintOpt->getAddress();
+    }
+
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
+        .arg(duid?duid->toText():"(no-duid)").arg(ia->getIAID())
+        .arg(hintOpt?hint.toText():"(no hint)");
+
+    // "Fake" allocation is processing of SOLICIT message. We pretend to do an
+    // allocation, but we do not put the lease in the database. That is ok,
+    // because we do not guarantee that the user will get that exact lease. If
+    // the user selects this server to do actual allocation (i.e. sends REQUEST)
+    // it should include this hint. That will help us during the actual lease
+    // allocation.
+    bool fake_allocation = false;
+    if (query->getType() == DHCPV6_SOLICIT) {
+        /// @todo: Check if we support rapid commit
+        fake_allocation = true;
+    }
+
+    CalloutHandlePtr callout_handle = getCalloutHandle(query);
+
+    // Use allocation engine to pick a lease for this client. Allocation engine
+    // will try to honour 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.
+    Lease6Collection leases = alloc_engine_->allocateLease6(subnet, duid,
+                                                            ia->getIAID(),
+                                                            hint, Lease::TYPE_PD,
+                                                            false, false,
+                                                            string(),
+                                                            fake_allocation,
+                                                            callout_handle);
+
+    // Create IA_PD that we will put in the response.
+    // Do not use OptionDefinition to create option's instance so
+    // as we can initialize IAID using a constructor.
+    boost::shared_ptr<Option6IA> ia_rsp(new Option6IA(D6O_IA_PD, ia->getIAID()));
+
+    if (!leases.empty()) {
+
+        ia_rsp->setT1(subnet->getT1());
+        ia_rsp->setT2(subnet->getT2());
+
+        for (Lease6Collection::iterator l = leases.begin();
+             l != leases.end(); ++l) {
+
+            // We have a lease! Let's wrap its content into IA_PD option
+            // with IAADDR suboption.
+            LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation?
+                      DHCP6_PD_LEASE_ADVERT:DHCP6_PD_LEASE_ALLOC)
+                .arg((*l)->addr_.toText())
+                .arg(static_cast<int>((*l)->prefixlen_))
+                .arg(duid?duid->toText():"(no-duid)")
+                .arg(ia->getIAID());
+
+            boost::shared_ptr<Option6IAPrefix>
+                addr(new Option6IAPrefix(D6O_IAPREFIX, (*l)->addr_,
+                                         (*l)->prefixlen_, (*l)->preferred_lft_,
+                                         (*l)->valid_lft_));
+            ia_rsp->addOption(addr);
+        }
+
+        // It would be possible to insert status code=0(success) as well,
+        // but this is considered waste of bandwidth as absence of status
+        // code is considered a success.
+
+    } else {
+        // Allocation engine did not allocate a lease. The engine logged
+        // cause of that failure. The only thing left is to insert
+        // status code to pass the sad news to the client.
+
+        LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, fake_allocation ?
+                  DHCP6_PD_LEASE_ADVERT_FAIL : DHCP6_PD_LEASE_ALLOC_FAIL)
+            .arg(duid?duid->toText():"(no-duid)")
+            .arg(ia->getIAID());
+
+        ia_rsp->addOption(createStatusCode(STATUS_NoPrefixAvail,
+                          "Sorry, no prefixes could be allocated."));
+    }
+    return (ia_rsp);
+}
+
+OptionPtr
 Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 Dhcpv6Srv::renewIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
                       const Option6ClientFqdnPtr& fqdn) {
                       const Option6ClientFqdnPtr& fqdn) {

+ 18 - 1
src/bin/dhcp6/dhcp6_srv.h

@@ -206,7 +206,7 @@ protected:
     /// @brief Processes IA_NA option (and assigns addresses if necessary).
     /// @brief Processes IA_NA option (and assigns addresses if necessary).
     ///
     ///
     /// Generates response to IA_NA. This typically includes selecting (and
     /// Generates response to IA_NA. This typically includes selecting (and
-    /// allocating a lease in case of REQUEST) a lease and creating
+    /// allocating a lease in case of REQUEST) an address lease and creating
     /// IAADDR option. In case of allocation failure, it may contain
     /// IAADDR option. In case of allocation failure, it may contain
     /// status code option with non-zero status, denoting cause of the
     /// status code option with non-zero status, denoting cause of the
     /// allocation failure.
     /// allocation failure.
@@ -224,6 +224,23 @@ protected:
                           Option6IAPtr ia,
                           Option6IAPtr ia,
                           const Option6ClientFqdnPtr& fqdn);
                           const Option6ClientFqdnPtr& fqdn);
 
 
+    /// @brief Processes IA_PD option (and assigns prefixes if necessary).
+    ///
+    /// Generates response to IA_PD. This typically includes selecting (and
+    /// allocating a lease in case of REQUEST) a prefix lease and creating
+    /// IAPREFIX option. In case of allocation failure, it may contain
+    /// status code option with non-zero status, denoting cause of the
+    /// allocation failure.
+    ///
+    /// @param subnet subnet the client is connected to
+    /// @param duid client's duid
+    /// @param query client's message (typically SOLICIT or REQUEST)
+    /// @param ia pointer to client's IA_PD option (client's request)
+    /// @return IA_PD option (server's response)
+    OptionPtr assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                          const Pkt6Ptr& query,
+                          boost::shared_ptr<Option6IA> ia);
+
     /// @brief Renews specific IA_NA option
     /// @brief Renews specific IA_NA option
     ///
     ///
     /// Generates response to IA_NA in Renew. This typically includes finding a
     /// Generates response to IA_NA in Renew. This typically includes finding a

+ 1 - 1
src/bin/dhcp6/tests/Makefile.am

@@ -60,7 +60,7 @@ TESTS += dhcp6_unittests
 dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += hooks_unittest.cc
 dhcp6_unittests_SOURCES += hooks_unittest.cc
-dhcp6_unittests_SOURCES += dhcp6_test_utils.h
+dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += config_parser_unittest.cc
 dhcp6_unittests_SOURCES += marker_file.cc
 dhcp6_unittests_SOURCES += marker_file.cc

+ 137 - 22
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -97,7 +97,7 @@ public:
                                 OptionPtr srvid = OptionPtr()) {
                                 OptionPtr srvid = OptionPtr()) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         pkt->setRemoteAddr(IOAddress("fe80::abcd"));
         pkt->setRemoteAddr(IOAddress("fe80::abcd"));
-        Option6IAPtr ia = generateIA(234, 1500, 3000);
+        Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
 
         if (msg_type != DHCPV6_REPLY) {
         if (msg_type != DHCPV6_REPLY) {
             IOAddress hint("2001:db8:1:1::dead:beef");
             IOAddress hint("2001:db8:1:1::dead:beef");
@@ -149,7 +149,7 @@ public:
 
 
     // Adds IA option to the message. Option holds an address.
     // Adds IA option to the message. Option holds an address.
     void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
     void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
-        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
         Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
         Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
                                                       300, 500));
                                                       300, 500));
         opt_ia->addOption(opt_iaaddr);
         opt_ia->addOption(opt_iaaddr);
@@ -158,7 +158,7 @@ public:
 
 
     // Adds IA option to the message. Option holds status code.
     // Adds IA option to the message. Option holds status code.
     void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
     void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
-        Option6IAPtr opt_ia = generateIA(iaid, 1500, 3000);
+        Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
         addStatusCode(status_code, "", opt_ia);
         addStatusCode(status_code, "", opt_ia);
         pkt->addOption(opt_ia);
         pkt->addOption(opt_ia);
     }
     }
@@ -312,7 +312,7 @@ TEST_F(NakedDhcpv6SrvTest, SolicitNoSubnet) {
 
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
     sol->addOption(clientid);
 
 
@@ -335,7 +335,7 @@ TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
     // Let's create a REQUEST
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
 
     // with a hint
     // with a hint
     IOAddress hint("2001:db8:1:1::dead:beef");
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -373,7 +373,7 @@ TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -408,7 +408,7 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     // Let's create a RELEASE
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
     ia->addOption(released_addr_opt);
@@ -563,7 +563,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
     sol->addOption(clientid);
 
 
@@ -647,7 +647,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
     sol->addOption(clientid);
 
 
@@ -672,6 +672,53 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 }
 }
 
 
 // This test verifies that incoming SOLICIT can be handled properly, that an
 // This test verifies that incoming SOLICIT can be handled properly, that an
+// ADVERTISE is generated, that the response has a prefix and that prefix
+// really belongs to the configured pool.
+//
+// This test sends a SOLICIT without any hint in IA_PD.
+//
+// constructed very simple SOLICIT message with:
+// - client-id option (mandatory)
+// - IA option (a request for address, without any addresses)
+//
+// expected returned ADVERTISE message:
+// - copy of client-id
+// - server-id
+// - IA that includes IAPREFIX
+TEST_F(Dhcpv6SrvTest, pdSolicitBasic) {
+
+    configurePdPool();
+
+    NakedDhcpv6Srv srv(0);
+
+    Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+    sol->setRemoteAddr(IOAddress("fe80::abcd"));
+    sol->addOption(generateIA(D6O_IA_PD, 234, 1500, 3000));
+    OptionPtr clientid = generateClientId();
+    sol->addOption(clientid);
+
+    // Pass it to the server and get an advertise
+    Pkt6Ptr reply = srv.processSolicit(sol);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prefix = checkIA_PD(reply, 234, subnet_->getT1(),
+                                                           subnet_->getT2());
+    ASSERT_TRUE(prefix);
+
+    // Check that the assigned prefix is indeed from the configured pool
+    checkIAAddr(prefix, prefix->getAddress(), Lease::TYPE_PD,
+                subnet_->getPreferred(), subnet_->getValid());
+    EXPECT_EQ(pd_pool_->getLength(), prefix->getLength());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+}
+
+// This test verifies that incoming SOLICIT can be handled properly, that an
 // ADVERTISE is generated, that the response has an address and that address
 // ADVERTISE is generated, that the response has an address and that address
 // really belongs to the configured pool.
 // really belongs to the configured pool.
 //
 //
@@ -692,7 +739,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     // Let's create a SOLICIT
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
 
     // with a valid hint
     // with a valid hint
     IOAddress hint("2001:db8:1:1::dead:beef");
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -747,7 +794,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     // Let's create a SOLICIT
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
     IOAddress hint("2001:db8:1::cafe:babe");
     IOAddress hint("2001:db8:1::cafe:babe");
     ASSERT_FALSE(subnet_->inPool(Lease::TYPE_NA, hint));
     ASSERT_FALSE(subnet_->inPool(Lease::TYPE_NA, hint));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
@@ -798,9 +845,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     sol2->setRemoteAddr(IOAddress("fe80::1223"));
     sol2->setRemoteAddr(IOAddress("fe80::1223"));
     sol3->setRemoteAddr(IOAddress("fe80::3467"));
     sol3->setRemoteAddr(IOAddress("fe80::3467"));
 
 
-    sol1->addOption(generateIA(1, 1500, 3000));
+    sol1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
-    sol2->addOption(generateIA(2, 1500, 3000));
+    sol2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
-    sol3->addOption(generateIA(3, 1500, 3000));
+    sol3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
 
     // different client-id sizes
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
     OptionPtr clientid1 = generateClientId(12);
@@ -878,7 +925,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     // Let's create a REQUEST
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(234, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
 
 
     // with a valid hint
     // with a valid hint
     IOAddress hint("2001:db8:1:1::dead:beef");
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -921,6 +968,74 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     LeaseMgrFactory::instance().deleteLease(addr->getAddress());
     LeaseMgrFactory::instance().deleteLease(addr->getAddress());
 }
 }
 
 
+// This test verifies that incoming REQUEST can be handled properly, that a
+// REPLY is generated, that the response has a prefix and that prefix
+// really belongs to the configured pool.
+//
+// This test sends a REQUEST with IA_PD that contains a valid hint.
+//
+// constructed very simple REQUEST message with:
+// - client-id option (mandatory)
+// - IA option (a request for address, with an address that belongs to the
+//              configured pool, i.e. is valid as hint)
+//
+// expected returned REPLY message:
+// - copy of client-id
+// - server-id
+// - IA that includes IAPREFIX
+TEST_F(Dhcpv6SrvTest, pdRequestBasic) {
+
+    configurePdPool();
+
+    NakedDhcpv6Srv srv(0);
+
+    // Let's create a REQUEST
+    Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+    req->setRemoteAddr(IOAddress("fe80::abcd"));
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_PD, 234, 1500, 3000);
+
+    // with a valid hint
+    IOAddress hint("2001:db8:1:2:f::");
+    ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, hint));
+    OptionPtr hint_opt(new Option6IAPrefix(D6O_IAPREFIX, hint, 64, 300, 500));
+    ia->addOption(hint_opt);
+    req->addOption(ia);
+    OptionPtr clientid = generateClientId();
+    req->addOption(clientid);
+
+    // server-id is mandatory in REQUEST
+    req->addOption(srv.getServerID());
+
+    // Pass it to the server and hope for a REPLY
+    Pkt6Ptr reply = srv.processRequest(req);
+
+    // check if we get response at all
+    checkResponse(reply, DHCPV6_REPLY, 1234);
+
+    OptionPtr tmp = reply->getOption(D6O_IA_PD);
+    ASSERT_TRUE(tmp);
+
+    // check that IA_NA was returned and that there's an address included
+    boost::shared_ptr<Option6IAPrefix> prf = checkIA_PD(reply, 234,
+                                                        subnet_->getT1(),
+                                                        subnet_->getT2());
+    ASSERT_TRUE(prf);
+
+    // check that we've got the address we requested
+    checkIAAddr(prf, hint, Lease::TYPE_PD, subnet_->getPreferred(),
+                subnet_->getValid());
+    EXPECT_EQ(pd_pool_->getLength(), prf->getLength());
+
+    // check DUIDs
+    checkServerId(reply, srv.getServerID());
+    checkClientId(reply, clientid);
+
+    // check that the lease is really in the database
+    Lease6Ptr l = checkPdLease(duid_, reply->getOption(D6O_IA_PD), prf);
+    EXPECT_TRUE(l);
+    EXPECT_TRUE(LeaseMgrFactory::instance().deleteLease(prf->getAddress()));
+}
+
 // This test checks that the server is offering different addresses to different
 // This test checks that the server is offering different addresses to different
 // clients in REQUEST. Please note that ADVERTISE is not a guarantee that such
 // clients in REQUEST. Please note that ADVERTISE is not a guarantee that such
 // and address will be assigned. Had the pool was very small and contained only
 // and address will be assigned. Had the pool was very small and contained only
@@ -941,9 +1056,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req2->setRemoteAddr(IOAddress("fe80::1223"));
     req2->setRemoteAddr(IOAddress("fe80::1223"));
     req3->setRemoteAddr(IOAddress("fe80::3467"));
     req3->setRemoteAddr(IOAddress("fe80::3467"));
 
 
-    req1->addOption(generateIA(1, 1500, 3000));
+    req1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
-    req2->addOption(generateIA(2, 1500, 3000));
+    req2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
-    req3->addOption(generateIA(3, 1500, 3000));
+    req3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
 
     // different client-id sizes
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
     OptionPtr clientid1 = generateClientId(12);
@@ -1050,7 +1165,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -1136,7 +1251,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, transid));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, transid));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, bogus_iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -1247,7 +1362,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
     // Let's create a RELEASE
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
     ia->addOption(released_addr_opt);
@@ -1324,7 +1439,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     // Let's create a RELEASE
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(bogus_iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, bogus_iaid, 1500, 3000);
 
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
     ia->addOption(released_addr_opt);

+ 130 - 0
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -0,0 +1,130 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <gtest/gtest.h>
+#include <dhcp6/tests/dhcp6_test_utils.h>
+
+namespace isc {
+namespace test {
+
+// Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
+// It returns IAADDR option for each chaining with checkIAAddr method.
+boost::shared_ptr<Option6IAAddr>
+Dhcpv6SrvTest::checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                          uint32_t expected_t1, uint32_t expected_t2) {
+    OptionPtr tmp = rsp->getOption(D6O_IA_NA);
+    // Can't use ASSERT_TRUE() in method that returns something
+    if (!tmp) {
+        ADD_FAILURE() << "IA_NA option not present in response";
+        return (boost::shared_ptr<Option6IAAddr>());
+    }
+
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    if (!ia) {
+        ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
+        return (boost::shared_ptr<Option6IAAddr>());
+    }
+
+    EXPECT_EQ(expected_iaid, ia->getIAID());
+    EXPECT_EQ(expected_t1, ia->getT1());
+    EXPECT_EQ(expected_t2, ia->getT2());
+
+    tmp = ia->getOption(D6O_IAADDR);
+    boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
+    return (addr);
+}
+
+boost::shared_ptr<Option6IAPrefix>
+Dhcpv6SrvTest::checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                          uint32_t expected_t1, uint32_t expected_t2) {
+    OptionPtr tmp = rsp->getOption(D6O_IA_PD);
+    // Can't use ASSERT_TRUE() in method that returns something
+    if (!tmp) {
+        ADD_FAILURE() << "IA_PD option not present in response";
+        return (boost::shared_ptr<Option6IAPrefix>());
+    }
+
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
+    if (!ia) {
+        ADD_FAILURE() << "IA_PD cannot convert option ptr to Option6";
+        return (boost::shared_ptr<Option6IAPrefix>());
+    }
+
+    EXPECT_EQ(expected_iaid, ia->getIAID());
+    EXPECT_EQ(expected_t1, ia->getT1());
+    EXPECT_EQ(expected_t2, ia->getT2());
+
+    tmp = ia->getOption(D6O_IAPREFIX);
+    boost::shared_ptr<Option6IAPrefix> addr = boost::dynamic_pointer_cast<Option6IAPrefix>(tmp);
+    return (addr);
+}
+
+// Checks if the lease sent to client is present in the database
+// and is valid when checked agasint the configured subnet
+Lease6Ptr
+Dhcpv6SrvTest::checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
+                          boost::shared_ptr<Option6IAAddr> addr) {
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+                                                            addr->getAddress());
+    if (!lease) {
+        std::cout << "Lease for " << addr->getAddress().toText()
+                  << " not found in the database backend.";
+        return (Lease6Ptr());
+    }
+
+    EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText());
+    EXPECT_TRUE(*lease->duid_ == *duid);
+    EXPECT_EQ(ia->getIAID(), lease->iaid_);
+    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+
+    return (lease);
+}
+
+Lease6Ptr
+Dhcpv6SrvTest::checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
+                            boost::shared_ptr<Option6IAPrefix> prefix){
+    boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_pd);
+
+    Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD,
+                                                            prefix->getAddress());
+    if (!lease) {
+        std::cout << "PD lease for " << prefix->getAddress().toText()
+                  << " not found in the database backend.";
+        return (Lease6Ptr());
+    }
+
+    EXPECT_EQ(prefix->getAddress().toText(), lease->addr_.toText());
+    EXPECT_TRUE(*lease->duid_ == *duid);
+    EXPECT_EQ(ia->getIAID(), lease->iaid_);
+    EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
+
+    return (lease);
+}
+
+
+// Generate IA_NA option with specified parameters
+boost::shared_ptr<Option6IA>
+NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1,
+                               uint32_t t2) {
+    boost::shared_ptr<Option6IA> ia =
+        boost::shared_ptr<Option6IA>(new Option6IA(type, iaid));
+    ia->setT1(t1);
+    ia->setT2(t2);
+    return (ia);
+}
+
+}; // end of isc::test namespace
+}; // end of isc namespace

+ 43 - 49
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -16,11 +16,15 @@
 ///
 ///
 /// @brief  This file contains utility classes used for DHCPv6 server testing
 /// @brief  This file contains utility classes used for DHCPv6 server testing
 
 
+#ifndef DHCP6_TEST_UTILS_H
+#define DHCP6_TEST_UTILS_H
+
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 
 
 #include <dhcp/pkt6.h>
 #include <dhcp/pkt6.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/iface_mgr.h>
 #include <dhcp/iface_mgr.h>
@@ -145,14 +149,9 @@ public:
         valid_iface_ = ifaces.begin()->getName();
         valid_iface_ = ifaces.begin()->getName();
     }
     }
 
 
-    // Generate IA_NA option with specified parameters
+    // Generate IA_NA or IA_PD option with specified parameters
-    boost::shared_ptr<Option6IA> generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) {
+    boost::shared_ptr<Option6IA> generateIA(uint16_t type, uint32_t iaid,
-        boost::shared_ptr<Option6IA> ia =
+                                            uint32_t t1, uint32_t t2);
-            boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, iaid));
-        ia->setT1(t1);
-        ia->setT2(t2);
-        return (ia);
-    }
 
 
     /// @brief generates interface-id option, based on text
     /// @brief generates interface-id option, based on text
     ///
     ///
@@ -328,32 +327,35 @@ public:
         CfgMgr::instance().addSubnet6(subnet_);
         CfgMgr::instance().addSubnet6(subnet_);
     }
     }
 
 
-    // Checks that server response (ADVERTISE or REPLY) contains proper IA_NA option
+    void configurePdPool() {
-    // It returns IAADDR option for each chaining with checkIAAddr method.
+        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 64, 80));
-    boost::shared_ptr<Option6IAAddr> checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+        subnet_->addPool(pd_pool_);
-                                            uint32_t expected_t1, uint32_t expected_t2) {
-        OptionPtr tmp = rsp->getOption(D6O_IA_NA);
-        // Can't use ASSERT_TRUE() in method that returns something
-        if (!tmp) {
-            ADD_FAILURE() << "IA_NA option not present in response";
-            return (boost::shared_ptr<Option6IAAddr>());
-        }
-
-        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(tmp);
-        if (!ia) {
-            ADD_FAILURE() << "IA_NA cannot convert option ptr to Option6";
-            return (boost::shared_ptr<Option6IAAddr>());
-        }
-
-        EXPECT_EQ(expected_iaid, ia->getIAID());
-        EXPECT_EQ(expected_t1, ia->getT1());
-        EXPECT_EQ(expected_t2, ia->getT2());
-
-        tmp = ia->getOption(D6O_IAADDR);
-        boost::shared_ptr<Option6IAAddr> addr = boost::dynamic_pointer_cast<Option6IAAddr>(tmp);
-        return (addr);
     }
     }
 
 
+    /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
+    ///        IA_NA option
+    ///
+    /// @param rsp server's response
+    /// @param expected_iaid expected IAID value
+    /// @param expected_t1 expected T1 value
+    /// @param expected_t2 expected T2 value
+    /// @return IAADDR option for easy chaining with checkIAAddr method
+    boost::shared_ptr<Option6IAAddr>
+        checkIA_NA(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+                   uint32_t expected_t1, uint32_t expected_t2);
+
+    /// @brief Checks that server response (ADVERTISE or REPLY) contains proper
+    ///        IA_PD option
+    ///
+    /// @param rsp server's response
+    /// @param expected_iaid expected IAID value
+    /// @param expected_t1 expected T1 value
+    /// @param expected_t2 expected T2 value
+    /// @return IAPREFIX option for easy chaining with checkIAAddr method
+    boost::shared_ptr<Option6IAPrefix>
+    checkIA_PD(const Pkt6Ptr& rsp, uint32_t expected_iaid,
+               uint32_t expected_t1, uint32_t expected_t2);
+
     // Check that generated IAADDR option contains expected address
     // Check that generated IAADDR option contains expected address
     // and lifetime values match the configured subnet
     // and lifetime values match the configured subnet
     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
     void checkIAAddr(const boost::shared_ptr<Option6IAAddr>& addr,
@@ -375,24 +377,11 @@ public:
     // Checks if the lease sent to client is present in the database
     // Checks if the lease sent to client is present in the database
     // and is valid when checked agasint the configured subnet
     // and is valid when checked agasint the configured subnet
     Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
     Lease6Ptr checkLease(const DuidPtr& duid, const OptionPtr& ia_na,
-                         boost::shared_ptr<Option6IAAddr> addr) {
+                         boost::shared_ptr<Option6IAAddr> addr);
-        boost::shared_ptr<Option6IA> ia = boost::dynamic_pointer_cast<Option6IA>(ia_na);
-
-        Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
-                                                                addr->getAddress());
-        if (!lease) {
-            std::cout << "Lease for " << addr->getAddress().toText()
-                      << " not found in the database backend.";
-            return (Lease6Ptr());
-        }
 
 
-        EXPECT_EQ(addr->getAddress().toText(), lease->addr_.toText());
+    Lease6Ptr checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
-        EXPECT_TRUE(*lease->duid_ == *duid);
+                           boost::shared_ptr<Option6IAPrefix> prefix);
-        EXPECT_EQ(ia->getIAID(), lease->iaid_);
-        EXPECT_EQ(subnet_->getID(), lease->subnet_id_);
 
 
-        return (lease);
-    }
 
 
     ~Dhcpv6SrvTest() {
     ~Dhcpv6SrvTest() {
         CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().deleteSubnets6();
@@ -401,9 +390,14 @@ public:
     /// A subnet used in most tests
     /// A subnet used in most tests
     Subnet6Ptr subnet_;
     Subnet6Ptr subnet_;
 
 
-    /// A pool used in most tests
+    /// A normal, non-temporary pool used in most tests
     Pool6Ptr pool_;
     Pool6Ptr pool_;
+
+    /// A prefix pool used in most tests
+    Pool6Ptr pd_pool_;
 };
 };
 
 
 }; // end of isc::test namespace
 }; // end of isc::test namespace
 }; // end of isc namespace
 }; // end of isc namespace
+
+#endif // DHCP6_TEST_UTILS_H

+ 7 - 7
src/bin/dhcp6/tests/hooks_unittest.cc

@@ -951,7 +951,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
     sol->addOption(clientid);
 
 
@@ -1019,7 +1019,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
     sol->addOption(clientid);
 
 
@@ -1091,7 +1091,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -1188,7 +1188,7 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -1279,7 +1279,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     // Let's create a RENEW
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr renewed_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(renewed_addr_opt);
     ia->addOption(renewed_addr_opt);
@@ -1347,7 +1347,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_release) {
     // Let's create a RELEASE
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
     ia->addOption(released_addr_opt);
@@ -1428,7 +1428,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_release) {
     // Let's create a RELEASE
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
     req->setRemoteAddr(IOAddress("fe80::abcd"));
-    boost::shared_ptr<Option6IA> ia = generateIA(iaid, 1500, 3000);
+    boost::shared_ptr<Option6IA> ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
 
 
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     OptionPtr released_addr_opt(new Option6IAAddr(D6O_IAADDR, addr, 300, 500));
     ia->addOption(released_addr_opt);
     ia->addOption(released_addr_opt);