]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1405] added more unittests
authorRazvan Becheriu <razvan@isc.org>
Fri, 30 Oct 2020 11:48:22 +0000 (13:48 +0200)
committerRazvan Becheriu <razvan@isc.org>
Wed, 18 Nov 2020 13:55:23 +0000 (15:55 +0200)
src/bin/dhcp4/tests/host_unittest.cc
src/bin/dhcp6/tests/host_unittest.cc
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc

index 49085ddd7f5c7d6ac2b62ef8c18d7f6affe2d63e..10629faea87f348514b474a8228d7be7310db884 100644 (file)
@@ -337,7 +337,49 @@ const char* CONFIGS[] = {
         "        \"interface\": \"eth0\"\n"
         "    }\n"
         "]\n"
-    "}"
+    "}",
+
+    // Configuration 8 both global and in-subnet
+    // 2 subnets, one reservations flags default (aka in-subnet),
+    // one reservations flags global and in-subnet.
+    // Host reservations for the same client, one global, one in each subnet
+    "{ \"interfaces-config\": {\n"
+        "      \"interfaces\": [ \"*\" ]\n"
+        "},\n"
+        "\"valid-lifetime\": 600,\n"
+        "\"reservations\": [ \n"
+        "{\n"
+        "   \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
+        "   \"hostname\": \"global-host\"\n"
+        "}\n"
+        "],\n"
+        "\"subnet4\": [\n"
+        "    {\n"
+        "        \"subnet\": \"10.0.0.0/24\", \n"
+        "        \"id\": 10,"
+        "        \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],\n"
+        "        \"interface\": \"eth0\",\n"
+        "        \"reservations\": [ \n"
+        "        {\n"
+        "           \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
+        "           \"hostname\": \"subnet-10-host\"\n"
+        "        }]\n"
+        "    },\n"
+        "    {\n"
+        "        \"subnet\": \"192.0.2.0/26\", \n"
+        "        \"id\": 20,"
+        "        \"pools\": [ { \"pool\": \"192.0.2.10-192.0.2.63\" } ],\n"
+        "        \"interface\": \"eth1\",\n"
+        "        \"reservations-global\": true,\n"
+        "        \"reservations-in-subnet\": true,\n"
+        "        \"reservations\": [ \n"
+        "        {\n"
+        "           \"hw-address\": \"aa:bb:cc:dd:ee:ff\",\n"
+        "           \"hostname\": \"subnet-20-host\"\n"
+        "        }]\n"
+        "    }\n"
+        "]\n"
+    "}\n"
 };
 
 /// @brief Test fixture class for testing global v4 reservations.
@@ -651,6 +693,25 @@ TEST_F(HostTest, allOverGlobal) {
     runDoraTest(CONFIGS[3], client, "subnet-10-host", "10.0.0.105");
 }
 
