Browse Source

[master] Merge branch 'trac3152': PD support in solicit/request

Conflicts:
	ChangeLog
Tomek Mrugalski 11 years ago
parent
commit
a9cd7cf196

+ 5 - 0
ChangeLog

@@ -1,3 +1,8 @@
+687.	[func]		tomek
+	b10-dhcp6: Prefix Delegation (IA_PD and IAPREFIX options) is now
+	supported in Solicit and Request messages.
+	(Trac #3152, git a0e73dd74658f2deb22fad2c7a1f56d122aa9021)
+
 686.	[bug]		tomek
 	b10-dhcp6 now sends back relayed traffic to proper port.
 	(Trac #3177, git 6b33de4bea92eecb64b6c673bf1b8ae51f8edcf1)

+ 45 - 16
src/bin/dhcp6/dhcp6_messages.mes

@@ -166,26 +166,50 @@ A "libreload" command was issued to reload the hooks libraries but for
 some reason the reload failed.  Other error messages issued from the
 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
-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
 is a normal behavior and indicates successful operation.
 
-% DHCP6_LEASE_ADVERT_FAIL failed to advertise a lease for client duid=%1, iaid=%2
-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
-such failure. Each specific failure is logged in a separate log entry.
+% 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 in response to a received SOLICIT, the server
+failed to advertise a non-temporary lease for a given client. There may
+be many reasons for such failure. Each 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 in response to a received SOLICIT, the
+server failed to advertise a prefix lease for the client. There may
+be many reasons for such failure. Each failure is logged in a separate
+log entry.
+
+% DHCP6_LEASE_ALLOC address lease %1 has been allocated (client duid=%2, iaid=%3)
+This debug message indicates that in response to a client's REQUEST
+message, the server successfully granted an non-temporary address
+lease. This is a 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 in response to a client's REQUEST
+message, the server successfully granted a prefix delegation lease. This
+is a normal behavior and indicates successful operation.
 
-% DHCP6_LEASE_ALLOC lease %1 has been allocated (client duid=%2, iaid=%3)
-This debug message indicates that the server successfully granted (in
-response to client's REQUEST message) a lease. This is a normal behavior
-and indicates successful operation.
+% DHCP6_LEASE_ALLOC_FAIL failed to grant an address lease for client duid=%1, iaid=%2
+This message indicates that in response to a received REQUEST, the server
+failed to grant a non-temporary address lease for the client. There may
+be many reasons for such failure. Each failure is logged in a separate
+log entry.
 
-% DHCP6_LEASE_ALLOC_FAIL failed to grant a lease for client duid=%1, iaid=%2
+% 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 lease for a given client. There may be many reasons for
-such failure. Each specific failure is logged in a separate log entry.
+received REQUEST) a prefix lease for a given client. There may be many reasons
+for such failure. Each failure is logged in a separate log entry.
 
 % DHCP6_LEASE_WITHOUT_DUID lease for address %1 does not have a DUID
 This error message indicates a database consistency failure. The lease
@@ -278,9 +302,14 @@ parsing actions and committal of changes failed.  The reason for the
 failure is given in the message.
 
 % DHCP6_PROCESS_IA_NA_REQUEST server is processing IA_NA option (duid=%1, iaid=%2, hint=%3)
-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
-as a hint for possible requested address.
+This is a debug message that indicates the processing of a received
+IA_NA option. It may optionally contain an address that may be used by
+the server 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
 A debug message listing the data received from the client or relay.

+ 110 - 2
src/bin/dhcp6/dhcp6_srv.cc

@@ -24,7 +24,7 @@
 #include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
-#include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/pkt6.h>
@@ -825,7 +825,6 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
     // We need to allocate addresses for all IA_NA options in the client's
     // question (i.e. SOLICIT or REQUEST) message.
     // @todo add support for IA_TA
-    // @todo add support for IA_PD
 
     // We need to select a subnet the client is connected in.
     Subnet6Ptr subnet = selectSubnet(question);
@@ -883,6 +882,14 @@ Dhcpv6Srv::assignLeases(const Pkt6Ptr& question, Pkt6Ptr& answer,
             }
             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:
             break;
         }
@@ -1311,6 +1318,107 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
 }
 
 OptionPtr
+Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
+                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia) {
+
+    // 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 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) {
+
+        // 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 = (query->getType() == DHCPV6_SOLICIT);
+
+    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_->allocateLeases6(subnet, duid,
+                                                            ia->getIAID(),
+                                                            hint, Lease::TYPE_PD,
+                                                            false, false,
+                                                            string(),
+                                                            fake_allocation,
+                                                            callout_handle);
+
+    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,
                       const Pkt6Ptr& query, boost::shared_ptr<Option6IA> ia,
                       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).
     ///
     /// 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
     /// status code option with non-zero status, denoting cause of the
     /// allocation failure.
@@ -224,6 +224,23 @@ protected:
                           Option6IAPtr ia,
                           const Option6ClientFqdnPtr& fqdn);
 
+    /// @brief Processes IA_PD option (and assigns prefixes if necessary).
+    ///
+    /// Generates response to IA_PD. This typically includes selecting (and
+    /// allocating in the case of REQUEST) a prefix lease and creating an
+    /// IAPREFIX option. In case of an allocation failure, it may contain a
+    /// status code option with non-zero status denoting the 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
     ///
     /// Generates response to IA_NA in Renew. This typically includes finding a

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

@@ -70,7 +70,7 @@ TESTS += dhcp6_unittests
 dhcp6_unittests_SOURCES  = dhcp6_unittests.cc
 dhcp6_unittests_SOURCES += dhcp6_srv_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 += config_parser_unittest.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()) {
         Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
         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) {
             IOAddress hint("2001:db8:1:1::dead:beef");
@@ -149,7 +149,7 @@ public:
 
     // Adds IA option to the message. Option holds an address.
     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,
                                                       300, 500));
         opt_ia->addOption(opt_iaaddr);
@@ -158,7 +158,7 @@ public:
 
     // Adds IA option to the message. Option holds status code.
     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);
         pkt->addOption(opt_ia);
     }
@@ -312,7 +312,7 @@ TEST_F(NakedDhcpv6SrvTest, SolicitNoSubnet) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -335,7 +335,7 @@ TEST_F(NakedDhcpv6SrvTest, RequestNoSubnet) {
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     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
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -373,7 +373,7 @@ TEST_F(NakedDhcpv6SrvTest, RenewNoSubnet) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     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));
     ia->addOption(renewed_addr_opt);
@@ -408,7 +408,7 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     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));
     ia->addOption(released_addr_opt);
@@ -563,7 +563,7 @@ TEST_F(Dhcpv6SrvTest, advertiseOptions) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -647,7 +647,7 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -672,6 +672,53 @@ TEST_F(Dhcpv6SrvTest, SolicitBasic) {
 }
 
 // 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
 // really belongs to the configured pool.
 //
@@ -692,7 +739,7 @@ TEST_F(Dhcpv6SrvTest, SolicitHint) {
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     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
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -747,7 +794,7 @@ TEST_F(Dhcpv6SrvTest, SolicitInvalidHint) {
     // Let's create a SOLICIT
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     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");
     ASSERT_FALSE(subnet_->inPool(Lease::TYPE_NA, hint));
     OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
@@ -798,9 +845,9 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
     sol2->setRemoteAddr(IOAddress("fe80::1223"));
     sol3->setRemoteAddr(IOAddress("fe80::3467"));
 
-    sol1->addOption(generateIA(1, 1500, 3000));
-    sol2->addOption(generateIA(2, 1500, 3000));
-    sol3->addOption(generateIA(3, 1500, 3000));
+    sol1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
+    sol2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
+    sol3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
@@ -878,7 +925,7 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     // Let's create a REQUEST
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
     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
     IOAddress hint("2001:db8:1:1::dead:beef");
@@ -921,6 +968,74 @@ TEST_F(Dhcpv6SrvTest, RequestBasic) {
     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
 // 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
@@ -941,9 +1056,9 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
     req2->setRemoteAddr(IOAddress("fe80::1223"));
     req3->setRemoteAddr(IOAddress("fe80::3467"));
 
-    req1->addOption(generateIA(1, 1500, 3000));
-    req2->addOption(generateIA(2, 1500, 3000));
-    req3->addOption(generateIA(3, 1500, 3000));
+    req1->addOption(generateIA(D6O_IA_NA, 1, 1500, 3000));
+    req2->addOption(generateIA(D6O_IA_NA, 2, 1500, 3000));
+    req3->addOption(generateIA(D6O_IA_NA, 3, 1500, 3000));
 
     // different client-id sizes
     OptionPtr clientid1 = generateClientId(12);
@@ -1050,7 +1165,7 @@ TEST_F(Dhcpv6SrvTest, RenewBasic) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     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));
     ia->addOption(renewed_addr_opt);
@@ -1136,7 +1251,7 @@ TEST_F(Dhcpv6SrvTest, RenewReject) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, transid));
     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));
     ia->addOption(renewed_addr_opt);
