void findClientLease(AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
+ Subnet4Ptr original_subnet = ctx.subnet_;
Subnet4Ptr subnet = ctx.subnet_;
SharedNetwork4Ptr network;
while (subnet) {
+ // Some of the subnets within a shared network may not be allowed
+ // for the client if classification restrictions have been applied.
+ if (!subnet->clientSupported(ctx.query_->getClasses())) {
+ if (network) {
+ subnet = network->getNextSubnet(original_subnet, subnet);
+ }
+ continue;
+ }
+
// If client identifier has been supplied, use it to lookup the lease. This
// search will return no lease if the client doesn't have any lease in the
// database or if the client didn't use client identifier to allocate the
subnet.reset();
} else {
- subnet = network->getNextSubnet(ctx.subnet_, subnet);
+ subnet = network->getNextSubnet(original_subnet, subnet);
}
}
}
inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
SharedNetwork4Ptr network;
ctx.subnet_->getSharedNetwork(network);
- if (!network) {
- // If there is no shared network associated with this subnet, we
- // simply check if the address is within the pool in this subnet.
- return (ctx.subnet_->inPool(Lease::TYPE_V4, address));
- }
// If the subnet belongs to a shared network we will be iterating
// over the subnets that belong to this shared network.
Subnet4Ptr current_subnet = ctx.subnet_;
while (current_subnet) {
- if (current_subnet->inPool(Lease::TYPE_V4, address)) {
- // We found a subnet that this address belongs to, so it
- // seems that this subnet is the good candidate for allocation.
- // Let's update the selected subnet.
- ctx.subnet_ = current_subnet;
- return (true);
+
+ if (current_subnet->clientSupported(ctx.query_->getClasses())) {
+ if (current_subnet->inPool(Lease::TYPE_V4, address)) {
+ // We found a subnet that this address belongs to, so it
+ // seems that this subnet is the good candidate for allocation.
+ // Let's update the selected subnet.
+ ctx.subnet_ = current_subnet;
+ return (true);
+ }
+ }
+
+ if (network) {
+ // Address is not within pools or client class not supported, so
+ // let's proceed to the next subnet.
+ current_subnet = network->getNextSubnet(ctx.subnet_, current_subnet);
+
+ } else {
+ // No shared network, so there are no more subnets to try.
+ current_subnet.reset();
}
- // Address is not within pools in this subnet, so let's proceed
- // to the next subnet.
- current_subnet = network->getNextSubnet(ctx.subnet_, current_subnet);
}
return (false);
subnet->getSharedNetwork(network);
uint64_t total_attempts = 0;
while (subnet) {
+
+ // Some of the subnets within a shared network may not be allowed
+ // for the client if classification restrictions have been applied.
+ if (!subnet->clientSupported(ctx.query_->getClasses())) {
+ if (network) {
+ subnet = network->getNextSubnet(original_subnet, subnet);
+ }
+ continue;
+ }
+
const uint64_t max_attempts = (attempts_ > 0 ? attempts_ :
subnet->getPoolCapacity(Lease::TYPE_V4));
for (uint64_t i = 0; i < max_attempts; ++i) {
// subnets in the same shared network.
if (network) {
subnet = network->getNextSubnet(original_subnet, subnet);
+
if (subnet) {
ctx.subnet_ = subnet;
}
EXPECT_EQ("192.0.2.17", lease2->addr_.toText());
}
+// This test verifies that the server can offer an address from a
+// different subnet than orginally selected, when the address pool in
+// the first subnet is exhausted.
+TEST_F(AllocEngine4Test, discoverSharedNetworkClassification) {
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Create two subnets, each with a single address pool. The first subnet
+ // has only one address in its address pool to make it easier to simulate
+ // address exhaustion.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, SubnetID(1)));
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("10.1.2.0"), 24, 1, 2, 3, SubnetID(2)));
+ Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.17"), IOAddress("192.0.2.17")));
+ Pool4Ptr pool2(new Pool4(IOAddress("10.1.2.5"), IOAddress("10.1.2.100")));
+ subnet1->addPool(pool1);
+ subnet2->addPool(pool2);
+
+ // Both subnets belong to the same network so they can be used
+ // interchangeably.
+ SharedNetwork4Ptr network(new SharedNetwork4("test_network"));
+ network->add(subnet1);
+ network->add(subnet2);
+
+ // Try to offer address from subnet1. There is one address available
+ // so it should be offerred.
+ AllocEngine::ClientContext4
+ ctx(subnet1, ClientIdPtr(), hwaddr_, IOAddress::IPV4_ZERO_ADDRESS(),
+ false, false, "host.example.com.", true);
+ ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
+ Lease4Ptr lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Apply restrictions on the subnet1. This should be only assigned
+ // to clients belonging to cable-modem class.
+ subnet1->allowClientClass("cable-modem");
+
+ // The allocation engine should determine that the subnet1 is not
+ // available for the client not belonging to the cable-modem class.
+ // Instead, it should offer an address from subnet2 that belongs
+ // to the same shared network.
+ ctx.subnet_ = subnet1;
+ lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Assign cable-modem class and try again. This time, we should
+ // offer an address from the subnet1.
+ ctx.query_->addClass(ClientClass("cable-modem"));
+
+ ctx.subnet_ = subnet1;
+ lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1->inPool(Lease::TYPE_V4, lease->addr_));
+}
+
// This test verifies that the server can allocate an address from a
// different subnet than orginally selected, when the address pool in
// the first subnet is exhausted.
EXPECT_EQ("192.0.2.17", lease2->addr_.toText());
}
+// This test verifies that the server can assign an address from a
+// different subnet than orginally selected, when the address pool in
+// the first subnet is exhausted.
+TEST_F(AllocEngine4Test, requestSharedNetworkClassification) {
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+
+ // Create two subnets, each with a single address pool. The first subnet
+ // has only one address in its address pool to make it easier to simulate
+ // address exhaustion.
+ Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, SubnetID(1)));
+ Subnet4Ptr subnet2(new Subnet4(IOAddress("10.1.2.0"), 24, 1, 2, 3, SubnetID(2)));
+ Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.17"), IOAddress("192.0.2.17")));
+ Pool4Ptr pool2(new Pool4(IOAddress("10.1.2.5"), IOAddress("10.1.2.100")));
+ subnet1->addPool(pool1);
+ subnet2->addPool(pool2);
+
+ // Both subnets belong to the same network so they can be used
+ // interchangeably.
+ SharedNetwork4Ptr network(new SharedNetwork4("test_network"));
+ network->add(subnet1);
+ network->add(subnet2);
+
+ // Try to offer address from subnet1. There is one address available
+ // so it should be offerred.
+ AllocEngine::ClientContext4
+ ctx(subnet1, ClientIdPtr(), hwaddr_, IOAddress::IPV4_ZERO_ADDRESS(),
+ false, false, "host.example.com.", false);
+ ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+ Lease4Ptr lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Remove the lease so as we can start over.
+ LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ // Apply restrictions on the subnet1. This should be only assigned
+ // to clients belonging to cable-modem class.
+ subnet1->allowClientClass("cable-modem");
+
+ // The allocation engine should determine that the subnet1 is not
+ // available for the client not belonging to the cable-modem class.
+ // Instead, it should assign an address from subnet2 that belongs
+ // to the same shared network.
+ ctx.subnet_ = subnet1;
+ lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Remove the lease so as we can start over.
+ LeaseMgrFactory::instance().deleteLease(lease->addr_);
+
+ // Assign cable-modem class and try again. This time, we should
+ // offer an address from the subnet1.
+ ctx.query_->addClass(ClientClass("cable-modem"));
+
+ ctx.subnet_ = subnet1;
+ lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet1->inPool(Lease::TYPE_V4, lease->addr_));
+
+ // Let's now remove the client from the cable-modem class and try
+ // to renew the address. The engine should determine that the
+ // client doesn't have access to the subnet1 pools anymore and
+ // assign an address from unrestricted subnet.
+ ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+ ctx.subnet_ = subnet1;
+ lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(lease);
+ EXPECT_TRUE(subnet2->inPool(Lease::TYPE_V4, lease->addr_));
+}
// This test checks if an expired lease can be reused in DHCPDISCOVER (fake
// allocation)