+// Verifies that when there are matching reservations at
+// both the global and subnet levels, client will be matched
+// to the subnet reservation, when subnet reservation true flags
+// are global and in-subnet, i.e. the subnet has the preference.
+TEST_F(HostTest, subnetOverGlobal) {
+    Dhcp4Client client(Dhcp4Client::SELECTING);
+
+    // Hardware address matches all reservations
+    client.setHWAddress("aa:bb:cc:dd:ee:ff");
+
+    // Change to subnet 20
+    client.setIfaceName("eth1");
+    client.setIfaceIndex(ETH1_INDEX);
+
+    // Subnet 20 uses both global and in-subnet reservations flags,
+    // so the subnet reservation has the preference.
+    runDoraTest(CONFIGS[8], client, "subnet-20-host", "192.0.2.10");
+}
+
 // Verifies that client class specified in the global reservation
 // may be used to influence pool selection.
 TEST_F(HostTest, clientClassGlobalPoolSelection) {
index 4dd97beb4039c59417df36153eaef83058c71ac5..feceada7a77b303fd9c3c7ff924cb7c79addb94e 100644 (file)
@@ -495,7 +495,7 @@ const char* CONFIGS[] = {
         "   \"client-classes\": [ \"reserved_class\" ]\n"
         "}\n"
         "],\n"
-    "\"shared-networks\": [{"
+        "\"shared-networks\": [{"
         "    \"name\": \"frog\",\n"
         "    \"subnet6\": [\n"
         "        {\n"
@@ -2401,6 +2401,21 @@ TEST_F(HostTest, globalReservationsNA) {
         // Should get dynamic address and host name
         ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::1", "subnet-duid-host"));
     }
+
+    {
+        SCOPED_TRACE("Subnet reservation preferred over global");
+        // Patch the second subnet to both global and in-subnet.
+        Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()->
+            getCfgSubnets6()->getSubnet(2);
+        ASSERT_TRUE(subnet);
+        subnet->setHostReservationMode(Network::HR_IN_SUBNET|Network::HR_GLOBAL);
+        client.clearConfig();
+        client.setInterface("eth1");
+        client.setDUID("01:02:03:05");
+        client.requestAddress(1234, IOAddress("::"));
+        // Should get dynamic address and host name because it has preference
+        ASSERT_NO_FATAL_FAILURE(sarrTest(client, "2001:db8:2::1", "subnet-duid-host"));
+    }
 }
 
 // Verifies fundamental Global vs Subnet host reservations for PD leases
@@ -2448,6 +2463,22 @@ TEST_F(HostTest, globalReservationsPD) {
         // Should get dynamic prefix and subnet reserved host name
         ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::100", "subnet-duid-host"));
     }
+
+    {
+        SCOPED_TRACE("Subnet reservation preferred over global");
+        // Patch the second subnet to both global and in-subnet.
+        Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()->
+            getCfgSubnets6()->getSubnet(2);
+        ASSERT_TRUE(subnet);
+        subnet->setHostReservationMode(Network::HR_IN_SUBNET|Network::HR_GLOBAL);
+        client.clearConfig();
+        client.setInterface("eth1");
+        client.setDUID("01:02:03:05");
+        client.requestPrefix(1);
+        // Should get dynamic prefix and subnet reserved host name
+        // because it has preference over the global reservation.
+        ASSERT_NO_FATAL_FAILURE(sarrTest(client, "3001::100", "subnet-duid-host"));
+    }
 }
 
 // Verifies that client class specified in the global reservation
index 14768838a6fa8a8ad62399892635c862beee9e28..922f4c5d96aeb5a306e9dc146799d58825741ba4 100644 (file)
@@ -1188,11 +1188,6 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
         return;
     }
 
-    if (allocateGlobalReservedLeases6(ctx, existing_leases)) {
-        // global reservation provided the lease, we're done
-        return;
-    }
-
     // Let's convert this from Lease::Type to IPv6Reserv::Type
     IPv6Resrv::Type type = ctx.currentIA().type_ == Lease::TYPE_NA ?
         IPv6Resrv::TYPE_NA : IPv6Resrv::TYPE_PD;
@@ -1360,15 +1355,18 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
             }
         }
     }
+
+    // Found no subnet reservations so now try the global reservation.
+    allocateGlobalReservedLeases6(ctx, existing_leases);
 }
 
-bool
+void
 AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
                                            Lease6Collection& existing_leases) {
     // Get the global host
     ConstHostPtr ghost = ctx.globalHost();
     if (!ghost) {
-        return (false);
+        return;
     }
 
     // We want to avoid allocating a new lease for an IA if there is already
@@ -1406,7 +1404,7 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
                 LeaseMgrFactory::instance().updateLease6(lease);
             }
 
-            return(true);
+            return;
         }
     }
 
@@ -1478,11 +1476,9 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
             // a lease corresponding to it and will skip it and then pick
             // the second reservation and turn it into the lease. This approach
             // would work for any number of reservations.
-            return (true);
+            return;
         }
     }
-
-    return(false);
 }
 
 void
