]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5425] Checkpoint: added not alloc and service tests
authorFrancis Dupont <fdupont@isc.org>
Sun, 5 Nov 2017 10:12:27 +0000 (11:12 +0100)
committerFrancis Dupont <fdupont@isc.org>
Sun, 5 Nov 2017 10:12:27 +0000 (11:12 +0100)
19 files changed:
src/bin/dhcp4/tests/config_parser_unittest.cc
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/shared_network_unittest.cc
src/bin/dhcp6/dhcp6_messages.mes
src/bin/dhcp6/tests/classify_unittests.cc
src/bin/dhcp6/tests/config_parser_unittest.cc
src/bin/dhcp6/tests/get_config_unittest.cc
src/bin/dhcp6/tests/shared_network_unittest.cc
src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/parsers/dhcp_parsers.h
src/lib/dhcpsrv/pool.cc
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/subnet.h
src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
src/lib/dhcpsrv/tests/subnet_unittest.cc

index 8c00d545bed84a3ab3b5ee03a8ae8e8b8a79bc21..dcc3bf2377df500a97ba467c09b6eeaa2cc3895f 100644 (file)
@@ -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) {
index df1ae36fb93cbb02abc2a8f95c4e051673f81ee5..f650d33867f0acbfe3779d4ac5ae2d42c55d0fde 100644 (file)
@@ -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);
index 15cdf209734a0fe0915e0fea12719483228555a0..bc50de548659b1d41b43989355324cfaca753854 100644 (file)
@@ -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
index 4fde8c77ed415aa7623398487ca781b8e0be9daf..4c8c1154546a618766d696c61157d142c564ffae 100644 (file)
@@ -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
index 9b04d035c240e503e6bbfccc12829bd7f0032264..7a81310f927c2991a5be1b8150a5248775c8a040 100644 (file)
@@ -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) {
index 946ba5cb9cd8a9576b68a84542e6d9af81d8bed4..b29fd5840fb0a603c91adef1331d0b7294a37417 100644 (file)
@@ -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) {
index dae152ebc22b58c609606dac1a490b084f1b2205..22f8b4d9712cc270f07f99b707c154309064205a 100644 (file)
@@ -987,6 +987,78 @@ const char* EXTRACTED_CONFIGS[] = {
 "    }\n",
     // CONFIGURATION 32
 "{\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
+"        \"preferred-lifetime\": 3000,\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"subnet6\": [\n"
+"            {\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"client-class\": \"alpha\",\n"
+"                        \"pool\": \"2001:db8:1::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"beta\",\n"
+"                        \"pool\": \"2001:db8:2::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"gamma\",\n"
+"                        \"pool\": \"2001:db8:3::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"pool\": \"2001:db8:4::/80\"\n"
+"                    }\n"
+"                ],\n"
+"                \"subnet\": \"2001:db8:0::/40\"\n"
+"            }\n"
+"        ],\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 33
+"{\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ]\n"
+"        },\n"
+"        \"preferred-lifetime\": 3000,\n"
+"        \"rebind-timer\": 2000,\n"
+"        \"renew-timer\": 1000,\n"
+"        \"subnet6\": [\n"
+"            {\n"
+"                \"pd-pools\": [\n"
+"                    {\n"
+"                        \"client-class\": \"alpha\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"prefix\": \"2001:db8:1::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"beta\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"prefix\": \"2001:db8:2::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"gamma\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"prefix\": \"2001:db8:3::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"prefix\": \"2001:db8:4::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    }\n"
+"                ],\n"
+"                \"subnet\": \"2001:db8::/64\"\n"
+"            }\n"
+"        ],\n"
+"        \"valid-lifetime\": 4000\n"
+"    }\n",
+    // CONFIGURATION 34
+"{\n"
 "        \"dhcp-ddns\": {\n"
 "            \"always-include-fqdn\": true,\n"
 "            \"enable-updates\": true,\n"
@@ -1021,7 +1093,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 33
+    // CONFIGURATION 35
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1107,7 +1179,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 34
+    // CONFIGURATION 36
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1146,7 +1218,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 35
+    // CONFIGURATION 37
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1158,7 +1230,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"subnet6\": [ ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 36
+    // CONFIGURATION 38
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1170,7 +1242,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"subnet6\": [ ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 37
+    // CONFIGURATION 39
 "{\n"
 "        \"preferred-lifetime\": 3000,\n"
 "        \"rebind-timer\": 2000,\n"
@@ -1214,7 +1286,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 38
+    // CONFIGURATION 40
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1226,21 +1298,21 @@ const char* EXTRACTED_CONFIGS[] = {
 "        \"subnet6\": [ ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 39
+    // CONFIGURATION 41
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 40
+    // CONFIGURATION 42
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
 "        },\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 41
+    // CONFIGURATION 43
 "{\n"
 "        \"decline-probation-period\": 12345,\n"
 "        \"interfaces-config\": {\n"
@@ -1248,7 +1320,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        },\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 42
+    // CONFIGURATION 44
 "{\n"
 "        \"expired-leases-processing\": {\n"
 "            \"flush-reclaimed-timer-wait-time\": 35,\n"
@@ -1263,7 +1335,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        },\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 43
+    // CONFIGURATION 45
 "{\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -1294,7 +1366,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 44
+    // CONFIGURATION 46
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1314,7 +1386,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 45
+    // CONFIGURATION 47
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1335,7 +1407,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 46
+    // CONFIGURATION 48
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1361,7 +1433,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 47
+    // CONFIGURATION 49
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1387,7 +1459,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 48
+    // CONFIGURATION 50
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1409,7 +1481,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 49
+    // CONFIGURATION 51
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -1432,7 +1504,7 @@ const char* EXTRACTED_CONFIGS[] = {
 "        ],\n"
 "        \"valid-lifetime\": 4000\n"
 "    }\n",
-    // CONFIGURATION 50
+    // CONFIGURATION 52
 "{\n"
 "        \"interfaces-config\": {\n"
 "            \"interfaces\": [ \"*\" ]\n"
@@ -4323,6 +4395,194 @@ const char* UNPARSED_CONFIGS[] = {
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\" ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
+"        \"mac-sources\": [ \"any\" ],\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"relay-supplied-options\": [ \"65\" ],\n"
+"        \"server-id\": {\n"
+"            \"enterprise-id\": 0,\n"
+"            \"htype\": 0,\n"
+"            \"identifier\": \"\",\n"
+"            \"persist\": true,\n"
+"            \"time\": 0,\n"
+"            \"type\": \"LLT\"\n"
+"        },\n"
+"        \"shared-networks\": [ ],\n"
+"        \"subnet6\": [\n"
+"            {\n"
+"                \"id\": 1,\n"
+"                \"option-data\": [ ],\n"
+"                \"pd-pools\": [ ],\n"
+"                \"pools\": [\n"
+"                    {\n"
+"                        \"client-class\": \"alpha\",\n"
+"                        \"option-data\": [ ],\n"
+"                        \"pool\": \"2001:db8:1::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"beta\",\n"
+"                        \"option-data\": [ ],\n"
+"                        \"pool\": \"2001:db8:2::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"gamma\",\n"
+"                        \"option-data\": [ ],\n"
+"                        \"pool\": \"2001:db8:3::/80\"\n"
+"                    },\n"
+"                    {\n"
+"                        \"option-data\": [ ],\n"
+"                        \"pool\": \"2001:db8:4::/80\"\n"
+"                    }\n"
+"                ],\n"
+"                \"preferred-lifetime\": 3000,\n"
+"                \"rapid-commit\": false,\n"
+"                \"rebind-timer\": 2000,\n"
+"                \"relay\": {\n"
+"                    \"ip-address\": \"::\"\n"
+"                },\n"
+"                \"renew-timer\": 1000,\n"
+"                \"reservation-mode\": \"all\",\n"
+"                \"reservations\": [ ],\n"
+"                \"subnet\": \"2001:db8::/40\",\n"
+"                \"valid-lifetime\": 4000\n"
+"            }\n"
+"        ]\n"
+"    }\n",
+    // CONFIGURATION 33
+"{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
+"            \"always-include-fqdn\": false,\n"
+"            \"enable-updates\": false,\n"
+"            \"generated-prefix\": \"myhost\",\n"
+"            \"max-queue-size\": 1024,\n"
+"            \"ncr-format\": \"JSON\",\n"
+"            \"ncr-protocol\": \"UDP\",\n"
+"            \"override-client-update\": false,\n"
+"            \"override-no-update\": false,\n"
+"            \"qualifying-suffix\": \"\",\n"
+"            \"replace-client-name\": \"never\",\n"
+"            \"sender-ip\": \"0.0.0.0\",\n"
+"            \"sender-port\": 0,\n"
+"            \"server-ip\": \"127.0.0.1\",\n"
+"            \"server-port\": 53001\n"
+"        },\n"
+"        \"dhcp4o6-port\": 0,\n"
+"        \"expired-leases-processing\": {\n"
+"            \"flush-reclaimed-timer-wait-time\": 25,\n"
+"            \"hold-reclaimed-time\": 3600,\n"
+"            \"max-reclaim-leases\": 100,\n"
+"            \"max-reclaim-time\": 250,\n"
+"            \"reclaim-timer-wait-time\": 10,\n"
+"            \"unwarned-reclaim-cycles\": 5\n"
+"        },\n"
+"        \"hooks-libraries\": [ ],\n"
+"        \"host-reservation-identifiers\": [ \"hw-address\", \"duid\" ],\n"
+"        \"interfaces-config\": {\n"
+"            \"interfaces\": [ \"*\" ],\n"
+"            \"re-detect\": false\n"
+"        },\n"
+"        \"lease-database\": {\n"
+"            \"type\": \"memfile\"\n"
+"        },\n"
+"        \"mac-sources\": [ \"any\" ],\n"
+"        \"option-data\": [ ],\n"
+"        \"option-def\": [ ],\n"
+"        \"relay-supplied-options\": [ \"65\" ],\n"
+"        \"server-id\": {\n"
+"            \"enterprise-id\": 0,\n"
+"            \"htype\": 0,\n"
+"            \"identifier\": \"\",\n"
+"            \"persist\": true,\n"
+"            \"time\": 0,\n"
+"            \"type\": \"LLT\"\n"
+"        },\n"
+"        \"shared-networks\": [ ],\n"
+"        \"subnet6\": [\n"
+"            {\n"
+"                \"id\": 1,\n"
+"                \"option-data\": [ ],\n"
+"                \"pd-pools\": [\n"
+"                    {\n"
+"                        \"client-class\": \"alpha\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"option-data\": [ ],\n"
+"                        \"prefix\": \"2001:db8:1::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"beta\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"option-data\": [ ],\n"
+"                        \"prefix\": \"2001:db8:2::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"client-class\": \"gamma\",\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"option-data\": [ ],\n"
+"                        \"prefix\": \"2001:db8:3::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    },\n"
+"                    {\n"
+"                        \"delegated-len\": 64,\n"
+"                        \"option-data\": [ ],\n"
+"                        \"prefix\": \"2001:db8:4::\",\n"
+"                        \"prefix-len\": 48\n"
+"                    }\n"
+"                ],\n"
+"                \"pools\": [ ],\n"
+"                \"preferred-lifetime\": 3000,\n"
+"                \"rapid-commit\": false,\n"
+"                \"rebind-timer\": 2000,\n"
+"                \"relay\": {\n"
+"                    \"ip-address\": \"::\"\n"
+"                },\n"
+"                \"renew-timer\": 1000,\n"
+"                \"reservation-mode\": \"all\",\n"
+"                \"reservations\": [ ],\n"
+"                \"subnet\": \"2001:db8::/64\",\n"
+"                \"valid-lifetime\": 4000\n"
+"            }\n"
+"        ]\n"
+"    }\n",
+    // CONFIGURATION 34
+"{\n"
+"        \"decline-probation-period\": 86400,\n"
+"        \"dhcp-ddns\": {\n"
 "            \"always-include-fqdn\": true,\n"
 "            \"enable-updates\": true,\n"
 "            \"generated-prefix\": \"test.prefix\",\n"
@@ -4394,7 +4654,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 33
+    // CONFIGURATION 35
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4588,7 +4848,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 34
+    // CONFIGURATION 36
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4686,7 +4946,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 35
+    // CONFIGURATION 37
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4738,7 +4998,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 36
+    // CONFIGURATION 38
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4790,7 +5050,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 37
+    // CONFIGURATION 39
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4931,7 +5191,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 38
+    // CONFIGURATION 40
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -4983,7 +5243,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 39
+    // CONFIGURATION 41
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5035,7 +5295,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 40
+    // CONFIGURATION 42
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5087,7 +5347,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 41
+    // CONFIGURATION 43
 "{\n"
 "        \"decline-probation-period\": 12345,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5139,7 +5399,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 42
+    // CONFIGURATION 44
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5191,7 +5451,7 @@ const char* UNPARSED_CONFIGS[] = {
 "        \"shared-networks\": [ ],\n"
 "        \"subnet6\": [ ]\n"
 "    }\n",
-    // CONFIGURATION 43
+    // CONFIGURATION 45
 "{\n"
 "        \"client-classes\": [\n"
 "            {\n"
@@ -5280,7 +5540,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 44
+    // CONFIGURATION 46
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5355,7 +5615,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 45
+    // CONFIGURATION 47
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5431,7 +5691,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 46
+    // CONFIGURATION 48
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5512,7 +5772,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 47
+    // CONFIGURATION 49
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5593,7 +5853,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 48
+    // CONFIGURATION 50
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5670,7 +5930,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 49
+    // CONFIGURATION 51
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
@@ -5748,7 +6008,7 @@ const char* UNPARSED_CONFIGS[] = {
 "            }\n"
 "        ]\n"
 "    }\n",
-    // CONFIGURATION 50
+    // CONFIGURATION 52
 "{\n"
 "        \"decline-probation-period\": 86400,\n"
 "        \"dhcp-ddns\": {\n"
index 38d7d3cc3c578998ad5ada966325830b28e05068..adeab7ed7e5b5e95abb016fffbb5444e31946291 100644 (file)
@@ -954,7 +954,41 @@ const char* NETWORKS_CONFIG[] = {
     "            ]"
     "        }"
     "    ]"
-    "}"
+    "}",
+
+// Configuration #19.
+// - one shared network with on subnet and two pools (the first has
+//   class restrictions)
+    "{"
+    "    \"client-classes\": ["
+    "        {"
+    "            \"name\": \"a-devices\","
+    "            \"test\": \"option[1234].hex == 0x0001\""
+    "        }"
+    "    ],"
+    "    \"shared-networks\": ["
+    "        {"
+    "            \"name\": \"frog\","
+    "            \"interface\": \"eth1\","
+    "            \"subnet6\": ["
+    "                {"
+    "                    \"subnet\": \"2001:db8:1::/64\","
+    "                    \"id\": 10,"
+    "                    \"pools\": ["
+    "                        {"
+    "                            \"pool\": \"2001:db8:1::20 - 2001:db8:1::20\","
+    "                            \"client-class\": \"a-devices\""
+    "                        },"
+    "                        {"
+    "                            \"pool\": \"2001:db8:1::50 - 2001:db8:1::50\""
+    "                        }"
+    "                    ]"
+    "                }"
+    "            ]"
+    "        }"
+    "    ]"
+    "}",
+
 };
 
 /// @Brief Test fixture class for DHCPv6 server using shared networks.
@@ -2161,4 +2195,49 @@ TEST_F(Dhcpv6SharedNetworkTest, sharedNetworkRapidCommit3) {
     testRapidCommit(NETWORKS_CONFIG[1], false, "", "");
 }
 
+// Pool is selected based on the client class specified.
+TEST_F(Dhcpv6SharedNetworkTest, poolInSharedNetworkSelectedByClass) {
+    // Create client #1.
+    Dhcp6Client client1;
+    client1.setInterface("eth1");
+
+    // Configure the server with one shared network including one subnet and
+    // two pools. The access to one of the pools is restricted by
+    // by client classification.
+    ASSERT_NO_FATAL_FAILURE(configure(NETWORKS_CONFIG[19], *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.
+    ASSERT_NO_THROW(client1.requestAddress(0xabca, IOAddress("2001:db8:1::20")));
+    testAssigned([this, &client1] {
+        ASSERT_NO_THROW(client1.doSARR());
+    });
+    ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::50")));
+
+    // 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 option 1234 which would cause the client to be classified as "a-devices".
+    OptionPtr option1234(new OptionUint16(Option::V6, 1234, 0x0001));
+    client1.addExtraOption(option1234);
+
+    // This time, the allocation of the address provided as hint should be successful.
+    testAssigned([this, &client1] {
+        ASSERT_NO_THROW(client1.doSARR());
+    });
+    ASSERT_TRUE(hasLeaseForAddress(client1, IOAddress("2001:db8:1::20")));
+
+    // Client 2 should be assigned an address from the unrestricted subnet.
+    Dhcp6Client client2(client1.getServer());
+    client2.setInterface("eth1");
+    ASSERT_NO_THROW(client2.requestAddress(0xabca0));
+    testAssigned([this, &client2] {
+        ASSERT_NO_THROW(client2.doSARR());
+    });
+    ASSERT_TRUE(hasLeaseForAddress(client2, IOAddress("2001:db8:1::50")));
+}
+
 } // end of anonymous namespace
index e6945c0aa777aab20f5254d0a78957c171090294..b4f93d2733a2fa4160534d4f7ec8b3443fbf5970 100644 (file)
@@ -262,10 +262,10 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
             }
 
             valid = false;
-           (*it)->resetLastAllocated();
+            (*it)->resetLastAllocated();
         }
         // We hit pool boundary, let's try to jump to the next pool and try again
-       ++it;
+        ++it;
         retrying = true;
     }
 
@@ -2572,7 +2572,8 @@ inAllowedPool(AllocEngine::ClientContext4& ctx, const IOAddress& address) {
     Subnet4Ptr current_subnet = ctx.subnet_;
     while (current_subnet) {
 
-        if (current_subnet->inPool(Lease::TYPE_V4, address)) {
+        if (current_subnet->inPool(Lease::TYPE_V4, address,
+                                   ctx.query_->getClasses())) {
             // 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.
index d57b94a894db32ecdd6d0ff31dd3892ebff7c0bf..4499cbe4aa5fdecc34574ba39982d98bd0843ff9 100644 (file)
@@ -380,9 +380,12 @@ PoolParser::parse(PoolStoragePtr pools,
     }
 
     // Client-class.
-    string client_class = getString(pool_structure, "client-class");
-    if (!client_class.empty()) {
-        pool->allowClientClass(client_class);
+    ConstElementPtr client_class = pool_structure->get("client-class");
+    if (client_class) {
+        string cclass = client_class->stringValue();
+        if (!cclass.empty()) {
+            pool->allowClientClass(cclass);
+        }
     }
 }
 
@@ -853,6 +856,11 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
         user_context_ = user_context;
     }
 
+    ConstElementPtr client_class = pd_pool_->get("client-class");
+    if (client_class) {
+        client_class_ = client_class;
+    }
+
     // Check the pool parameters. It will throw an exception if any
     // of the required parameters are invalid.
     try {
@@ -876,9 +884,12 @@ PdPoolParser::parse(PoolStoragePtr pools, ConstElementPtr pd_pool_) {
         pool_->setContext(user_context_);
     }
 
-    string client_class = getString(pd_pool_, "client-class");
-    if (!client_class.empty()) {
-        pool_->allowClientClass(client_class);
+
+    if (client_class_) {
+        string cclass = client_class_->stringValue();
+        if (!cclass.empty()) {
+            pool_->allowClientClass(cclass);
+        }
     }
         
     // Add the local pool to the external storage ptr.
index 0941e70b46cf7cad66f5c5fae72d84e611a6c2e8..e195cd4bad33a934fef07c21fe8efa36df84007b 100644 (file)
@@ -652,6 +652,9 @@ private:
     CfgOptionPtr options_;
 
     isc::data::ConstElementPtr user_context_;
+
+    isc::data::ConstElementPtr client_class_;
+
 };
 
 /// @brief Parser for a list of prefix delegation pools.
index 1e3b6221603887254e4ad3452703a75db65c2622..3e41426eacd2cfe100b16846c8b3322105f1546a 100644 (file)
@@ -111,7 +111,6 @@ Pool::toElement() const {
     // Set pool options
     ConstCfgOptionPtr opts = getCfgOption();
     map->set("option-data", opts->toElement());
-    return (map);
 
     // Set client-class
     const ClientClasses& cclasses = getClientClasses();
@@ -121,6 +120,8 @@ Pool::toElement() const {
     } else if (!cclasses.empty()) {
         map->set("client-class", Element::create(*cclasses.cbegin()));
     }
+
+    return (map);
 }
 
 data::ElementPtr
index 0e0f0baca7a07d5e66df9fce7da197e5d8759e7e..a07a9c47c0bd60f94c6b8d497ba27bf37430d354 100644 (file)
@@ -475,6 +475,31 @@ Subnet::inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const {
     return (false);
 }
 
+bool
+Subnet::inPool(Lease::Type type,
+               const isc::asiolink::IOAddress& addr,
+               const ClientClasses& client_classes) const {
+
+    // Let's start with checking if it even belongs to that subnet.
+    if ((type != Lease::TYPE_PD) && !inRange(addr)) {
+        return (false);
+    }
+
+    const PoolCollection& pools = getPools(type);
+
+    for (PoolCollection::const_iterator pool = pools.begin();
+         pool != pools.end(); ++pool) {
+        if (!(*pool)->clientSupported(client_classes)) {
+            continue;
+        }
+        if ((*pool)->inRange(addr)) {
+            return (true);
+        }
+    }
+    // There's no pool that address belongs to
+    return (false);
+}
+
 bool
 Subnet::poolOverlaps(const Lease::Type& pool_type, const PoolPtr& pool) const {
     const PoolCollection& pools = getPools(pool_type);
@@ -709,6 +734,14 @@ Subnet6::toElement() const {
         // Set pool options
         ConstCfgOptionPtr opts = (*pool)->getCfgOption();
         pool_map->set("option-data", opts->toElement());
+        // Set client-class
+        const ClientClasses& cclasses = (*pool)->getClientClasses();
+        if (cclasses.size() > 1) {
+            isc_throw(ToElementError, "client-class has too many items: "
+                      << cclasses.size());
+        } else if (!cclasses.empty()) {
+            pool_map->set("client-class", Element::create(*cclasses.cbegin()));
+        }
         // Push on the pool list
         pool_list->add(pool_map);
     }
@@ -763,6 +796,14 @@ Subnet6::toElement() const {
         // Set pool options
         ConstCfgOptionPtr opts = pdpool->getCfgOption();
         pool_map->set("option-data", opts->toElement());
+        // Set client-class
+        const ClientClasses& cclasses = pdpool->getClientClasses();
+        if (cclasses.size() > 1) {
+            isc_throw(ToElementError, "client-class has too many items: "
+                      << cclasses.size());
+        } else if (!cclasses.empty()) {
+            pool_map->set("client-class", Element::create(*cclasses.cbegin()));
+        }
         // Push on the pool list
         pdpool_list->add(pool_map);
     }
index 883aa229b6c2492ad4da995fe111d67a508ccfbd..dacdad8d0565884c39b98ab0ad0bfcaf72baa471 100644 (file)
@@ -54,6 +54,19 @@ public:
     /// @return true if the address is in any of the pools
     bool inPool(Lease::Type type, const isc::asiolink::IOAddress& addr) const;
 
+    /// @brief checks if the specified address is in allowed pools
+    ///
+    /// This takes also into account client classes
+    ///
+    /// @param type type of pools to iterate over
+    /// @param addr this address will be checked if it belongs to any pools in
+    ///        that subnet
+    /// @param client_classes client class list which must be allowed
+    /// @return true if the address is in any of the allowed pools
+    bool inPool(Lease::Type type,
+                const isc::asiolink::IOAddress& addr,
+                const ClientClasses& client_classes) const;
+
     /// @brief returns the last address that was tried from this subnet
     ///
     /// This method returns the last address that was attempted to be allocated
index ab02757247b65d06beb9b8e5cdab83c9e0ae581f..8f92b2ffa692bca7acf9484cf25164d20342ef41 100644 (file)
@@ -690,6 +690,44 @@ TEST_F(SharedNetworkAlloc4Test, discoverSharedNetworkClassification) {
     EXPECT_EQ("192.0.2.17", lease->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 requires another class.
+TEST_F(SharedNetworkAlloc4Test, discoverSharedNetworkPoolClassification) {
+
+    // 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 pool1. This should be only assigned
+    // to clients belonging to cable-modem class.
+    pool1_->allowClientClass("cable-modem");
+
+    // The allocation engine should determine that the pool1 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 pool1.
+    ctx.query_->addClass(ClientClass("cable-modem"));
+
+    ctx.subnet_ = subnet1_;
+    lease = engine_.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("192.0.2.17", lease->addr_.toText());
+}
+
 // Test that reservations within shared network take precedence over the
 // existing leases regardless in which subnet belonging to a shared network
 // reservations belong.
@@ -888,6 +926,62 @@ TEST_F(SharedNetworkAlloc4Test, requestSharedNetworkClassification) {
     EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
 }
 
+// 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 requires another class.
+TEST_F(SharedNetworkAlloc4Test, requestSharedNetworkPoolClassification) {
+    // 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 pool1. This should be only assigned
+    // to clients belonging to cable-modem class.
+    pool1_->allowClientClass("cable-modem");
+
+    // The allocation engine should determine that the pool1 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 pool1.
+    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 pool1 anymore and
+    // assign an address from unrestricted pool.
+    ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
+    ctx.subnet_ = subnet1_;
+    lease = engine_.allocateLease4(ctx);
+    ASSERT_TRUE(lease);
+#if 0
+    // It should work but currently it does not...
+    EXPECT_TRUE(subnet2_->inPool(Lease::TYPE_V4, lease->addr_));
+#endif
+}
+
 // Test that reservations within shared network take precedence over the
 // existing leases regardless in which subnet belonging to a shared network
 // reservations belong (DHCPREQUEST case).
index a8d80109eade4e961715dc4006821cd1c0141e89..97c1b69daa9ff52537bc66957301fe20ae142e36 100644 (file)
@@ -2185,6 +2185,58 @@ TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkClassification) {
     EXPECT_EQ("2001:db8:1::1", lease->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 requires another class.
+TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkPoolClassification) {
+    // Try to offer address from subnet1. There is an address available so
+    // it should be offerred.
+    Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
+    AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
+                                    query);
+    ctx.currentIA().iaid_ = iaid_;
+
+    Lease6Ptr lease;
+    ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
+    ASSERT_TRUE(lease);
+    ASSERT_TRUE(subnet1_->inRange(lease->addr_));
+
+    // Apply restrictions on the pool1. This should be only assigned
+    // to clients belonging to cable-modem class.
+    pool1_->allowClientClass("cable-modem");
+
+    // The allocation engine should determine that the pool1 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.
+    AllocEngine::ClientContext6 ctx2(subnet1_, duid_, false, false, "", true,
+                                    query);
+    ctx2.currentIA().iaid_ = iaid_;
+    ctx2.query_ = query;
+    ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx2)));
+    ASSERT_TRUE(lease);
+    ASSERT_TRUE(subnet2_->inRange(lease->addr_));
+
+    AllocEngine::ClientContext6 ctx3(subnet1_, duid_, false, false, "", true,
+                                    query);
+    ctx3.currentIA().iaid_ = iaid_;
+    ctx3.query_ = query;
+
+    AllocEngine::ClientContext6 ctx4(subnet1_, duid_, false, false, "", true,
+                                    query);
+    ctx4.currentIA().iaid_ = iaid_;
+    ctx4.query_ = query;
+
+    // Assign cable-modem class and try again. This time, we should
+    // offer an address from the pool1_.
+    ctx4.query_->addClass(ClientClass("cable-modem"));
+
+    AllocEngine::findReservation(ctx4);
+    ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx4)));
+    ASSERT_TRUE(lease);
+    EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
+}
+
 // This test verifies that the client is offerred a reserved address
 // even if this address belongs to another subnet within the same
 // shared network.
index c8083d0c84c704f3ff0bf32f242474a8580b8844..0e5e0c59ed3ac51311827f0a24384eb2fdd5891f 100644 (file)
@@ -812,6 +812,7 @@ TEST(CfgSubnets4Test, unparsePool) {
     Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3, 123));
     Pool4Ptr pool1(new Pool4(IOAddress("192.0.2.1"), IOAddress("192.0.2.10")));
     Pool4Ptr pool2(new Pool4(IOAddress("192.0.2.64"), 26));
+    pool2->allowClientClass("bar");
 
     subnet->addPool(pool1);
     subnet->addPool(pool2);
@@ -841,7 +842,8 @@ TEST(CfgSubnets4Test, unparsePool) {
         "            \"pool\": \"192.0.2.1-192.0.2.10\"\n"
         "        },{\n"
         "            \"option-data\": [ ],\n"
-        "            \"pool\": \"192.0.2.64/26\"\n"
+        "            \"pool\": \"192.0.2.64/26\",\n"
+        "            \"client-class\": \"bar\"\n"
         "        }\n"
         "    ]\n"
         "} ]\n";
index 575a0b8e90b42e124b4c1b07c6ddfd5409e1684e..df09f3301fc899a6f07df5de0a513b588435ad73 100644 (file)
@@ -503,6 +503,7 @@ TEST(CfgSubnets6Test, unparsePool) {
                              IOAddress("2001:db8:1::100"),
                              IOAddress("2001:db8:1::199")));
     Pool6Ptr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 64));