@@ -1247,7 +1362,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseBasic) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     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));
     ia->addOption(released_addr_opt);
@@ -1324,7 +1439,7 @@ TEST_F(Dhcpv6SrvTest, ReleaseReject) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, transid));
     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));
     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
 
+#ifndef DHCP6_TEST_UTILS_H
+#define DHCP6_TEST_UTILS_H
+
 #include <gtest/gtest.h>
 
 #include <dhcp/pkt6.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_iaprefix.h>
 #include <dhcp/option_int_array.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/iface_mgr.h>
@@ -145,14 +149,9 @@ public:
         valid_iface_ = ifaces.begin()->getName();
     }
 
-    // Generate IA_NA option with specified parameters
-    boost::shared_ptr<Option6IA> generateIA(uint32_t iaid, uint32_t t1, uint32_t t2) {
-        boost::shared_ptr<Option6IA> ia =
-            boost::shared_ptr<Option6IA>(new Option6IA(D6O_IA_NA, iaid));
-        ia->setT1(t1);
-        ia->setT2(t2);
-        return (ia);
-    }
+    // Generate IA_NA or IA_PD option with specified parameters
+    boost::shared_ptr<Option6IA> generateIA(uint16_t type, uint32_t iaid,
+                                            uint32_t t1, uint32_t t2);
 
     /// @brief generates interface-id option, based on text
     ///
@@ -328,32 +327,35 @@ public:
         CfgMgr::instance().addSubnet6(subnet_);
     }
 
-    // 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> 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);
+    void configurePdPool() {
+        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 64, 80));
+        subnet_->addPool(pd_pool_);
     }
 
+    /// @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
     // and lifetime values match the configured subnet
     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
     // and is valid when checked agasint the configured subnet
     Lease6Ptr 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());
-        }
+                         boost::shared_ptr<Option6IAAddr> addr);
 
-        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_);
+    Lease6Ptr checkPdLease(const DuidPtr& duid, const OptionPtr& ia_pd,
+                           boost::shared_ptr<Option6IAPrefix> prefix);
 
-        return (lease);
-    }
 
     // see wireshark.cc for descriptions
     // The descriptions are too large and too closely related to the
@@ -416,9 +405,14 @@ public:
     /// A subnet used in most tests
     Subnet6Ptr subnet_;
 
-    /// A pool used in most tests
+    /// A normal, non-temporary pool used in most tests
     Pool6Ptr pool_;
+
+    /// A prefix pool used in most tests
+    Pool6Ptr pd_pool_;
 };
 
 }; // end of isc::test namespace
 }; // end of isc namespace
+
+#endif // DHCP6_TEST_UTILS_H

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

@@ -924,7 +924,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -992,7 +992,7 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
     sol->setIface(valid_iface_);
-    sol->addOption(generateIA(234, 1500, 3000));
+    sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
     OptionPtr clientid = generateClientId();
     sol->addOption(clientid);
 
@@ -1064,7 +1064,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     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));
     ia->addOption(renewed_addr_opt);
@@ -1161,7 +1161,7 @@ TEST_F(HooksDhcpv6SrvTest, leaseUpdate_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     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));
     ia->addOption(renewed_addr_opt);
@@ -1252,7 +1252,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_renew) {
     // Let's create a RENEW
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
     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));
     ia->addOption(renewed_addr_opt);
@@ -1320,7 +1320,7 @@ TEST_F(HooksDhcpv6SrvTest, basic_lease6_release) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     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));
     ia->addOption(released_addr_opt);
@@ -1401,7 +1401,7 @@ TEST_F(HooksDhcpv6SrvTest, skip_lease6_release) {
     // Let's create a RELEASE
     Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RELEASE, 1234));
     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));
     ia->addOption(released_addr_opt);