index 43f6ca4b68f4952a3a379e69ce43073ee8a8420a..b7c85fdb02e7c576fb2d09388b5b5e745d61a175 100644 (file)
@@ -1051,7 +1051,7 @@ private:
     ///
     /// @param ctx client context that contains all details (subnet, client-id, etc.)
     /// @param existing_leases leases that are already associated with the client
-    bool
+    void
     allocateGlobalReservedLeases6(ClientContext6& ctx, Lease6Collection& existing_leases);
 
     /// @brief Removes leases that are reserved for someone else.
index 29a0017ef073f3955db68afdadf2224aab463924..1868799e41db77c06b9207125d811e9c92bcb48c 100644 (file)
@@ -3109,7 +3109,7 @@ TEST_F(AllocEngine4Test, globalReservationReservedAddressDiscover) {
     EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
     EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
 
-    // We should allocate the reserverd address.
+    // We should allocate the reserved address.
     Lease4Ptr lease = engine.allocateLease4(ctx);
     ASSERT_TRUE(lease);
     EXPECT_EQ("192.0.77.123", lease->addr_.toText());
@@ -3157,7 +3157,7 @@ TEST_F(AllocEngine4Test, globalReservationReservedAddressRequest) {
     EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
     EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
 
-    // We should allocate the reserverd address.
+    // We should allocate the reserved address.
     Lease4Ptr lease = engine.allocateLease4(ctx);
     ASSERT_TRUE(lease);
     EXPECT_EQ("192.0.77.123", lease->addr_.toText());
@@ -3275,6 +3275,216 @@ TEST_F(AllocEngine4Test, globalReservationDynamicRequest) {
     EXPECT_FALSE(ctx.old_lease_);
 }
 
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a subnet reservation.
+// - Client sends DISCOVER
+// - Client is allocated the reserved address.
+// - Lease is not added to the lease database
+TEST_F(AllocEngine4Test, mixedReservationReservedAddressDiscover) {
+    // Create reservation for the client.
+    HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                          Host::IDENT_HWADDR, subnet_->getID(),
+                          SUBNET_ID_UNUSED, IOAddress("192.0.2.123"),
+                          "foo.example.org"));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Query allocation engine for the lease to be assigned to this
+    // client without specifying the address to be assigned.
+    AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
+                                    IOAddress("0.0.0.0"), false, false,
+                                    "", true);
+    ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
+
+    // Look up the host.
+    AllocEngine::findReservation(ctx);
+
+    // We should have the correct current host
+    EXPECT_TRUE(ctx.currentHost());
+    EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
+    EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
+
+    // We should allocate the reserved address.
+    Lease4Ptr lease = engine.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+    // This is a "fake" allocation so the returned lease should not be committed
+    // to the lease database.
+    EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(lease->addr_));
+
+    // Client had no lease in the database, so the old lease returned should
+    // be NULL.
+    EXPECT_FALSE(ctx.old_lease_);
+}
+
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a subnet reservation.
+// - Client sends REQUEST
+// - Client is allocated the reserved address.
+// - Lease is added to the lease database
+TEST_F(AllocEngine4Test, mixedReservationReservedAddressRequest) {
+    // Create reservation for the client.
+    HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                          Host::IDENT_HWADDR, subnet_->getID(),
+                          SUBNET_ID_UNUSED, IOAddress("192.0.2.123"),
+                          "foo.example.org"));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Query allocation engine for the lease to be assigned to this
+    // client without specifying the address to be assigned.
+    AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
+                                    IOAddress("0.0.0.0"), false, false,
+                                    "", false);
+    ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+
+    // Look up the host.
+    AllocEngine::findReservation(ctx);
+
+    // We should have the correct current host
+    EXPECT_TRUE(ctx.currentHost());
+    EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
+    EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
+
+    // We should allocate the reserved address.
+    Lease4Ptr lease = engine.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+
+    // Client had no lease in the database, so the old lease returned should
+    // be NULL.
+    EXPECT_FALSE(ctx.old_lease_);
+}
+
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a global and a subnet reservation.
+// - Client sends DISCOVER
+// - Client is allocated the reserved address.
+// - Lease is not added to the lease database
+TEST_F(AllocEngine4Test, bothReservationReservedAddressDiscover) {
+    // Create reservations for the client.
+    HostPtr ghost(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                           Host::IDENT_HWADDR, SUBNET_ID_GLOBAL,
+                           SUBNET_ID_UNUSED, IOAddress("192.0.77.123")));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
+    HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                          Host::IDENT_HWADDR, subnet_->getID(),
+                          SUBNET_ID_UNUSED, IOAddress("192.0.2.123"),
+                          "foo.example.org"));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Query allocation engine for the lease to be assigned to this
+    // client without specifying the address to be assigned.
+    AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
+                                    IOAddress("0.0.0.0"), false, false,
+                                    "", true);
+    ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
+
+    // Look up the host.
+    AllocEngine::findReservation(ctx);
+
+    // We should have the correct current host
+    EXPECT_TRUE(ctx.currentHost());
+    EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
+    EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
+
+    // We should allocate the reserved address.
+    Lease4Ptr lease = engine.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+    // This is a "fake" allocation so the returned lease should not be committed
+    // to the lease database.
+    EXPECT_FALSE(LeaseMgrFactory::instance().getLease4(lease->addr_));
+
+    // Client had no lease in the database, so the old lease returned should
+    // be NULL.
+    EXPECT_FALSE(ctx.old_lease_);
+}
+
+// This test checks the behavior of the allocation engine in the following
+// scenario:
+// - Client has no lease in the database.
+// - Client has a global and a subnet reservation.
+// - Client sends REQUEST
+// - Client is allocated the reserved address.
+// - Lease is added to the lease database
+TEST_F(AllocEngine4Test, bothReservationReservedAddressRequest) {
+    // Create reservations for the client.
+    HostPtr ghost(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                           Host::IDENT_HWADDR, SUBNET_ID_GLOBAL,
+                           SUBNET_ID_UNUSED, IOAddress("192.0.77.123")));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
+    HostPtr host(new Host(&hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                          Host::IDENT_HWADDR, subnet_->getID(),
+                          SUBNET_ID_UNUSED, IOAddress("192.0.2.123"),
+                          "foo.example.org"));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 0, false);
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Query allocation engine for the lease to be assigned to this
+    // client without specifying the address to be assigned.
+    AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
+                                    IOAddress("0.0.0.0"), false, false,
+                                    "", false);
+    ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+
+    // Look up the host.
+    AllocEngine::findReservation(ctx);
+
+    // We should have the correct current host
+    EXPECT_TRUE(ctx.currentHost());
+    EXPECT_EQ(ctx.currentHost()->getHostname(), host->getHostname());
+    EXPECT_EQ(ctx.currentHost()->getIPv4Reservation(), host->getIPv4Reservation());
+
+    // We should allocate the reserved address.
+    Lease4Ptr lease = engine.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("192.0.2.123", lease->addr_.toText());
+
+    // Check that the lease is indeed in LeaseMgr
+    Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+    ASSERT_TRUE(from_mgr);
+
+    // Now check that the lease in LeaseMgr has the same parameters
+    detailCompareLease(lease, from_mgr);
+
+    // Client had no lease in the database, so the old lease returned should
+    // be NULL.
+    EXPECT_FALSE(ctx.old_lease_);
+}
+
 
 // Exercises AllocEnginer4Test::updateExtendedInfo4() through various
 // permutations of client packet content.