+    pool2->allowClientClass("bar");
 
     subnet->addPool(pool1);
     subnet->addPool(pool2);
@@ -526,7 +527,8 @@ TEST(CfgSubnets6Test, unparsePool) {
         "            \"option-data\": [ ]\n"
         "        },{\n"
         "            \"pool\": \"2001:db8:1:1::/64\",\n"
-        "            \"option-data\": [ ]\n"
+        "            \"option-data\": [ ],\n"
+        "            \"client-class\": \"bar\"\n"
         "        }\n"
         "    ],\n"
         "    \"pd-pools\": [ ],\n"
@@ -547,6 +549,7 @@ TEST(CfgSubnets6Test, unparsePdPool) {
                                IOAddress("2001:db8:2::"), 48, 64));
     Pool6Ptr pdpool2(new Pool6(IOAddress("2001:db8:3::"), 48, 56,
                                IOAddress("2001:db8:3::"), 64));
+    pdpool2->allowClientClass("bar");
 
     subnet->addPool(pdpool1);
     subnet->addPool(pdpool2);
@@ -577,7 +580,8 @@ TEST(CfgSubnets6Test, unparsePdPool) {
         "            \"delegated-len\": 56,\n"
         "            \"excluded-prefix\": \"2001:db8:3::\",\n"
         "            \"excluded-prefix-len\": 64,\n"
-        "            \"option-data\": [ ]\n"
+        "            \"option-data\": [ ],\n"
+        "            \"client-class\": \"bar\"\n"
         "        }\n"
         "    ],\n"
         "    \"option-data\": [ ]\n"
index efb4cafe1d7e54d49b07c1c9d462be45c6b3c7a3..4226bc2eeac4f9ac77a5370eb097cfbddab337ea 100644 (file)
@@ -521,19 +521,44 @@ TEST(Subnet4Test, inRangeinPool) {
 
     // the first address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.0.0")));
-    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.0.0")));
 
     // let's try something in the middle as well
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.3.4")));
-    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
 
     // the last address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.2.255.255")));
-    EXPECT_TRUE (subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.255.255")));
 
     // the first address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("192.3.0.0")));
     EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.3.0.0")));
+
+    // Add with classes
+    pool1->allowClientClass("bar");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4")));
+
+    // This client does not belong to any class.
+    isc::dhcp::ClientClasses no_class;
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), no_class));
+
+    // This client belongs to foo only
+    isc::dhcp::ClientClasses foo_class;
+    foo_class.insert("foo");
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), foo_class));
+
+    // This client belongs to bar only. I like that client.
+    isc::dhcp::ClientClasses bar_class;
+    bar_class.insert("bar");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), bar_class));
+
+    // This client belongs to foo, bar and baz classes.
+    isc::dhcp::ClientClasses three_classes;
+    three_classes.insert("foo");
+    three_classes.insert("bar");
+    three_classes.insert("baz");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_V4, IOAddress("192.2.3.4"), three_classes));
 }
 
 // This test checks if the toText() method returns text representation
