Commit 3c48a2eb authored by Francis Dupont's avatar Francis Dupont
Browse files

[5425] Checkpoint: added not alloc and service tests

parent b4e2cf34
......@@ -4040,6 +4040,7 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config));
extractConfig(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
......@@ -4097,6 +4098,95 @@ TEST_F(Dhcp4ParserTest, classifySubnets) {
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
}
// Goal of this test is to verify that multiple pools can be configured
// with defined client classes.
TEST_F(Dhcp4ParserTest, classifyPools) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pools\": [ { "
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
" \"client-class\": \"alpha\" "
" },"
" {"
" \"pool\": \"192.0.3.101 - 192.0.3.150\", "
" \"client-class\": \"beta\" "
" },"
" {"
" \"pool\": \"192.0.4.101 - 192.0.4.150\", "
" \"client-class\": \"gamma\" "
" },"
" {"
" \"pool\": \"192.0.5.101 - 192.0.5.150\" "
" } ],"
" \"subnet\": \"192.0.0.0/16\" "
" } ],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config, true));
extractConfig(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
const Subnet4Collection* subnets =
CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->getAll();
ASSERT_TRUE(subnets);
ASSERT_EQ(1, subnets->size());
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_V4);
ASSERT_EQ(4, pools.size()); // We expect 4 pools
// Let's check if client belonging to alpha class is supported in pool[0]
// and not supported in any other pool (except pool[3], which allows
// everyone).
ClientClasses classes;
classes.insert("alpha");
EXPECT_TRUE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
// Let's check if client belonging to beta class is supported in pool[1]
// and not supported in any other pool (except pools[3], which allows
// everyone).
classes.clear();
classes.insert("beta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_TRUE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
// Let's check if client belonging to gamma class is supported in pool[2]
// and not supported in any other pool (except pool[3], which allows
// everyone).
classes.clear();
classes.insert("gamma");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_TRUE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
// Let's check if client belonging to some other class (not mentioned in
// the config) is supported only in pool[3], which allows everyone.
classes.clear();
classes.insert("delta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
// Finally, let's check class-less client. He should be allowed only in
// the last pool, which does not have any class restrictions.
classes.clear();
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE(pools.at(3)->clientSupported(classes));
}
// This test verifies that the host reservations can be specified for
// respective IPv4 subnets.
TEST_F(Dhcp4ParserTest, reservations) {
......
......@@ -2318,6 +2318,75 @@ TEST_F(Dhcpv4SrvTest, clientClassify) {
EXPECT_TRUE(srv_.selectSubnet(dis));
}
// Checks if the client-class field is indeed used for pool selection.
TEST_F(Dhcpv4SrvTest, clientPoolClassify) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
// This test configures 2 pools.
// The second pool does not play any role here. The client's
// IP address belongs to the first pool, so only that first
// pool is being tested.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ "
"{ \"pools\": [ { "
" \"pool\": \"192.0.2.1 - 192.0.2.100\", "
" \"client-class\": \"foo\" }, "
" { \"pool\": \"192.0.3.1 - 192.0.3.100\", "
" \"client-class\": \"xyzzy\" } ], "
" \"subnet\": \"192.0.0.0/16\" } "
"],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP4(config, true));
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
CfgMgr::instance().commit();
// check if returned status is OK
ASSERT_TRUE(status);
comment_ = config::parseAnswer(rcode_, status);
ASSERT_EQ(0, rcode_);
// Create a simple packet that we'll use for classification
Pkt4Ptr dis = Pkt4Ptr(new Pkt4(DHCPDISCOVER, 1234));
dis->setRemoteAddr(IOAddress("192.0.2.1"));
dis->setCiaddr(IOAddress("192.0.2.1"));
dis->setIface("eth0");
OptionPtr clientid = generateClientId();
dis->addOption(clientid);
// This discover does not belong to foo class, so it will not
// be serviced
Pkt4Ptr offer = srv.processDiscover(dis);
EXPECT_FALSE(offer);
// Let's add the packet to bar class and try again.
dis->addClass("bar");
// Still not supported, because it belongs to wrong class.
offer = srv.processDiscover(dis);
EXPECT_FALSE(offer);
// Let's add it to matching class.
dis->addClass("foo");
// This time it should work
offer = srv.processDiscover(dis);
ASSERT_TRUE(offer);
EXPECT_EQ(DHCPOFFER, offer->getType());
EXPECT_FALSE(offer->getYiaddr().isV4Zero());
}
// Verifies last resort option 43 is backward compatible
TEST_F(Dhcpv4SrvTest, option43LastResort) {
IfaceMgrTestConfig test_config(true);
......
......@@ -698,7 +698,7 @@ const char* NETWORKS_CONFIG[] = {
// Configuration #13.
// - 2 classes
// - 2 shared networks, each with 1 subnet and client class restricton
// - 2 shared networks, each with 1 subnet and client class restriction
"{"
" \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
......@@ -859,6 +859,42 @@ const char* NETWORKS_CONFIG[] = {
" ]"
" }"
" ]"
"}",
// Configuration #16
// - 1 shared network with 1 subnet and 2 pools and client class restriction
"{"
" \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
" },"
" \"client-classes\": ["
" {"
" \"name\": \"a-devices\","
" \"test\": \"option[93].hex == 0x0001\""
" }"
" ],"
" \"valid-lifetime\": 600,"
" \"shared-networks\": ["
" {"
" \"name\": \"frog\","
" \"interface\": \"eth1\","
" \"subnet4\": ["
" {"
" \"subnet\": \"192.0.2.0/24\","
" \"id\": 10,"
" \"pools\": ["
" {"
" \"pool\": \"192.0.2.1 - 192.0.2.63\","
" \"client-class\": \"a-devices\""
" },"
" {"
" \"pool\": \"192.0.2.100 - 192.0.2.100\""
" }"
" ]"
" }"
" ]"
" }"
" ]"
"}"
};
......@@ -1779,4 +1815,45 @@ TEST_F(Dhcpv4SharedNetworkTest, customServerIdentifier) {
EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
}
// Access to a pool within shared network is restricted by client
// classification.
TEST_F(Dhcpv4SharedNetworkTest, poolInSharedNetworkSelectedByClass) {
// Create client #1
Dhcp4Client client1(Dhcp4Client::SELECTING);
client1.setIfaceName("eth1");
// Configure the server with one shared network including one subnet and
// in 2 pools in it. The access to one of the pools is restricted
// by client classification.
configure(NETWORKS_CONFIG[16], *client1.getServer());
// Client #1 requests an address in the restricted pool but can't be assigned
// this address because the client doesn't belong to a certain class.
testAssigned([this, &client1] {
doDORA(client1, "192.0.2.100", "192.0.2.63");
});
// Release the lease that the client has got, because we'll need this address
// further in the test.
testAssigned([this, &client1] {
ASSERT_NO_THROW(client1.doRelease());
});
// Add option93 which would cause the client to be classified as "a-devices".
OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
client1.addExtraOption(option93);
// This time, the allocation of the address provided as hint should be successful.
testAssigned([this, &client1] {
doDORA(client1, "192.0.2.63", "192.0.2.63");
});
// Client 2 should be assigned an address from the unrestricted pool.
Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
client2.setIfaceName("eth1");
testAssigned([this, &client2] {
doDORA(client2, "192.0.2.100");
});
}
} // end of anonymous namespace
......@@ -60,7 +60,7 @@ The first argument specifies the client and transaction identification
information. The second argument includes all classes to which the
packet has been assigned.
% DHCP6_CLASS_UNCONFIGURED %1: client packet belongs to an unconfigured class: %1
% DHCP6_CLASS_UNCONFIGURED %1: client packet belongs to an unconfigured class: %2
This debug message informs that incoming packet belongs to a class
which cannot be found in the configuration. Either a hook written
before the classification was added to Kea is used, or class naming is
......
......@@ -643,6 +643,98 @@ TEST_F(ClassifyTest, clientClassifySubnet) {
EXPECT_TRUE(srv_.selectSubnet(sol));
}
// Checks if the client-class field is indeed used for pool selection.
TEST_F(ClassifyTest, clientClassifyPool) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// This test configures 2 pools.
// The second pool does not play any role here. The client's
// IP address belongs to the first pool, so only that first
// pool is being tested.
std::string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"client-classes\": [ "
" { "
" \"name\": \"foo\" "
" }, "
" { "
" \"name\": \"bar\" "
" } "
"], "
"\"subnet6\": [ "
" { \"pools\": [ "
" { "
" \"pool\": \"2001:db8:1::/64\", "
" \"client-class\": \"foo\" "
" }, "
" { "
" \"pool\": \"2001:db8:2::/64\", "
" \"client-class\": \"xyzzy\" "
" } "
" ], "
" \"subnet\": \"2001:db8:2::/40\" "
" } "
"], "
"\"valid-lifetime\": 4000 }";
ASSERT_NO_THROW(configure(config));
OptionPtr clientid = generateClientId();
Pkt6Ptr query1 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
query1->setRemoteAddr(IOAddress("2001:db8:1::3"));
query1->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
query1->addOption(clientid);
query1->setIface("eth1");
Pkt6Ptr query2 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
query2->setRemoteAddr(IOAddress("2001:db8:1::3"));
query2->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
query2->addOption(clientid);
query2->setIface("eth1");
Pkt6Ptr query3 = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
query3->setRemoteAddr(IOAddress("2001:db8:1::3"));
query3->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
query3->addOption(clientid);
query3->setIface("eth1");
// This discover does not belong to foo class, so it will not
// be serviced
srv.classifyPacket(query1);
Pkt6Ptr response1 = srv.processSolicit(query1);
ASSERT_TRUE(response1);
OptionPtr ia_na1 = response1->getOption(D6O_IA_NA);
ASSERT_TRUE(ia_na1);
EXPECT_TRUE(ia_na1->getOption(D6O_STATUS_CODE));
EXPECT_FALSE(ia_na1->getOption(D6O_IAADDR));
// Let's add the packet to bar class and try again.
query2->addClass("bar");
// Still not supported, because it belongs to wrong class.
srv.classifyPacket(query2);
Pkt6Ptr response2 = srv.processSolicit(query2);
ASSERT_TRUE(response2);
OptionPtr ia_na2 = response2->getOption(D6O_IA_NA);
ASSERT_TRUE(ia_na2);
EXPECT_TRUE(ia_na2->getOption(D6O_STATUS_CODE));
EXPECT_FALSE(ia_na2->getOption(D6O_IAADDR));
// Let's add it to matching class.
query3->addClass("foo");
// This time it should work
srv.classifyPacket(query3);
Pkt6Ptr response3 = srv.processSolicit(query3);
ASSERT_TRUE(response3);
OptionPtr ia_na3 = response3->getOption(D6O_IA_NA);
ASSERT_TRUE(ia_na3);
EXPECT_FALSE(ia_na3->getOption(D6O_STATUS_CODE));
EXPECT_TRUE(ia_na3->getOption(D6O_IAADDR));
}
// Tests whether a packet with custom vendor-class (not erouter or docsis)
// is classified properly.
TEST_F(ClassifyTest, vendorClientClassification2) {
......
......@@ -4134,6 +4134,194 @@ TEST_F(Dhcp6ParserTest, classifySubnets) {
EXPECT_TRUE (subnets->at(3)->clientSupported(classes));
}
// Goal of this test is to verify that multiple pools can be configured
// with defined client classes.
TEST_F(Dhcp6ParserTest, classifyPools) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { "
" \"pool\": \"2001:db8:1::/80\", "
" \"client-class\": \"alpha\" "
" },"
" {"
" \"pool\": \"2001:db8:2::/80\", "
" \"client-class\": \"beta\" "
" },"
" {"
" \"pool\": \"2001:db8:3::/80\", "
" \"client-class\": \"gamma\" "
" },"
" {"
" \"pool\": \"2001:db8:4::/80\" "
" } ],"
" \"subnet\": \"2001:db8:0::/40\" "
" } ],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP6(config, true));
extractConfig(config);
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
checkResult(x, 0);
const Subnet6Collection* subnets =
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
ASSERT_TRUE(subnets);
ASSERT_EQ(1, subnets->size());
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_NA);
ASSERT_EQ(4, pools.size()); // We expect 4 pools
// Let's check if client belonging to alpha class is supported in pool[0]
// and not supported in any other pool (except pool[3], which allows
// everyone).
ClientClasses classes;
classes.insert("alpha");
EXPECT_TRUE (pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to beta class is supported in pool[1]
// and not supported in any other pool (except pool[3], which allows
// everyone).
classes.clear();
classes.insert("beta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_TRUE (pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to gamma class is supported in pool[2]
// and not supported in any other pool (except pool[3], which allows
// everyone).
classes.clear();
classes.insert("gamma");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_TRUE (pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to some other class (not mentioned in
// the config) is supported only in pool[3], which allows everyone.
classes.clear();
classes.insert("delta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Finally, let's check class-less client. He should be allowed only in
// the last pool, which does not have any class restrictions.
classes.clear();
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
}
// Goal of this test is to verify that multiple pdpools can be configured
// with defined client classes.
TEST_F(Dhcp6ParserTest, classifyPdPools) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pd-pools\": [ { "
" \"prefix-len\": 48, "
" \"delegated-len\": 64, "
" \"prefix\": \"2001:db8:1::\", "
" \"client-class\": \"alpha\" "
" },"
" {"
" \"prefix-len\": 48, "
" \"delegated-len\": 64, "
" \"prefix\": \"2001:db8:2::\", "
" \"client-class\": \"beta\" "
" },"
" {"
" \"prefix-len\": 48, "
" \"delegated-len\": 64, "
" \"prefix\": \"2001:db8:3::\", "
" \"client-class\": \"gamma\" "
" },"
" {"
" \"prefix-len\": 48, "
" \"delegated-len\": 64, "
" \"prefix\": \"2001:db8:4::\" "
" } ],"
" \"subnet\": \"2001:db8::/64\" "
" } ],"
"\"valid-lifetime\": 4000 }";
ConstElementPtr json;
ASSERT_NO_THROW(json = parseDHCP6(config, true));
extractConfig(config);
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
checkResult(x, 0);
const Subnet6Collection* subnets =
CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
ASSERT_TRUE(subnets);
ASSERT_EQ(1, subnets->size());
const PoolCollection& pools = subnets->at(0)->getPools(Lease::TYPE_PD);
ASSERT_EQ(4, pools.size()); // We expect 4 pools
// Let's check if client belonging to alpha class is supported in pool[0]
// and not supported in any other pool (except pool[3], which allows
// everyone).
ClientClasses classes;
classes.insert("alpha");
EXPECT_TRUE (pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to beta class is supported in pool[1]
// and not supported in any other pool (except pool[3], which allows
// everyone).
classes.clear();
classes.insert("beta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_TRUE (pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to gamma class is supported in pool[2]
// and not supported in any other pool (except pool[3], which allows
// everyone).
classes.clear();
classes.insert("gamma");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_TRUE (pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Let's check if client belonging to some other class (not mentioned in
// the config) is supported only in pool[3], which allows everyone.
classes.clear();
classes.insert("delta");
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
// Finally, let's check class-less client. He should be allowed only in
// the last pool, which does not have any class restrictions.
classes.clear();
EXPECT_FALSE(pools.at(0)->clientSupported(classes));
EXPECT_FALSE(pools.at(1)->clientSupported(classes));
EXPECT_FALSE(pools.at(2)->clientSupported(classes));
EXPECT_TRUE (pools.at(3)->clientSupported(classes));
}
// This test checks the ability of the server to parse a configuration
// containing a full, valid dhcp-ddns (D2ClientConfig) entry.
TEST_F(Dhcp6ParserTest, d2ClientConfig) {
......