index a2113f405807959695362cd29b8eb981844b4c55..5e5f5bf04feec42c108220a2fd1effd2bd584fa1 100644 (file)
@@ -3402,7 +3402,6 @@ TEST_F(AllocEngine6Test, hostDynamicAddress) {
     HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
                  Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
                  asiolink::IOAddress("0.0.0.0")));
-
     host->setHostname("host1");
 
     CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
@@ -3701,6 +3700,288 @@ TEST_F(AllocEngine6Test, globalHostReservedPrefix) {
         << "Lease lifetime was not extended, but it should";
 }
 
+// Verifies that client with a subnet address reservation can get and
+// renew a lease for an address in the subnet.
+TEST_F(AllocEngine6Test, mixedHostReservedAddress) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                 Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
+                 asiolink::IOAddress("0.0.0.0")));
+    host->setHostname("mhost1");
+    IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("2001:db8:1::1c"), 128);
+    host->addReservation(resv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Create context which will be used to try to allocate leases
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
+    ctx.currentIA().iaid_ = iaid_;
+
+    // Look up the reservation.
+    findReservation(*engine, ctx);
+    // Make sure we found our host.
+    ConstHostPtr current = ctx.currentHost();
+    ASSERT_TRUE(current);
+    ASSERT_EQ("mhost1", current->getHostname());
+
+    // Check that we have been allocated the fixed address.
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
+
+    // We're going to rollback the clock a little so we can verify a renewal.
+    // We verify the "time" change in case the lease returned to us
+    // by expectOneLease ever becomes a copy and not what is in the lease mgr.
+    --lease->cltt_;
+    lease->updateCurrentExpirationTime();
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
+    ASSERT_TRUE(from_mgr);
+    EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
+
+    // This is what the client will send in his renew message.
+    AllocEngine::HintContainer hints;
+    hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::1c"), 128));
+
+    // Set test fixture hostname_ to the expected value. This gets checked in
+    // renewTest.
+    hostname_ = "mhost1";
+
+    // Client should receive a lease.
+    Lease6Collection renewed = renewTest(*engine, pool_, hints, true);
+    ASSERT_EQ(1, renewed.size());
+
+    // And the lease lifetime should be extended.
+    EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
+        << "Lease lifetime was not extended, but it should";
+}
+
+// Verifies that client with a subnet prefix reservation can get and
+// renew a lease for a prefix in the subnet.
+TEST_F(AllocEngine6Test, mixedHostReservedPrefix) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                 Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
+                 asiolink::IOAddress("0.0.0.0")));
+    host->setHostname("mhost1");
+    IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
+    host->addReservation(resv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Create context which will be used to try to allocate leases
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
+    ctx.currentIA().type_ = Lease::TYPE_PD;
+    ctx.currentIA().iaid_ = iaid_;
+
+    // Look up the reservation.
+    findReservation(*engine, ctx);
+    // Make sure we found our host.
+    ConstHostPtr current = ctx.currentHost();
+    ASSERT_TRUE(current);
+    ASSERT_EQ("mhost1", current->getHostname());
+
+    // Check that we have been allocated the fixed prefix.
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
+
+    // We're going to rollback the clock a little so we can verify a renewal.
+    // We verify the "time" change in case the lease returned to us
+    // by expectOneLease ever becomes a copy and not what is in the lease mgr.
+    --lease->cltt_;
+    lease->updateCurrentExpirationTime();
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
+    ASSERT_TRUE(from_mgr);
+    EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
+
+    // This is what the client will send in his renew message.
+    AllocEngine::HintContainer hints;
+    hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
+
+    // Set test fixture hostname_ to the expected value. This gets checked via
+    // renewTest.
+    hostname_ = "mhost1";
+
+    // We need a PD pool to fake renew_test
+    Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
+
+    // Client should receive a lease.
+    Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, true);
+    ASSERT_EQ(1, renewed.size());
+
+    // And the lease lifetime should be extended.
+    EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
+        << "Lease lifetime was not extended, but it should";
+}
+
+// Verifies that client with a subnet and a global address reservation
+// can get and renew a lease for an address in the subnet.
+TEST_F(AllocEngine6Test, bothHostReservedAddress) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    HostPtr ghost(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                  Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
+                  asiolink::IOAddress("0.0.0.0")));
+    ghost->setHostname("ghost1");
+    IPv6Resrv gresv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("3001::1"), 128);
+    ghost->addReservation(gresv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
+
+    HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                 Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
+                 asiolink::IOAddress("0.0.0.0")));
+    host->setHostname("mhost1");
+    IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("2001:db8:1::1c"), 128);
+    host->addReservation(resv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Create context which will be used to try to allocate leases
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
+    ctx.currentIA().iaid_ = iaid_;
+
+    // Look up the reservation.
+    findReservation(*engine, ctx);
+    // Make sure we found our host.
+    ConstHostPtr current = ctx.currentHost();
+    ASSERT_TRUE(current);
+    ASSERT_EQ("mhost1", current->getHostname());
+
+    // Check that we have been allocated the fixed address.
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
+
+    // We're going to rollback the clock a little so we can verify a renewal.
+    // We verify the "time" change in case the lease returned to us
+    // by expectOneLease ever becomes a copy and not what is in the lease mgr.
+    --lease->cltt_;
+    lease->updateCurrentExpirationTime();
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
+    ASSERT_TRUE(from_mgr);
+    EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
+
+    // This is what the client will send in his renew message.
+    AllocEngine::HintContainer hints;
+    hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::1c"), 128));
+
+    // Set test fixture hostname_ to the expected value. This gets checked in
+    // renewTest.
+    hostname_ = "mhost1";
+
+    // Client should receive a lease.
+    Lease6Collection renewed = renewTest(*engine, pool_, hints, true);
+    ASSERT_EQ(1, renewed.size());
+
+    // And the lease lifetime should be extended.
+    EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
+        << "Lease lifetime was not extended, but it should";
+}
+
+// Verifies that client with a subnet and a global prefix reservation
+// can get and renew a lease for a prefix in the subnet.
+TEST_F(AllocEngine6Test, bothHostReservedPrefix) {
+    boost::scoped_ptr<AllocEngine> engine;
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_TRUE(engine);
+
+    HostPtr ghost(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                  Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
+                  asiolink::IOAddress("0.0.0.0")));
+    ghost->setHostname("ghost1");
+    IPv6Resrv gresv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
+    ghost->addReservation(gresv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
+
+    HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
+                 Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
+                 asiolink::IOAddress("0.0.0.0")));
+    host->setHostname("mhost1");
+    IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
+    host->addReservation(resv);
+
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
+    CfgMgr::instance().commit();
+
+    subnet_->setHostReservationMode(Network::HR_GLOBAL|Network::HR_IN_SUBNET);
+
+    // Create context which will be used to try to allocate leases
+    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
+    ctx.currentIA().type_ = Lease::TYPE_PD;
+    ctx.currentIA().iaid_ = iaid_;
+
+    // Look up the reservation.
+    findReservation(*engine, ctx);
+    // Make sure we found our host.
+    ConstHostPtr current = ctx.currentHost();
+    ASSERT_TRUE(current);
+    ASSERT_EQ("mhost1", current->getHostname());
+
+    // Check that we have been allocated the fixed prefix.
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
+
+    // We're going to rollback the clock a little so we can verify a renewal.
+    // We verify the "time" change in case the lease returned to us
+    // by expectOneLease ever becomes a copy and not what is in the lease mgr.
+    --lease->cltt_;
+    lease->updateCurrentExpirationTime();
+    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+                                                               lease->addr_);
+    ASSERT_TRUE(from_mgr);
+    EXPECT_EQ(from_mgr->cltt_, lease->cltt_);
+
+    // This is what the client will send in his renew message.
+    AllocEngine::HintContainer hints;
+    hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
+
+    // Set test fixture hostname_ to the expected value. This gets checked via
+    // renewTest.
+    hostname_ = "mhost1";
+
+    // We need a PD pool to fake renew_test
+    Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
+
+    // Client should receive a lease.
+    Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, true);
+    ASSERT_EQ(1, renewed.size());
+
+    // And the lease lifetime should be extended.
+    EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
+        << "Lease lifetime was not extended, but it should";
+}
+
 /// @brief Test fixture class for testing storage of extended lease data.
 /// It primarily creates several configuration items common to the
 /// extended info tests.