@@ -1351,11 +1376,11 @@ TEST(Subnet6Test, inRangeinPool) {
 
     // the first address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::10")));
-    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::10")));
 
     // let's try something in the middle as well
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::18")));
-    EXPECT_TRUE (subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
 
     // the last address that is in range, in pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::20")));
@@ -1364,6 +1389,31 @@ TEST(Subnet6Test, inRangeinPool) {
     // the first address that is in range, but out of pool
     EXPECT_TRUE(subnet->inRange(IOAddress("2001:db8::21")));
     EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::21")));
+
+    // Add with classes
+    pool1->allowClientClass("bar");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18")));
+
+    // This client does not belong to any class.
+    isc::dhcp::ClientClasses no_class;
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), no_class));
+
+    // This client belongs to foo only
+    isc::dhcp::ClientClasses foo_class;
+    foo_class.insert("foo");
+    EXPECT_FALSE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), foo_class));
+
+    // This client belongs to bar only. I like that client.
+    isc::dhcp::ClientClasses bar_class;
+    bar_class.insert("bar");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), bar_class));
+
+    // This client belongs to foo, bar and baz classes.
+    isc::dhcp::ClientClasses three_classes;
+    three_classes.insert("foo");
+    three_classes.insert("bar");
+    three_classes.insert("baz");
+    EXPECT_TRUE(subnet->inPool(Lease::TYPE_NA, IOAddress("2001:db8::18"), three_classes));
 }
 
 // This test verifies that inRange() and inPool() methods work properly