Browse Source

[master] Merge branch 'trac3625'

Marcin Siodelski 10 years ago
parent
commit
50906735c8
34 changed files with 1099 additions and 767 deletions
  1. 2 1
      src/bin/dhcp4/dhcp4_srv.cc
  2. 20 35
      src/bin/dhcp6/dhcp6_srv.cc
  3. 1 7
      src/bin/dhcp6/json_config_parser.cc
  4. 77 55
      src/bin/dhcp6/tests/config_parser_unittest.cc
  5. 3 2
      src/bin/dhcp6/tests/confirm_unittest.cc
  6. 4 3
      src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
  7. 1 0
      src/bin/dhcp6/tests/d2_unittest.cc
  8. 4 3
      src/bin/dhcp6/tests/dhcp6_message_test.cc
  9. 65 48
      src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
  10. 5 2
      src/bin/dhcp6/tests/dhcp6_test_utils.cc
  11. 1 1
      src/bin/dhcp6/tests/dhcp6_test_utils.h
  12. 3 2
      src/bin/dhcp6/tests/fqdn_unittest.cc
  13. 8 2
      src/bin/dhcp6/tests/hooks_unittest.cc
  14. 4 2
      src/bin/dhcp6/tests/kea_controller_unittest.cc
  15. 8 8
      src/bin/dhcp6/tests/rebind_unittest.cc
  16. 2 1
      src/bin/dhcp6/tests/sarr_unittest.cc
  17. 2 0
      src/lib/dhcpsrv/Makefile.am
  18. 1 7
      src/lib/dhcpsrv/cfg_subnets4.cc
  19. 20 26
      src/lib/dhcpsrv/cfg_subnets4.h
  20. 182 0
      src/lib/dhcpsrv/cfg_subnets6.cc
  21. 211 0
      src/lib/dhcpsrv/cfg_subnets6.h
  22. 0 108
      src/lib/dhcpsrv/cfgmgr.cc
  23. 0 91
      src/lib/dhcpsrv/cfgmgr.h
  24. 0 4
      src/lib/dhcpsrv/dhcpsrv_messages.mes
  25. 5 3
      src/lib/dhcpsrv/srv_config.cc
  26. 20 0
      src/lib/dhcpsrv/srv_config.h
  27. 75 0
      src/lib/dhcpsrv/subnet_selector.h
  28. 1 0
      src/lib/dhcpsrv/tests/Makefile.am
  29. 9 6
      src/lib/dhcpsrv/tests/alloc_engine_unittest.cc
  30. 11 10
      src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc
  31. 353 0
      src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc
  32. 0 322
      src/lib/dhcpsrv/tests/cfgmgr_unittest.cc
  33. 0 1
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
  34. 1 17
      src/lib/dhcpsrv/tests/srv_config_unittest.cc

+ 2 - 1
src/bin/dhcp4/dhcp4_srv.cc

@@ -33,6 +33,7 @@
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/utils.h>
 #include <dhcpsrv/utils.h>
 #include <hooks/callout_handle.h>
@@ -1542,7 +1543,7 @@ Dhcpv4Srv::selectSubnet(const Pkt4Ptr& question) const {
 
     Subnet4Ptr subnet;
 
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
     selector.ciaddr_ = question->getCiaddr();
     selector.giaddr_ = question->getGiaddr();
     selector.local_address_ = question->getLocalAddr();

+ 20 - 35
src/bin/dhcp6/dhcp6_srv.cc

@@ -38,6 +38,7 @@
 #include <dhcpsrv/lease_mgr.h>
 #include <dhcpsrv/lease_mgr_factory.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/utils.h>
 #include <exceptions/exceptions.h>
 #include <hooks/callout_handle.h>
@@ -868,42 +869,24 @@ Dhcpv6Srv::sanityCheck(const Pkt6Ptr& pkt, RequirementLevel clientid,
 
 Subnet6Ptr
 Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
+    // Initialize subnet selector with the values used to select the subnet.
+    SubnetSelector selector;
+    selector.iface_name_ = question->getIface();
+    selector.remote_address_ = question->getRemoteAddr();
+    selector.first_relay_linkaddr_ = IOAddress("::");
+    selector.client_classes_ = question->classes_;
+
+    // Initialize fields specific to relayed messages.
+    if (!question->relay_info_.empty()) {
+        selector.first_relay_linkaddr_ = question->relay_info_.back().linkaddr_;
+        selector.interface_id_ =
+            question->getAnyRelayOption(D6O_INTERFACE_ID,
+                                        Pkt6::RELAY_GET_FIRST);
+    }
 
-    Subnet6Ptr subnet;
-
-    if (question->relay_info_.empty()) {
-        // This is a direct (non-relayed) message
-
-        // Try to find a subnet if received packet from a directly connected client
-        subnet = CfgMgr::instance().getSubnet6(question->getIface(),
-                                               question->classes_);
-        if (!subnet) {
-            // If no subnet was found, try to find it based on remote address
-            subnet = CfgMgr::instance().getSubnet6(question->getRemoteAddr(),
-                                                   question->classes_);
-        }
-    } else {
-
-        // This is a relayed message
-        OptionPtr interface_id = question->getAnyRelayOption(D6O_INTERFACE_ID,
-                                                             Pkt6::RELAY_GET_FIRST);
-        if (interface_id) {
-            subnet = CfgMgr::instance().getSubnet6(interface_id,
-                                                   question->classes_);
-        }
-
-        if (!subnet) {
-            // If no interface-id was specified (or not configured on server),
-            // let's try address matching
-            IOAddress link_addr = question->relay_info_.back().linkaddr_;
+    Subnet6Ptr subnet = CfgMgr::instance().getCurrentCfg()->
+        getCfgSubnets6()->selectSubnet(selector);
 
-            // if relay filled in link_addr field, then let's use it
-            if (link_addr != IOAddress("::")) {
-                subnet = CfgMgr::instance().getSubnet6(link_addr,
-                                                       question->classes_, true);
-            }
-        }
-    }
 
     // Let's execute all callouts registered for subnet6_receive
     if (HooksManager::calloutsPresent(Hooks.hook_index_subnet6_select_)) {
@@ -919,7 +902,9 @@ Dhcpv6Srv::selectSubnet(const Pkt6Ptr& question) {
         // We pass pointer to const collection for performance reasons.
         // Otherwise we would get a non-trivial performance penalty each
         // time subnet6_select is called.
-        callout_handle->setArgument("subnet6collection", CfgMgr::instance().getSubnets6());
+        callout_handle->setArgument("subnet6collection",
+                                    CfgMgr::instance().getCurrentCfg()->
+                                    getCfgSubnets6()->getAll());
 
         // Call user (and server-side) callouts
         HooksManager::callCallouts(Hooks.hook_index_subnet6_select_, *callout_handle);

+ 1 - 7
src/bin/dhcp6/json_config_parser.cc

@@ -344,7 +344,7 @@ public:
             // subnet id is invalid (duplicate). Thus, we catch exceptions
             // here to append a position in the configuration string.
             try {
-                isc::dhcp::CfgMgr::instance().addSubnet6(sub6ptr);
+                CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(sub6ptr);
             } catch (const std::exception& ex) {
                 isc_throw(DhcpConfigError, ex.what() << " ("
                           << subnet->getPosition() << ")");
@@ -534,12 +534,6 @@ public:
     ///
     /// @param subnets_list pointer to a list of IPv6 subnets
     void build(ConstElementPtr subnets_list) {
-        // @todo: Implement more subtle reconfiguration than toss
-        // the old one and replace with the new one.
-
-        // remove old subnets
-        isc::dhcp::CfgMgr::instance().deleteSubnets6();
-
         BOOST_FOREACH(ConstElementPtr subnet, subnets_list->listValue()) {
             ParserPtr parser(new Subnet6ConfigParser("subnet"));
             parser->build(subnet);

+ 77 - 55
src/bin/dhcp6/tests/config_parser_unittest.cc

@@ -26,6 +26,7 @@
 #include <dhcpsrv/addr_utilities.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_selector.h>
 #include <dhcpsrv/testutils/config_result_check.h>
 #include <hooks/hooks_manager.h>
 
@@ -247,13 +248,13 @@ public:
     getOptionFromSubnet(const IOAddress& subnet_address,
                         const uint16_t option_code,
                         const uint16_t expected_options_count = 1) {
-        Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(subnet_address,
-                                                          classify_);
+        Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+            selectSubnet(subnet_address, classify_);
         if (!subnet) {
             /// @todo replace toText() with the use of operator <<.
             ADD_FAILURE() << "A subnet for the specified address "
                           << subnet_address.toText()
-                          << "does not exist in Config Manager";
+                          << " does not exist in Config Manager";
         }
         OptionContainerPtr options =
             subnet->getCfgOption()->getAll("dhcp6");
@@ -469,6 +470,8 @@ public:
                            const uint16_t option_code,
                            const uint8_t* expected_data,
                            const size_t expected_data_len) {
+        CfgMgr::instance().clear();
+
         std::string config = createConfigWithOption(params);
         ASSERT_TRUE(executeConfiguration(config, "parse option configuration"));
 
@@ -557,8 +560,8 @@ TEST_F(Dhcp6ParserTest, subnetGlobalDefaults) {
 
     // Now check if the configuration was indeed handled and we have
     // expected pool configured.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-        classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     EXPECT_EQ(1000, subnet->getT1());
     EXPECT_EQ(2000, subnet->getT2());
@@ -605,7 +608,10 @@ TEST_F(Dhcp6ParserTest, multipleSubnets) {
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
         checkResult(x, 0);
 
-        const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+        CfgMgr::instance().commit();
+
+        const Subnet6Collection* subnets =
+            CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
         ASSERT_TRUE(subnets);
         ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
 
@@ -660,7 +666,10 @@ TEST_F(Dhcp6ParserTest, multipleSubnetsExplicitIDs) {
         EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
         checkResult(x, 0);
 
-        const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+        CfgMgr::instance().commit();
+
+        const Subnet6Collection* subnets =
+            CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
         ASSERT_TRUE(subnets);
         ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
 
@@ -796,7 +805,10 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    CfgMgr::instance().commit();
+
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
 
@@ -805,7 +817,9 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    subnets = CfgMgr::instance().getSubnets6();
+    CfgMgr::instance().commit();
+
+    subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(3, subnets->size()); // We expect 3 subnets now (4th is removed)
 
@@ -820,12 +834,16 @@ TEST_F(Dhcp6ParserTest, reconfigureRemoveSubnet) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
+    CfgMgr::instance().commit();
+
     // Do reconfiguration
     json = Element::fromJSON(config_second_removed);
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    subnets = CfgMgr::instance().getSubnets6();
+    CfgMgr::instance().commit();
+
+    subnets = CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(3, subnets->size()); // We expect 4 subnets
 
@@ -863,8 +881,8 @@ TEST_F(Dhcp6ParserTest, subnetLocal) {
     // returned value should be 0 (configuration success)
     checkResult(status, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     EXPECT_EQ(1, subnet->getT1());
     EXPECT_EQ(2, subnet->getT2());
@@ -898,8 +916,8 @@ TEST_F(Dhcp6ParserTest, subnetInterface) {
     // returned value should be 0 (configuration success)
     checkResult(status, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     EXPECT_EQ(valid_iface_, subnet->getIface());
 }
@@ -931,8 +949,8 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceBogus) {
     checkResult(status, 1);
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     EXPECT_FALSE(subnet);
 }
 
@@ -993,16 +1011,20 @@ TEST_F(Dhcp6ParserTest, subnetInterfaceId) {
 
     // Try to get a subnet based on bogus interface-id option
     OptionBuffer tmp(bogus_interface_id.begin(), bogus_interface_id.end());
-    OptionPtr ifaceid(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(ifaceid, classify_);
+    SubnetSelector selector;
+    selector.first_relay_linkaddr_ = IOAddress("5000::1");
+    selector.interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(selector);
     EXPECT_FALSE(subnet);
 
     // Now try to get subnet for valid interface-id value
     tmp = OptionBuffer(valid_interface_id.begin(), valid_interface_id.end());
-    ifaceid.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
-    subnet = CfgMgr::instance().getSubnet6(ifaceid, classify_);
+    selector.interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+    subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(selector);
     ASSERT_TRUE(subnet);
-    EXPECT_TRUE(ifaceid->equals(subnet->getInterfaceId()));
+    EXPECT_TRUE(selector.interface_id_->equals(subnet->getInterfaceId()));
 }
 
 
@@ -1084,7 +1106,8 @@ TEST_F(Dhcp6ParserTest, multiplePools) {
     ASSERT_NO_THROW(status = configureDhcp6Server(srv_, json));
     checkResult(status, 0);
 
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(2, subnets->size()); // We expect 2 subnets
 
@@ -1163,8 +1186,8 @@ TEST_F(Dhcp6ParserTest, poolPrefixLen) {
     // returned value must be 1 (configuration parse error)
     checkResult(x, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     EXPECT_EQ(1000, subnet->getT1());
     EXPECT_EQ(2000, subnet->getT2());
@@ -1205,8 +1228,8 @@ TEST_F(Dhcp6ParserTest, pdPoolBasics) {
     checkResult(x, 0);
 
     // Test that we can retrieve the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // Fetch the collection of PD pools.  It should have 1 entry.
@@ -1277,8 +1300,8 @@ TEST_F(Dhcp6ParserTest, pdPoolList) {
     checkResult(x, 0);
 
     // Test that we can retrieve the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // Fetch the collection of NA pools.  It should have 1 entry.
@@ -1333,8 +1356,8 @@ TEST_F(Dhcp6ParserTest, subnetAndPrefixDelegated) {
     checkResult(x, 0);
 
     // Test that we can retrieve the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
 
     ASSERT_TRUE(subnet);
 
@@ -2027,8 +2050,8 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
     ASSERT_EQ(2, options->size());
@@ -2122,8 +2145,8 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
     checkResult(status, 0);
 
     // Options should be now available for the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     // Try to get the option from the space dhcp6.
     OptionDescriptor desc1 = subnet->getCfgOption()->get("dhcp6", 38);
@@ -2278,8 +2301,8 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
     checkResult(status, 0);
 
     // Get the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // We should have one option available.
@@ -2341,8 +2364,8 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    Subnet6Ptr subnet1 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                       classify_);
+    Subnet6Ptr subnet1 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet1);
     OptionContainerPtr options1 = subnet1->getCfgOption()->getAll("dhcp6");
     ASSERT_EQ(1, options1->size());
@@ -2367,8 +2390,8 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
                sizeof(subid_expected));
 
     // Test another subnet in the same way.
-    Subnet6Ptr subnet2 = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:2::4"),
-                                                       classify_);
+    Subnet6Ptr subnet2 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:2::4"), classify_);
     ASSERT_TRUE(subnet2);
     OptionContainerPtr options2 = subnet2->getCfgOption()->getAll("dhcp6");
     ASSERT_EQ(1, options2->size());
@@ -2402,8 +2425,6 @@ TEST_F(Dhcp6ParserTest, optionDataBoolean) {
     ASSERT_TRUE(executeConfiguration(config, "parse configuration with a"
                                      " boolean value"));
 
-    CfgMgr::instance().commit();
-
     // The subnet should now hold one option with the code 1000.
     OptionDescriptor desc =
         getOptionFromSubnet(IOAddress("2001:db8:1::5"), 1000);
@@ -2538,8 +2559,8 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
     ASSERT_EQ(1, options->size());
@@ -2581,8 +2602,8 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
     OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
     ASSERT_EQ(1, options->size());
@@ -2659,8 +2680,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
     checkResult(status, 0);
 
     // Options should be now available for the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // Try to get the option from the vendor space 4491
@@ -2721,8 +2742,8 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
     checkResult(status, 0);
 
     // Options should be now available for the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // Try to get the option from the vendor space 4491
@@ -2864,8 +2885,8 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
     checkResult(status, 0);
 
     // Get the subnet.
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::5"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::5"), classify_);
     ASSERT_TRUE(subnet);
 
     // We should have one option available.
@@ -3151,8 +3172,8 @@ TEST_F(Dhcp6ParserTest, subnetRelayInfo) {
     // returned value should be 0 (configuration success)
     checkResult(status, 0);
 
-    Subnet6Ptr subnet = CfgMgr::instance().getSubnet6(IOAddress("2001:db8:1::1"),
-                                                      classify_);
+    Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
+        selectSubnet(IOAddress("2001:db8:1::1"), classify_);
     ASSERT_TRUE(subnet);
     EXPECT_EQ("2001:db8:1::abcd", subnet->getRelayInfo().addr_.toText());
 }
@@ -3191,7 +3212,8 @@ TEST_F(Dhcp6ParserTest, classifySubnets) {
     EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
     checkResult(x, 0);
 
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
 

+ 3 - 2
src/bin/dhcp6/tests/confirm_unittest.cc

@@ -233,7 +233,8 @@ TEST_F(ConfirmTest, relayedClientNoAddress) {
     // Configure the server.
     configure(CONFIRM_CONFIGS[1], *client.getServer());
     // Make sure we ended-up having expected number of subnets configured.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_EQ(2, subnets->size());
     // Client to send relayed message.
     client.useRelay();
@@ -255,7 +256,7 @@ TEST_F(ConfirmTest, relayedClientNoSubnet) {
     ASSERT_NO_FATAL_FAILURE(requestLease(CONFIRM_CONFIGS[1], 2, client));
     // Now that the client has a lease, let's remove any subnets to check
     // how the server would respond to the Confirm.
-    ASSERT_NO_THROW(CfgMgr::instance().deleteSubnets6());
+    ASSERT_NO_THROW(CfgMgr::instance().clear());
     // Send Confirm message to the server.
     ASSERT_NO_THROW(client.doConfirm());
     // Client should have received a status code option and this option should

+ 4 - 3
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -182,7 +182,7 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
     ElementPtr config = Element::fromJSON(config_txt);
 
     // Make sure there are no subnets configured.
-    CfgMgr::instance().deleteSubnets6();
+    CfgMgr::instance().clear();
 
     // Now send the command
     int rcode = -1;
@@ -192,11 +192,12 @@ TEST_F(CtrlDhcpv6SrvTest, configReload) {
     EXPECT_EQ(0, rcode); // Expect success
 
     // Check that the config was indeed applied.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->getAll();
     EXPECT_EQ(3, subnets->size());
 
     // Clean up after the test.
-    CfgMgr::instance().deleteSubnets6();
+    CfgMgr::instance().clear();
 }
 
 } // End of anonymous namespace

+ 1 - 0
src/bin/dhcp6/tests/d2_unittest.cc

@@ -128,6 +128,7 @@ Dhcp6SrvD2Test::configureD2(bool enable_d2, const bool exp_result,
 
 void
 Dhcp6SrvD2Test::configure(const std::string& config, bool exp_result) {
+    CfgMgr::instance().clear();
     ElementPtr json = Element::fromJSON(config);
     ConstElementPtr status;
 

+ 4 - 3
src/bin/dhcp6/tests/dhcp6_message_test.cc

@@ -53,7 +53,8 @@ Dhcpv6MessageTest::requestLease(const std::string& config,
     // Configure the server.
     configure(config, *client.getServer());
     // Make sure we ended-up having expected number of subnets configured.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_EQ(subnets_num, subnets->size());
     // Do the actual 4-way exchange.
     ASSERT_NO_THROW(client.doSARR());
@@ -63,8 +64,8 @@ Dhcpv6MessageTest::requestLease(const std::string& config,
     // subnets.
     ASSERT_EQ(1, client.getLeaseNum());
     Lease6 lease_client = client.getLease(0);
-    ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client.addr_,
-                                              ClientClasses()));
+    ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
+                selectSubnet(lease_client.addr_, ClientClasses()));
     // Check that the client's lease matches the information on the server
     // side.
     Lease6Ptr lease_server = checkLease(lease_client);

+ 65 - 48
src/bin/dhcp6/tests/dhcp6_srv_unittest.cc

@@ -1182,8 +1182,9 @@ TEST_F(Dhcpv6SrvTest, selectSubnetAddr) {
 
     // CASE 1: We have only one subnet defined and we received local traffic.
     // The only available subnet used to be picked, but not anymore
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
 
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     pkt->setRemoteAddr(IOAddress("fe80::abcd"));
@@ -1196,38 +1197,42 @@ TEST_F(Dhcpv6SrvTest, selectSubnetAddr) {
     // We should NOT select it.
 
     // Identical steps as in case 1, but repeated for clarity
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
     pkt->setRemoteAddr(IOAddress("2001:db8:abcd::2345"));
     Subnet6Ptr selected = srv.selectSubnet(pkt);
     EXPECT_FALSE(selected);
 
     // CASE 3: We have three subnets defined and we received local traffic.
     // Nothing should be selected.
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
     pkt->setRemoteAddr(IOAddress("fe80::abcd"));
     selected = srv.selectSubnet(pkt);
     EXPECT_FALSE(selected);
 
     // CASE 4: We have three subnets defined and we received relayed traffic
     // that came out of subnet 2. We should select subnet2 then
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
     pkt->setRemoteAddr(IOAddress("2001:db8:2::baca"));
     selected = srv.selectSubnet(pkt);
     EXPECT_EQ(selected, subnet2);
 
     // CASE 5: We have three subnets defined and we received relayed traffic
     // that came out of undefined subnet. We should select nothing
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
     pkt->setRemoteAddr(IOAddress("2001:db8:4::baca"));
     EXPECT_FALSE(srv.selectSubnet(pkt));
 }
@@ -1246,8 +1251,9 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
 
     // CASE 1: We have only one subnet defined and it is available via eth0.
     // Packet came from eth0. The only available subnet should be selected
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
 
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     pkt->setIface("eth0");
@@ -1257,8 +1263,9 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
 
     // CASE 2: We have only one subnet defined and it is available via eth0.
     // Packet came from eth1. We should not select it
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
 
     pkt->setIface("eth1");
 
@@ -1268,10 +1275,11 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
     // CASE 3: We have only 3 subnets defined, one over eth0, one remote and
     // one over wifi1.
     // Packet came from eth1. We should not select it
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
 
     pkt->setIface("eth0");
     EXPECT_EQ(subnet1, srv.selectSubnet(pkt));
@@ -1298,8 +1306,9 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayLinkaddr) {
 
     // CASE 1: We have only one subnet defined and we received relayed traffic.
     // The only available subnet should NOT be selected.
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
 
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     pkt->relay_info_.push_back(relay);
@@ -1309,19 +1318,21 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayLinkaddr) {
 
     // CASE 2: We have three subnets defined and we received relayed traffic.
     // Nothing should be selected.
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
     selected = srv.selectSubnet(pkt);
     EXPECT_EQ(selected, subnet2);
 
     // CASE 3: We have three subnets defined and we received relayed traffic
     // that came out of subnet 2. We should select subnet2 then
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
 
     // Source of the packet should have no meaning. Selection is based
     // on linkaddr field in the relay
@@ -1331,10 +1342,11 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayLinkaddr) {
 
     // CASE 4: We have three subnets defined and we received relayed traffic
     // that came out of undefined subnet. We should select nothing
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
     pkt->relay_info_.clear();
     relay.linkaddr_ = IOAddress("2001:db8:4::1234");
     pkt->relay_info_.push_back(relay);
@@ -1357,8 +1369,9 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayInterfaceId) {
 
     // CASE 1: We have only one subnet defined and it is for interface-id "relay1"
     // Packet came with interface-id "relay2". We should not select subnet1
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1); // just a single subnet
+    CfgMgr::instance().commit();
 
     Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     Pkt6::RelayInfo relay;
@@ -1374,18 +1387,20 @@ TEST_F(Dhcpv6SrvTest, selectSubnetRelayInterfaceId) {
 
     // CASE 2: We have only one subnet defined and it is for interface-id "relay2"
     // Packet came with interface-id "relay2". We should select it
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet2); // just a single subnet
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2); // just a single subnet
+    CfgMgr::instance().commit();
     selected = srv.selectSubnet(pkt);
     EXPECT_EQ(selected, subnet2);
 
     // CASE 3: We have only 3 subnets defined: one remote for interface-id "relay1",
     // one remote for interface-id "relay2" and third local
     // packet comes with interface-id "relay2". We should select subnet2
-    CfgMgr::instance().deleteSubnets6();
-    CfgMgr::instance().addSubnet6(subnet1);
-    CfgMgr::instance().addSubnet6(subnet2);
-    CfgMgr::instance().addSubnet6(subnet3);
+    CfgMgr::instance().clear();
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet1);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet2);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet3);
+    CfgMgr::instance().commit();
 
     EXPECT_EQ(subnet2, srv.selectSubnet(pkt));
 }
@@ -1938,7 +1953,8 @@ TEST_F(Dhcpv6SrvTest, relayOverride) {
     ASSERT_NO_THROW(configure(config));
 
     // Let's get the subnet configuration objects
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_EQ(2, subnets->size());
 
     // Let's get them for easy reference
@@ -2014,7 +2030,8 @@ TEST_F(Dhcpv6SrvTest, relayOverrideAndClientClass) {
     ASSERT_NO_THROW(configure(config));
 
     // Let's get the subnet configuration objects
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_EQ(2, subnets->size());
 
     // Let's get them for easy reference

+ 5 - 2
src/bin/dhcp6/tests/dhcp6_test_utils.cc

@@ -36,8 +36,9 @@ Dhcpv6SrvTest::Dhcpv6SrvTest()
                               64));
     subnet_->addPool(pool_);
 
-    isc::dhcp::CfgMgr::instance().deleteSubnets6();
-    isc::dhcp::CfgMgr::instance().addSubnet6(subnet_);
+    isc::dhcp::CfgMgr::instance().clear();
+    isc::dhcp::CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
+    isc::dhcp::CfgMgr::instance().commit();
 
     // configure PD pool
     pd_pool_ = isc::dhcp::Pool6Ptr
@@ -604,6 +605,8 @@ Dhcpv6SrvTest::configure(const std::string& config, NakedDhcpv6Srv& srv) {
     int rcode;
     ConstElementPtr comment = config::parseAnswer(rcode, status);
     ASSERT_EQ(0, rcode);
+
+    CfgMgr::instance().commit();
 }
 
 // Generate IA_NA option with specified parameters

+ 1 - 1
src/bin/dhcp6/tests/dhcp6_test_utils.h

@@ -348,7 +348,7 @@ public:
     ///
     /// Removes existing configuration.
     ~Dhcpv6SrvTest() {
-        isc::dhcp::CfgMgr::instance().deleteSubnets6();
+        isc::dhcp::CfgMgr::instance().clear();
     };
 
     /// @brief Runs DHCPv6 configuration from the JSON string.

+ 3 - 2
src/bin/dhcp6/tests/fqdn_unittest.cc

@@ -974,13 +974,14 @@ TEST_F(FqdnDhcpv6SrvTest, processRequestReuseExpiredLease) {
     // We are going to configure a subnet with a pool that consists of
     // exactly one address. This address will be handed out to the
     // client, will get expired and then be reused.
-    CfgMgr::instance().deleteSubnets6();
+    CfgMgr::instance().clear();
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1:1::"), 56, 1, 2,
                                      3, 4));
     subnet_->setIface("eth0");
     pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr));
     subnet_->addPool(pool_);
-    CfgMgr::instance().addSubnet6(subnet_);
+    CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->add(subnet_);
+    CfgMgr::instance().commit();
 
     // Allocate a lease.
     testProcessMessage(DHCPV6_REQUEST, "myhost.example.com",

+ 8 - 2
src/bin/dhcp6/tests/hooks_unittest.cc

@@ -922,6 +922,8 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
     comment_ = parseAnswer(rcode_, status);
     ASSERT_EQ(0, rcode_);
 
+    CfgMgr::instance().commit();
+
     // Prepare solicit packet. Server should select first subnet for it
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
@@ -942,7 +944,8 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
     // Check that pkt6 argument passing was successful and returned proper value
     EXPECT_TRUE(callback_pkt6_.get() == sol.get());
 
-    const Subnet6Collection* exp_subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* exp_subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
 
     // The server is supposed to pick the first subnet, because of matching
     // interface. Check that the value is reported properly.
@@ -990,6 +993,8 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     comment_ = parseAnswer(rcode_, status);
     ASSERT_EQ(0, rcode_);
 
+    CfgMgr::instance().commit();
+
     // Prepare solicit packet. Server should select first subnet for it
     Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
     sol->setRemoteAddr(IOAddress("fe80::abcd"));
@@ -1016,7 +1021,8 @@ TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
     ASSERT_TRUE(addr_opt);
 
     // Get all subnets and use second subnet for verification
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_EQ(2, subnets->size());
 
     // Advertised address must belong to the second pool (in subnet's range,

+ 4 - 2
src/bin/dhcp6/tests/kea_controller_unittest.cc

@@ -106,7 +106,8 @@ TEST_F(JSONFileBackendTest, jsonFile) {
     EXPECT_NO_THROW(srv->init(TEST_FILE));
 
     // Now check if the configuration has been applied correctly.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(3, subnets->size()); // We expect 3 subnets.
 
@@ -176,7 +177,8 @@ TEST_F(JSONFileBackendTest, comments) {
     EXPECT_NO_THROW(srv->init(TEST_FILE));
 
     // Now check if the configuration has been applied correctly.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets =
+        CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->getAll();
     ASSERT_TRUE(subnets);
     ASSERT_EQ(1, subnets->size());
 

+ 8 - 8
src/bin/dhcp6/tests/rebind_unittest.cc

@@ -223,8 +223,8 @@ TEST_F(RebindTest, directClient) {
     // subnets.
     ASSERT_EQ(1, client.getLeaseNum());
     Lease6 lease_client2 = client.getLease(0);
-    ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client2.addr_,
-                                              ClientClasses()));
+    ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
+                selectSubnet(lease_client2.addr_, ClientClasses()));
     // The client's lease should have been extended. The client will
     // update the cltt to current time when the lease gets extended.
     ASSERT_GE(lease_client2.cltt_ - lease_client.cltt_, 1000);
@@ -337,8 +337,8 @@ TEST_F(RebindTest, relayedClient) {
     // subnets.
     ASSERT_EQ(1, client.getLeaseNum());
     Lease6 lease_client2 = client.getLease(0);
-    ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client2.addr_,
-                                              ClientClasses()));
+    ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
+                selectSubnet(lease_client2.addr_, ClientClasses()));
     // The client's lease should have been extended. The client will
     // update the cltt to current time when the lease gets extended.
     ASSERT_GE(lease_client2.cltt_ - lease_client.cltt_, 1000);
@@ -498,8 +498,8 @@ TEST_F(RebindTest, directClientPD) {
     // subnets.
     ASSERT_EQ(1, client.getLeaseNum());
     Lease6 lease_client2 = client.getLease(0);
-    ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client2.addr_,
-                                              ClientClasses()));
+    ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
+                selectSubnet(lease_client2.addr_, ClientClasses()));
     // The client's lease should have been extended. The client will
     // update the cltt to current time when the lease gets extended.
     ASSERT_GE(lease_client2.cltt_ - lease_client.cltt_, 1000);
@@ -675,8 +675,8 @@ TEST_F(RebindTest, relayedUnicast) {
     // subnets.
     ASSERT_EQ(1, client.getLeaseNum());
     Lease6 lease_client2 = client.getLease(0);
-    ASSERT_TRUE(CfgMgr::instance().getSubnet6(lease_client2.addr_,
-                                              ClientClasses()));
+    ASSERT_TRUE(CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->
+                selectSubnet(lease_client2.addr_, ClientClasses()));
     // The client's lease should have been extended. The client will
     // update the cltt to current time when the lease gets extended.
     ASSERT_GE(lease_client2.cltt_ - lease_client.cltt_, 1000);

+ 2 - 1
src/bin/dhcp6/tests/sarr_unittest.cc

@@ -76,7 +76,8 @@ TEST_F(SARRTest, directClientPrefixHint) {
     client.usePD();
     configure(CONFIGS[0], *client.getServer());
     // Make sure we ended-up having expected number of subnets configured.
-    const Subnet6Collection* subnets = CfgMgr::instance().getSubnets6();
+    const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
+        getCfgSubnets6()->getAll();
     ASSERT_EQ(1, subnets->size());
     // Append IAPREFIX option to the client's message.
     ASSERT_NO_THROW(client.useHint(100, 200, 64, "2001:db8:3:33::33"));

+ 2 - 0
src/lib/dhcpsrv/Makefile.am

@@ -49,6 +49,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
 libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h
 libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h
 libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
+libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
 libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
 libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
@@ -79,6 +80,7 @@ libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
 libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
 libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
 libkea_dhcpsrv_la_SOURCES += subnet_id.h
+libkea_dhcpsrv_la_SOURCES += subnet_selector.h
 libkea_dhcpsrv_la_SOURCES += triplet.h
 libkea_dhcpsrv_la_SOURCES += utils.h
 

+ 1 - 7
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -32,12 +32,6 @@ const IOAddress BCAST_ADDRESS("255.255.255.255");
 namespace isc {
 namespace dhcp {
 
-CfgSubnets4::Selector::Selector()
-    : ciaddr_(ZERO_ADDRESS), giaddr_(ZERO_ADDRESS),
-      local_address_(ZERO_ADDRESS), remote_address_(ZERO_ADDRESS),
-      client_classes_(ClientClasses()), iface_name_(std::string()) {
-}
-
 void
 CfgSubnets4::add(const Subnet4Ptr& subnet) {
     /// @todo: Check that this new subnet does not cross boundaries of any
@@ -52,7 +46,7 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
 }
 
 Subnet4Ptr
-CfgSubnets4::selectSubnet(const Selector& selector) const {
+CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
     // If relayed message has been received, try to match the giaddr with the
     // relay address specified for a subnet. It is also possible that the relay
     // address will not match with any of the relay addresses accross all

+ 20 - 26
src/lib/dhcpsrv/cfg_subnets4.h

@@ -17,6 +17,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_selector.h>
 #include <boost/shared_ptr.hpp>
 
 namespace isc {
@@ -35,30 +36,6 @@ namespace dhcp {
 class CfgSubnets4 {
 public:
 
-    /// @brief Subnet selector used in @c CfgSubnets4::selectSubnet.
-    ///
-    /// This structure holds various parameters extracted from a packet sent
-    /// by a DHCP client used to select the subnet for the client.
-    struct Selector {
-        /// @brief ciaddr from the client's message.
-        asiolink::IOAddress ciaddr_;
-        /// @brief giaddr from the client's message.
-        asiolink::IOAddress giaddr_;
-        /// @brief Address on which the message was received.
-        asiolink::IOAddress local_address_;
-        /// @brief Source address of the message.
-        asiolink::IOAddress remote_address_;
-        /// @brief Classes that the client belongs to.
-        ClientClasses client_classes_;
-        /// @brief Name of the interface on which the message was received.
-        std::string iface_name_;
-
-        /// @brief Default constructor.
-        ///
-        ///  Sets the default values for the @c Selector.
-        Selector();
-    };
-
     /// @brief Adds new subnet to the configuration.
     ///
     /// @param subnet Pointer to the subnet being added.
@@ -107,6 +84,14 @@ public:
     ///
     /// If the address matches with a subnet, the subnet is returned.
     ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets (possibly a couple of times)
+    /// to find the one which fulfils the search criteria. The subnet storage
+    /// is implemented as a simple STL vector which precludes fast searches
+    /// using specific keys. Hence, full scan is required. To improve the
+    /// search performance a different container type is required, e.g.
+    /// multi-index container, or something of a similar functionality.
+    ///
     /// @param selector Const reference to the selector structure which holds
     /// various information extracted from the client's packet which are used
     /// to find appropriate subnet.
@@ -114,13 +99,22 @@ public:
     /// @return Pointer to the selected subnet or NULL if no subnet found.
     /// @throw isc::BadValue if the values in the subnet selector are invalid
     /// or they are insufficient to select a subnet.
-    Subnet4Ptr selectSubnet(const Selector& selector) const;
+    Subnet4Ptr selectSubnet(const SubnetSelector& selector) const;
 
     /// @brief Returns pointer to a subnet if provided address is in its range.
     ///
     /// This method returns a pointer to the subnet if the address passed in
     /// parameter is in range with this subnet. This is mainly used for unit
-    /// testing. This method is also called by the @c selectSubnet(Selector).
+    /// testing. This method is also called by the
+    /// @c selectSubnet(SubnetSelector).
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets to find the one which fulfils
+    /// the search criteria. The subnet storage is implemented as a simple
+    /// STL vector which precludes fast searches using specific keys.
+    /// Hence, full scan is required. To improve the search performance a
+    /// different container type is required, e.g. multi-index container,
+    /// or something of a similar functionality.
     ///
     /// @param address Address for which the subnet is searched.
     /// @param client_classes Optional parameter specifying the classes that

+ 182 - 0
src/lib/dhcpsrv/cfg_subnets6.cc

@@ -0,0 +1,182 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcpsrv/cfg_subnets6.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/subnet_id.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+void
+CfgSubnets6::add(const Subnet6Ptr& subnet) {
+    /// @todo: Check that this new subnet does not cross boundaries of any
+    /// other already defined subnet.
+    if (isDuplicate(*subnet)) {
+        isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
+                  << subnet->getID() << "' is already in use");
+    }
+    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
+              .arg(subnet->toText());
+    subnets_.push_back(subnet);
+}
+
+Subnet6Ptr
+CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
+    Subnet6Ptr subnet;
+
+    // If relay agent link address is set to zero it means that we're dealing
+    // with a directly connected client.
+    if (selector.first_relay_linkaddr_ == IOAddress("::")) {
+        // If interface name is known try to match it with interface names
+        // specified for configured subnets.
+        if (!selector.iface_name_.empty()) {
+            subnet = selectSubnet(selector.iface_name_,
+                                  selector.client_classes_);
+        }
+
+        // If interface name didn't match, try the client's address.
+        if (!subnet && selector.remote_address_ != IOAddress("::")) {
+            subnet = selectSubnet(selector.remote_address_,
+                                  selector.client_classes_);
+        }
+
+    // If relay agent link address is set, we're dealing with a relayed message.
+    } else {
+
+        // Find the subnet using the Interface Id option, if present.
+        subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
+
+        // If Interface ID option could not be matched for any subnet, try
+        // the relay agent link address.
+        if (!subnet) {
+            subnet = selectSubnet(selector.first_relay_linkaddr_,
+                                  selector.client_classes_,
+                                  true);
+        }
+    }
+
+    // Return subnet found, or NULL if not found.
+    return (subnet);
+}
+
+Subnet6Ptr
+CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
+                          const ClientClasses& client_classes,
+                          const bool is_relay_address) const {
+
+    // If the specified address is a relay address we first need to match
+    // it with the relay addresses specified for all subnets.
+    if (is_relay_address) {
+        for (Subnet6Collection::const_iterator subnet = subnets_.begin();
+             subnet != subnets_.end(); ++subnet) {
+
+            // If the specified address matches the relay address, return this
+            // subnet.
+            if (is_relay_address &&
+                ((*subnet)->getRelayInfo().addr_ == address) &&
+                (*subnet)->clientSupported(client_classes)) {
+                LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+                          DHCPSRV_CFGMGR_SUBNET6_RELAY)
+                    .arg((*subnet)->toText()).arg(address.toText());
+                return (*subnet);
+            }
+        }
+    }
+
+    // No success so far. Check if the specified address is in range
+    // with any subnet.
+    for (Subnet6Collection::const_iterator subnet = subnets_.begin();
+         subnet != subnets_.end(); ++subnet) {
+        if ((*subnet)->inRange(address) &&
+            (*subnet)->clientSupported(client_classes)) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
+                      .arg((*subnet)->toText()).arg(address.toText());
+            return (*subnet);
+        }
+    }
+
+    // Nothing found.
+    return (Subnet6Ptr());
+}
+
+
+Subnet6Ptr
+CfgSubnets6::selectSubnet(const std::string& iface_name,
+                          const ClientClasses& client_classes) const {
+
+    // If empty interface specified, we can't select subnet by interface.
+    if (!iface_name.empty()) {
+        for (Subnet6Collection::const_iterator subnet = subnets_.begin();
+             subnet != subnets_.end(); ++subnet) {
+
+            // If interface name matches with the one specified for the subnet
+            // and the client is not rejected based on the classification,
+            // return the subnet.
+            if ((iface_name == (*subnet)->getIface()) &&
+                (*subnet)->clientSupported(client_classes)) {
+
+                LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+                          DHCPSRV_CFGMGR_SUBNET6_IFACE)
+                    .arg((*subnet)->toText()).arg(iface_name);
+                return (*subnet);
+            }
+        }
+    }
+
+    // No subnet found for this interface name.
+    return (Subnet6Ptr());
+}
+
+Subnet6Ptr
+CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
+                          const ClientClasses& client_classes) const {
+    // We can only select subnet using an interface id, if the interface
+    // id is known.
+    if (interface_id) {
+        for (Subnet6Collection::const_iterator subnet = subnets_.begin();
+             subnet != subnets_.end(); ++subnet) {
+
+            // If interface id matches for the subnet and the subnet is not
+            // rejected based on the classification.
+            if ((*subnet)->getInterfaceId() &&
+                (*subnet)->getInterfaceId()->equals(interface_id) &&
+                (*subnet)->clientSupported(client_classes)) {
+
+                LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+                      DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
+                    .arg((*subnet)->toText());
+                return (*subnet);
+            }
+        }
+    }
+    // No subnet found.
+    return (Subnet6Ptr());
+}
+
+bool
+CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
+    for (Subnet6Collection::const_iterator subnet_it = subnets_.begin();
+         subnet_it != subnets_.end(); ++subnet_it) {
+        if ((*subnet_it)->getID() == subnet.getID()) {
+            return (true);
+        }
+    }
+    return (false);
+}
+
+} // end of namespace isc::dhcp
+} // end of namespace isc

+ 211 - 0
src/lib/dhcpsrv/cfg_subnets6.h

@@ -0,0 +1,211 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CFG_SUBNETS6_H
+#define CFG_SUBNETS6_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/option.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_selector.h>
+#include <util/optional_value.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Holds subnets configured for the DHCPv6 server.
+///
+/// This class holds a collection of subnets configured for the DHCPv6 server.
+/// It allows for retrieving a subnet for the particular client using various
+/// parameters extracted from the DHCPv6 message. These parameters must be
+/// assigned to the appropriate members of the @c SubnetSelector structure.
+///
+/// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet
+/// is selected for the client.
+class CfgSubnets6 {
+public:
+
+    /// @brief Adds new subnet to the configuration.
+    ///
+    /// @param subnet Pointer to the subnet being added.
+    ///
+    /// @throw isc::DuplicateSubnetID If the subnet id for the new subnet
+    /// duplicates id of an existing subnet.
+    void add(const Subnet6Ptr& subnet);
+
+    /// @brief Returns pointer to the collection of all IPv6 subnets.
+    ///
+    /// This is used in a hook (subnet6_select), where the hook is able
+    /// to choose a different subnet. Server code has to offer a list
+    /// of possible choices (i.e. all subnets).
+    ///
+    /// @return A pointer to const Subnet6 collection
+    const Subnet6Collection* getAll() const {
+        return (&subnets_);
+    }
+
+    /// @brief Selects a subnet using parameters specified in the selector.
+    ///
+    /// This method tries to retrieve the subnet for the client using various
+    /// parameters extracted from the client's message using the following
+    /// logic.
+    ///
+    /// If the relay agent link address is set to zero it is assumed that
+    /// the subnet is selected for the directly connected client.
+    /// In this case it is checked if there is any subnet associated with the
+    /// interface over which the message has been received. If there is no
+    /// subnet explicitly associated with this interface the client's address
+    /// will be used to check if the address is in range with any of the
+    /// subnets.
+    ///
+    /// If the message was relayed it is possible that the relay agent has
+    /// appended an Interface ID option. If this option is present, the method
+    /// will check if it matches with any explicitly specified interface id
+    /// for any subnet. If it does, the subnet is returned. Otherwise, the
+    /// relay agents link address is used to select the subnet. In this case,
+    /// the method will first check if this link address is explicitly
+    /// associated with any subnet. If not, it is checked if the link address
+    /// is in range with any of the subnets.
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets (possibly a couple of times)
+    /// to find the one which fulfils the search criteria. The subnet storage
+    /// is implemented as a simple STL vector which precludes fast searches
+    /// using specific keys. Hence, full scan is required. To improve the
+    /// search performance a different container type is required, e.g.
+    /// multi-index container, or something of a similar functionality.
+    ///
+    /// @param selector Const reference to the selector structure which holds
+    /// various information extracted from the client's packet which are used
+    /// to find appropriate subnet.
+    ///
+    /// @return Pointer to the selected subnet or NULL if no subnet found.
+    Subnet6Ptr selectSubnet(const SubnetSelector& selector) const;
+
+    /// @brief Selects the subnet using a specified address.
+    ///
+    /// This method searches for the subnet using the specified address. If
+    /// the specified address is a link address on the relay agent (which is
+    /// indicated by the 3rd argument) the method will first try to match the
+    /// specified address with the relay addresses explicitly specified for
+    /// existing subnets. If no match is found, the method will check if the
+    /// address is in range with any of the subnets.
+    ///
+    /// If the address is not a relay agent link address (@c is_relay_address
+    /// is set to false), the method will simply check if the address is in
+    /// range with any of the subnets.
+    ///
+    /// @note This method is mainly to be used in unit tests, which often
+    /// require sanity-checking if the subnet exists for the particular
+    /// address. For other purposes the @c selectSubnet(SubnetSelector) should
+    /// rather be used instead.
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets (possibly a couple of times)
+    /// to find the one which fulfils the search criteria. The subnet storage
+    /// is implemented as a simple STL vector which precludes fast searches
+    /// using specific keys. Hence, full scan is required. To improve the
+    /// search performance a different container type is required, e.g.
+    /// multi-index container, or something of a similar functionality.
+    ///
+    /// @param address Address for which the subnet is searched.
+    /// @param client_classes Optional parameter specifying the classes that
+    /// the client belongs to.
+    /// @param is_relay_address Specifies if the provided address is an
+    /// address of the relay agent (true) or not (false).
+    ///
+    /// @return Pointer to the selected subnet or NULL if no subnet found.
+    Subnet6Ptr
+    selectSubnet(const asiolink::IOAddress& address,
+                 const ClientClasses& client_classes = ClientClasses(),
+                 const bool is_relay_address = false) const;
+
+private:
+
+    /// @brief Selects a subnet using the interface name.
+    ///
+    /// This method searches for the subnet using the name of the interface.
+    /// If any of the subnets is explicitly associated with the interface
+    /// name, the subnet is returned.
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets to find the one which fulfils
+    /// the search criteria. The subnet storage is implemented as a
+    /// simple STL vector which precludes fast searches using specific
+    /// keys. Hence, full scan is required. To improve the search
+    /// performance a different container type is required, e.g.
+    /// multi-index container, or something of a similar functionality.
+    ///
+    /// @param iface_name Interface name.
+    /// @param client_classes Optional parameter specifying the classes that
+    /// the client belongs to.
+    ///
+    /// @return Pointer to the selected subnet or NULL if no subnet found.
+    Subnet6Ptr
+    selectSubnet(const std::string& iface_name,
+                 const ClientClasses& client_classes) const;
+
+    /// @brief Selects a subnet using Interface ID option.
+    ///
+    /// This method searches for the subnet using the Interface ID option
+    /// inserted by the relay agent to the message from a client. If any
+    /// of the subnets is explicitly associated with that interface id, the
+    /// subnet is returned.
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets to find the one which fulfils
+    /// the search criteria. The subnet storage is implemented as a
+    /// simple STL vector which precludes fast searches using specific
+    /// keys. Hence, full scan is required. To improve the search
+    /// performance a different container type is required, e.g.
+    /// multi-index container, or something of a similar functionality.
+    ///
+    /// @param interface_id An instance of the Interface ID option received
+    /// from the client.
+    /// @param client_classes Optional parameter specifying the classes that
+    /// the client belongs to.
+    ///
+    /// @return Pointer to the selected subnet or NULL if no subnet found.
+    Subnet6Ptr
+    selectSubnet(const OptionPtr& interface_id,
+                 const ClientClasses& client_classes) const;
+
+    /// @brief Checks that the IPv6 subnet with the given id already exists.
+    ///
+    /// @param subnet Subnet for which this function will check if the other
+    /// subnet with equal id already exists.
+    ///
+    /// @return true if the duplicate subnet exists.
+    bool isDuplicate(const Subnet6& subnet) const;
+
+    /// @brief A container for IPv6 subnets.
+    Subnet6Collection subnets_;
+
+};
+
+/// @name Pointer to the @c CfgSubnets6 objects.
+//@{
+/// @brief Non-const pointer.
+typedef boost::shared_ptr<CfgSubnets6> CfgSubnets6Ptr;
+
+/// @brief Const pointer.
+typedef boost::shared_ptr<const CfgSubnets6> ConstCfgSubnets6Ptr;
+
+//@}
+
+}
+}
+
+#endif // CFG_SUBNETS6_H

+ 0 - 108
src/lib/dhcpsrv/cfgmgr.cc

@@ -62,114 +62,6 @@ CfgMgr::addOptionSpace6(const OptionSpacePtr& space) {
     spaces6_.insert(make_pair(space->getName(), space));
 }
 
-Subnet6Ptr
-CfgMgr::getSubnet6(const std::string& iface,
-                   const isc::dhcp::ClientClasses& classes) {
-
-    if (!iface.length()) {
-        return (Subnet6Ptr());
-    }
-
-    // If there is more than one, we need to choose the proper one
-    for (Subnet6Collection::iterator subnet = subnets6_.begin();
-         subnet != subnets6_.end(); ++subnet) {
-
-        // If client is rejected because of not meeting client class criteria...
-        if (!(*subnet)->clientSupported(classes)) {
-            continue;
-        }
-
-        if (iface == (*subnet)->getIface()) {
-            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
-                      DHCPSRV_CFGMGR_SUBNET6_IFACE)
-                .arg((*subnet)->toText()).arg(iface);
-            return (*subnet);
-        }
-    }
-    return (Subnet6Ptr());
-}
-
-Subnet6Ptr
-CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint,
-                   const isc::dhcp::ClientClasses& classes,
-                   const bool relay) {
-
-    // If there is more than one, we need to choose the proper one
-    for (Subnet6Collection::iterator subnet = subnets6_.begin();
-         subnet != subnets6_.end(); ++subnet) {
-
-        // If client is rejected because of not meeting client class criteria...
-        if (!(*subnet)->clientSupported(classes)) {
-            continue;
-        }
-
-        // If the hint is a relay address, and there is relay info specified
-        // for this subnet and those two match, then use this subnet.
-        if (relay && ((*subnet)->getRelayInfo().addr_ == hint) ) {
-            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
-                      DHCPSRV_CFGMGR_SUBNET6_RELAY)
-                .arg((*subnet)->toText()).arg(hint.toText());
-            return (*subnet);
-        }
-
-        if ((*subnet)->inRange(hint)) {
-            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
-                      .arg((*subnet)->toText()).arg(hint.toText());
-            return (*subnet);
-        }
-    }
-
-    // sorry, we don't support that subnet
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_NO_SUBNET6)
-              .arg(hint.toText());
-    return (Subnet6Ptr());
-}
-
-Subnet6Ptr CfgMgr::getSubnet6(OptionPtr iface_id_option,
-                              const isc::dhcp::ClientClasses& classes) {
-    if (!iface_id_option) {
-        return (Subnet6Ptr());
-    }
-
-    // Let's iterate over all subnets and for those that have interface-id
-    // defined, check if the interface-id is equal to what we are looking for
-    for (Subnet6Collection::iterator subnet = subnets6_.begin();
-         subnet != subnets6_.end(); ++subnet) {
-
-        // If client is rejected because of not meeting client class criteria...
-        if (!(*subnet)->clientSupported(classes)) {
-            continue;
-        }
-
-        if ( (*subnet)->getInterfaceId() &&
-             ((*subnet)->getInterfaceId()->equals(iface_id_option))) {
-            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
-                      DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
-                .arg((*subnet)->toText());
-            return (*subnet);
-        }
-    }
-    return (Subnet6Ptr());
-}
-
-void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
-    /// @todo: Check that this new subnet does not cross boundaries of any
-    /// other already defined subnet.
-    /// @todo: Check that there is no subnet with the same interface-id
-    if (isDuplicate(*subnet)) {
-        isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
-                  << subnet->getID() << "' is already in use");
-    }
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
-              .arg(subnet->toText());
-    subnets6_.push_back(subnet);
-}
-
-void CfgMgr::deleteSubnets6() {
-    LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_DELETE_SUBNET6);
-    subnets6_.clear();
-}
-
 
 std::string CfgMgr::getDataDir() {
     return (datadir_);

+ 0 - 91
src/lib/dhcpsrv/cfgmgr.h

@@ -126,97 +126,6 @@ public:
         return (spaces6_);
     }
 
-    /// @brief get IPv6 subnet by address
-    ///
-    /// Finds a matching subnet, based on an address. This can be used
-    /// in two cases: when trying to find an appropriate lease based on
-    /// a) relay link address (that must be the address that is on link)
-    /// b) our global address on the interface the message was received on
-    ///    (for directly connected clients)
-    ///
-    /// If there are any classes specified in a subnet, that subnet
-    /// will be selected only if the client belongs to appropriate class.
-    ///
-    /// @note The client classification is checked before any relay
-    /// information checks are conducted.
-    ///
-    /// If relay is true then relay info overrides (i.e. value the sysadmin
-    /// can configure in Dhcp6/subnet6[X]/relay/ip-address) can be used.
-    /// That is applicable only for relays. Those overrides must not be used
-    /// for client address or for client hints. They are for link-addr field
-    /// in the RELAY_FORW message only.
-    ///
-    /// @param hint an address that belongs to a searched subnet
-    /// @param classes classes the client belongs to
-    /// @param relay true if address specified in hint is a relay
-    ///
-    /// @return a subnet object (or NULL if no suitable match was fount)
-    Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint,
-                          const isc::dhcp::ClientClasses& classes,
-                          const bool relay = false);
-
-    /// @brief get IPv6 subnet by interface name
-    ///
-    /// Finds a matching local subnet, based on interface name. This
-    /// is used for selecting subnets that were explicitly marked by the
-    /// user as reachable over specified network interface.
-    ///
-    /// If there are any classes specified in a subnet, that subnet
-    /// will be selected only if the client belongs to appropriate class.
-    ///
-    /// @param iface_name interface name
-    /// @param classes classes the client belongs to
-    ///
-    /// @return a subnet object (or NULL if no suitable match was fount)
-    Subnet6Ptr getSubnet6(const std::string& iface_name,
-                          const isc::dhcp::ClientClasses& classes);
-
-    /// @brief get IPv6 subnet by interface-id
-    ///
-    /// Another possibility to find a subnet is based on interface-id.
-    ///
-    /// If there are any classes specified in a subnet, that subnet
-    /// will be selected only if the client belongs to appropriate class.
-    ///
-    /// @param interface_id content of interface-id option returned by a relay
-    /// @param classes classes the client belongs to
-    ///
-    /// @return a subnet object
-    Subnet6Ptr getSubnet6(OptionPtr interface_id,
-                          const isc::dhcp::ClientClasses& classes);
-
-    /// @brief adds an IPv6 subnet
-    ///
-    /// @param subnet new subnet to be added.
-    void addSubnet6(const Subnet6Ptr& subnet);
-
-    /// @todo: Add subnet6 removal routines. Currently it is not possible
-    /// to remove subnets. The only case where subnet6 removal would be
-    /// needed is a dynamic server reconfiguration - a use case that is not
-    /// planned to be supported any time soon.
-
-    /// @brief removes all IPv6 subnets
-    ///
-    /// This method removes all existing IPv6 subnets. It is used during
-    /// reconfiguration - old configuration is wiped and new definitions
-    /// are used to recreate subnets.
-    ///
-    /// @todo Implement more intelligent approach. Note that comparison
-    /// between old and new configuration is tricky. For example: is
-    /// 2000::/64 and 2000::/48 the same subnet or is it something
-    /// completely new?
-    void deleteSubnets6();
-
-    /// @brief returns const reference to all subnets6
-    ///
-    /// This is used in a hook (subnet6_select), where the hook is able
-    /// to choose a different subnet. Server code has to offer a list
-    /// of possible choices (i.e. all subnets).
-    /// @return a pointer to const Subnet6 collection
-    const Subnet6Collection* getSubnets6() {
-        return (&subnets6_);
-    }
-
     /// @brief returns path do the data directory
     ///
     /// This method returns a path to writeable directory that DHCP servers

+ 0 - 4
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -83,10 +83,6 @@ of active interfaces. This doesn't prevent the server from listening to
 the DHCP traffic through open sockets, but will rather be used by Interface
 Manager to select active interfaces when sockets are re-opened.
 
-% DHCPSRV_CFGMGR_DELETE_SUBNET6 deleting all IPv6 subnets
-A debug message noting that the DHCP configuration manager has deleted all IPv6
-subnets in its database.
-
 % DHCPSRV_CFGMGR_NO_SUBNET4 no suitable subnet is defined for address hint %1
 This debug message is output when the DHCP configuration manager has received
 a request for an IPv4 subnet for the specified address, but no such

+ 5 - 3
src/lib/dhcpsrv/srv_config.cc

@@ -26,12 +26,14 @@ namespace dhcp {
 
 SrvConfig::SrvConfig()
     : sequence_(0), cfg_option_def_(new CfgOptionDef()),
-      cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()) {
+      cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()),
+      cfg_subnets6_(new CfgSubnets6()) {
 }
 
 SrvConfig::SrvConfig(const uint32_t sequence)
     : sequence_(sequence), cfg_option_def_(new CfgOptionDef()),
-      cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()) {
+      cfg_option_(new CfgOption()), cfg_subnets4_(new CfgSubnets4()),
+      cfg_subnets6_(new CfgSubnets6()) {
 }
 
 std::string
@@ -49,7 +51,7 @@ SrvConfig::getConfigSummary(const uint32_t selection) const {
     }
 
     if ((selection & CFGSEL_SUBNET6) == CFGSEL_SUBNET6) {
-        subnets_num = CfgMgr::instance().getSubnets6()->size();
+        subnets_num = getCfgSubnets6()->getAll()->size();
         if (subnets_num > 0) {
             s << "added IPv6 subnets: " << subnets_num;
         } else {

+ 20 - 0
src/lib/dhcpsrv/srv_config.h

@@ -19,6 +19,7 @@
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option_def.h>
 #include <dhcpsrv/cfg_subnets4.h>
+#include <dhcpsrv/cfg_subnets6.h>
 #include <dhcpsrv/logging_info.h>
 #include <boost/shared_ptr.hpp>
 #include <vector>
@@ -201,6 +202,22 @@ public:
         return (cfg_subnets4_);
     }
 
+    /// @brief Returns pointer to non-const object holding subnets configuration
+    /// for DHCPv6.
+    ///
+    /// @return Pointer to the object holding subnets configuration for DHCPv4.
+    CfgSubnets6Ptr getCfgSubnets6() {
+        return (cfg_subnets6_);
+    }
+
+    /// @brief Returns pointer to const object holding subnets configuration for
+    /// DHCPv4.
+    ///
+    /// @return Pointer to the object holding subnets configuration for DHCPv6.
+    ConstCfgSubnets6Ptr getCfgSubnets6() const {
+        return (cfg_subnets6_);
+    }
+
     //@}
 
     /// @brief Copies the currnet configuration to a new configuration.
@@ -305,6 +322,9 @@ private:
     /// @brief Pointer to subnets configuration for IPv4.
     CfgSubnets4Ptr cfg_subnets4_;
 
+    /// @brief Pointer to subnets configuration for IPv4.
+    CfgSubnets6Ptr cfg_subnets6_;
+
 };
 
 /// @name Pointers to the @c SrvConfig object.

+ 75 - 0
src/lib/dhcpsrv/subnet_selector.h

@@ -0,0 +1,75 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SUBNET_SELECTOR_H
+#define SUBNET_SELECTOR_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/classify.h>
+#include <dhcp/option.h>
+#include <string>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Subnet selector used to specify parameters used to select a subnet.
+///
+/// This structure holds various parameters extracted from a packet sent
+/// by a DHCP client used to select the subnet for the client. This selector
+/// is common for IPv4 and IPv6 subnets.
+struct SubnetSelector {
+    /// @name DHCPv4 specific parameters.
+    //@{
+    /// @brief ciaddr from the client's message.
+    asiolink::IOAddress ciaddr_;
+    /// @brief giaddr from the client's message.
+    asiolink::IOAddress giaddr_;
+    //@}
+
+    /// @name DHCPv6 specific parameters.
+    //@{
+    /// @brief Interface id option.
+    OptionPtr interface_id_;
+    /// @brief First relay link address.
+    asiolink::IOAddress first_relay_linkaddr_;
+    //@}
+
+    /// @brief Address on which the message was received.
+    asiolink::IOAddress local_address_;
+    /// @brief Source address of the message.
+    asiolink::IOAddress remote_address_;
+    /// @brief Classes that the client belongs to.
+    ClientClasses client_classes_;
+    /// @brief Name of the interface on which the message was received.
+    std::string iface_name_;
+
+    /// @brief Default constructor.
+    ///
+    /// Sets the default values for the @c Selector.
+    SubnetSelector()
+        : ciaddr_(asiolink::IOAddress("0.0.0.0")),
+          giaddr_(asiolink::IOAddress("0.0.0.0")),
+          interface_id_(),
+          first_relay_linkaddr_(asiolink::IOAddress("::")),
+          local_address_(asiolink::IOAddress("0.0.0.0")),
+          remote_address_(asiolink::IOAddress("0.0.0.0")),
+          client_classes_(), iface_name_(std::string()) {
+    }
+};
+
+
+}
+}
+
+#endif // SUBNET_SELECTOR_H

+ 1 - 0
src/lib/dhcpsrv/tests/Makefile.am

@@ -59,6 +59,7 @@ libdhcpsrv_unittests_SOURCES += cfg_iface_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_option_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_option_def_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
+libdhcpsrv_unittests_SOURCES += cfg_subnets6_unittest.cc
 libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
 libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc

+ 9 - 6
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -91,6 +91,8 @@ public:
     /// in many tests, initializes cfg_mgr configuration and creates
     /// lease database.
     AllocEngine6Test() {
+        CfgMgr::instance().clear();
+
         duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
         iaid_ = 42;
 
@@ -115,7 +117,6 @@ public:
     void initSubnet(const IOAddress& subnet, const IOAddress& pool_start,
                     const IOAddress& pool_end) {
         CfgMgr& cfg_mgr = CfgMgr::instance();
-        cfg_mgr.deleteSubnets6();
 
         subnet_ = Subnet6Ptr(new Subnet6(subnet, 56, 1, 2, 3, 4));
         pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end));
@@ -125,7 +126,8 @@ public:
         pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, subnet, 56, 64));
         subnet_->addPool(pd_pool_);
 
-        cfg_mgr.addSubnet6(subnet_);
+        cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
+        cfg_mgr.commit();
 
     }
 
@@ -855,13 +857,13 @@ TEST_F(AllocEngine6Test, outOfAddresses6) {
 
     IOAddress addr("2001:db8:1::ad");
     CfgMgr& cfg_mgr = CfgMgr::instance();
-    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+    cfg_mgr.clear(); // Get rid of the default test configuration
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
     pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
-    cfg_mgr.addSubnet6(subnet_);
+    cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
 
     // Just a different duid
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
@@ -939,13 +941,14 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 
     IOAddress addr("2001:db8:1::ad");
     CfgMgr& cfg_mgr = CfgMgr::instance();
-    cfg_mgr.deleteSubnets6(); // Get rid of the default test configuration
+    cfg_mgr.clear(); // Get rid of the default test configuration
 
     // Create configuration similar to other tests, but with a single address pool
     subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
     pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
     subnet_->addPool(pool_);
-    cfg_mgr.addSubnet6(subnet_);
+    cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
+    cfg_mgr.commit();
 
     // Let's create an expired lease
     DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));

+ 11 - 10
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc

@@ -18,6 +18,7 @@
 #include <dhcpsrv/cfg_subnets4.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/subnet_selector.h>
 #include <gtest/gtest.h>
 
 using namespace isc;
@@ -29,7 +30,7 @@ namespace {
 
 // This test verifies that it is possible to retrieve a subnet using an
 // IP address.
-TEST(CfgSubnets4Test, getSubnetByCiaddr) {
+TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
     CfgSubnets4 cfg;
 
     // Create 3 subnets.
@@ -38,7 +39,7 @@ TEST(CfgSubnets4Test, getSubnetByCiaddr) {
     Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
 
     // Make sure that initially the subnets don't exist.
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
     selector.ciaddr_ = IOAddress("192.0.2.0");
     // Set some unicast local address to simulate a Renew.
     selector.local_address_ = IOAddress("10.0.0.100");
@@ -70,7 +71,7 @@ TEST(CfgSubnets4Test, getSubnetByCiaddr) {
 
 // This test verifies that when the classification information is specified for
 // subnets, the proper subnets are returned by the subnet configuration.
-TEST(CfgSubnets4Test, getSubnetByClasses) {
+TEST(CfgSubnets4Test, selectSubnetByClasses) {
     CfgSubnets4 cfg;
 
     // Create 3 subnets.
@@ -83,7 +84,7 @@ TEST(CfgSubnets4Test, getSubnetByClasses) {
     cfg.add(subnet2);
     cfg.add(subnet3);
 
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
 
     selector.local_address_ = IOAddress("10.0.0.10");
 
@@ -144,7 +145,7 @@ TEST(CfgSubnets4Test, getSubnetByClasses) {
 
 // This test verifies that the relay information can be used to retrieve the
 // subnet.
-TEST(CfgSubnetsTest, getSubnetByRelayAddress) {
+TEST(CfgSubnetsTest, selectSubnetByRelayAddress) {
     CfgSubnets4 cfg;
 
     // Create 3 subnets.
@@ -157,7 +158,7 @@ TEST(CfgSubnetsTest, getSubnetByRelayAddress) {
     cfg.add(subnet2);
     cfg.add(subnet3);
 
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
 
     // Check that without relay-info specified, subnets are not selected
     selector.giaddr_ = IOAddress("10.0.0.1");
@@ -183,7 +184,7 @@ TEST(CfgSubnetsTest, getSubnetByRelayAddress) {
 
 // This test verifies that the subnet can be selected for the client
 // using a source address if the client hasn't set the ciaddr.
-TEST(CfgSubnetsTest, getSubnetNoCiaddr) {
+TEST(CfgSubnetsTest, selectSubnetNoCiaddr) {
     CfgSubnets4 cfg;
 
     // Create 3 subnets.
@@ -192,7 +193,7 @@ TEST(CfgSubnetsTest, getSubnetNoCiaddr) {
     Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
 
     // Make sure that initially the subnets don't exist.
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
     selector.remote_address_ = IOAddress("192.0.2.0");
     // Set some unicast local address to simulate a Renew.
     selector.local_address_ = IOAddress("10.0.0.100");
@@ -223,7 +224,7 @@ TEST(CfgSubnetsTest, getSubnetNoCiaddr) {
 
 // This test verifies that the subnet can be selected using an address
 // set on the local interface.
-TEST(CfgSubnetsTest, getSubnetInterface) {
+TEST(CfgSubnetsTest, selectSubnetInterface) {
     // The IfaceMgrTestConfig object initializes fake interfaces:
     // eth0, eth1 and lo on the configuration manager. The CfgSubnets4
     // object uses addresses assigned to these fake interfaces to
@@ -231,7 +232,7 @@ TEST(CfgSubnetsTest, getSubnetInterface) {
     IfaceMgrTestConfig config(true);
 
     CfgSubnets4 cfg;
-    CfgSubnets4::Selector selector;
+    SubnetSelector selector;
 
     // Initially, there are no subnets configured, so none of the IPv4
     // addresses assigned to eth0 and eth1 can match with any subnet.

+ 353 - 0
src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc

@@ -0,0 +1,353 @@
+// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp/classify.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/option.h>
+#include <dhcpsrv/cfg_subnets6.h>
+#include <dhcpsrv/subnet.h>
+#include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/subnet_selector.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief Generates interface id option.
+///
+/// @param text Interface id in a textual format.
+OptionPtr
+generateInterfaceId(const std::string& text) {
+    OptionBuffer buffer(text.begin(), text.end());
+    return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer));
+}
+
+// This test checks that the subnet can be selected using a relay agent's
+// link address.
+TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {
+    CfgSubnets6 cfg;
+
+    // Let's configure 3 subnets
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Make sure that none of the subnets is selected when there is no relay
+    // information configured for them.
+    SubnetSelector selector;
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::1");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::2");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::3");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Now specify relay information.
+    subnet1->setRelayInfo(IOAddress("2001:db8:ff::1"));
+    subnet2->setRelayInfo(IOAddress("2001:db8:ff::2"));
+    subnet3->setRelayInfo(IOAddress("2001:db8:ff::3"));
+
+    // And try again. This time relay-info is there and should match.
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::1");
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::2");
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::3");
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+}
+
+// This test checks that the subnet can be selected using an interface
+// name associated with a asubnet.
+TEST(CfgSubnets6Test, selectSubnetByInterfaceName) {
+    CfgSubnets6 cfg;
+
+    // Let's create 3 subnets.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+    subnet1->setIface("foo");
+    subnet2->setIface("bar");
+    subnet3->setIface("foobar");
+
+    // Until subnets are added to the configuration, there should be nothing
+    // returned.
+    SubnetSelector selector;
+    selector.iface_name_ = "foo";
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add one of the subnets.
+    cfg.add(subnet1);
+
+    // The subnet should be now selected for the interface name "foo".
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+
+    // Check that the interface name is checked even when there is
+    // only one subnet defined: there should be nothing returned when
+    // other interface name is specified.
+    selector.iface_name_ = "bar";
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add other subnets.
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // When we specify correct interface names, the subnets should be returned.
+    selector.iface_name_ = "foobar";
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+    selector.iface_name_ = "bar";
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+
+    // When specifying a non-existing interface the subnet should not be
+    // returned.
+    selector.iface_name_ = "xyzzy";
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
+// This test checks that the subnet can be selected using an Interface ID
+// option inserted by a relay.
+TEST(CfgSubnets6Test, selectSubnetByInterfaceId) {
+    CfgSubnets6 cfg;
+
+    // Create 3 subnets.
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+
+    // Create Interface-id options used in subnets 1,2, and 3
+    OptionPtr ifaceid1 = generateInterfaceId("relay1.eth0");
+    OptionPtr ifaceid2 = generateInterfaceId("VL32");
+    // That's a strange interface-id, but this is a real life example
+    OptionPtr ifaceid3 = generateInterfaceId("ISAM144|299|ipv6|nt:vp:1:110");
+
+    // Bogus interface-id.
+    OptionPtr ifaceid_bogus = generateInterfaceId("non-existent");
+
+    // Assign interface ids to the respective subnets.
+    subnet1->setInterfaceId(ifaceid1);
+    subnet2->setInterfaceId(ifaceid2);
+    subnet3->setInterfaceId(ifaceid3);
+
+    // There shouldn't be any subnet configured at this stage.
+    SubnetSelector selector;
+    selector.interface_id_ = ifaceid1;
+    // Note that some link address must be specified to indicate that it is
+    // a relayed message!
+    selector.first_relay_linkaddr_ = IOAddress("5000::1");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add one of the subnets.
+    cfg.add(subnet1);
+
+    // If only one subnet has been specified, it should be returned when the
+    // interface id matches. But, for a different interface id there should be
+    // no match.
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Add other subnets.
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Now that we have all subnets added. we should be able to retrieve them
+    // using appropriate interface ids.
+    selector.interface_id_ = ifaceid3;
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+
+    // For invalid interface id, there should be nothing returned.
+    selector.interface_id_ = ifaceid_bogus;
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
+// Test that the client classes are considered when the subnet is selected by
+// the relay link address.
+TEST(CfgSubnets6Test, selectSubnetByRelayAddressAndClassify) {
+    CfgSubnets6 cfg;
+
+    // Let's configure 3 subnets
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Let's sanity check that we can use that configuration.
+    SubnetSelector selector;
+    selector.first_relay_linkaddr_ = IOAddress("2000::123");
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("3000::345");
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("4000::567");
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+
+    // Client now belongs to bar class.
+    selector.client_classes_.insert("bar");
+
+    // There are no class restrictions defined, so everything should work
+    // as before.
+    selector.first_relay_linkaddr_ = IOAddress("2000::123");
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("3000::345");
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("4000::567");
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+
+    // Now let's add client class restrictions.
+    subnet1->allowClientClass("foo"); // Serve here only clients from foo class
+    subnet2->allowClientClass("bar"); // Serve here only clients from bar class
+    subnet3->allowClientClass("baz"); // Serve here only clients from baz class
+
+    // The same check as above should result in client being served only in
+    // bar class, i.e. subnet2
+    selector.first_relay_linkaddr_ = IOAddress("2000::123");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("3000::345");
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("4000::567");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Now let's check that client with wrong class is not supported
+    selector.client_classes_.clear();
+    selector.client_classes_.insert("some_other_class");
+    selector.first_relay_linkaddr_ = IOAddress("2000::123");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("3000::345");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("4000::567");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+
+    // Finally, let's check that client without any classes is not supported
+    selector.client_classes_.clear();
+    selector.first_relay_linkaddr_ = IOAddress("2000::123");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("3000::345");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.first_relay_linkaddr_ = IOAddress("4000::567");
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
+// Test that client classes are considered when the subnet is selcted by the
+// interface name.
+TEST(CfgSubnets6Test, selectSubnetByInterfaceNameAndClaassify) {
+    CfgSubnets6 cfg;
+
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+    subnet1->setIface("foo");
+    subnet2->setIface("bar");
+    subnet3->setIface("foobar");
+
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Now we have only one subnet, any request will be served from it
+    SubnetSelector selector;
+    selector.client_classes_.insert("bar");
+    selector.iface_name_ = "foo";
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.iface_name_ = "bar";
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.iface_name_ = "foobar";
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+
+    subnet1->allowClientClass("foo"); // Serve here only clients from foo class
+    subnet2->allowClientClass("bar"); // Serve here only clients from bar class
+    subnet3->allowClientClass("baz"); // Serve here only clients from baz class
+
+    selector.iface_name_ = "foo";
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.iface_name_ = "bar";
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.iface_name_ = "foobar";
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
+// Test that client classes are considered when the interface is selected by
+// the interface id.
+TEST(CfgSubnets6Test, selectSubnetByInterfaceIdAndClassify) {
+    CfgSubnets6 cfg;
+
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+
+    // interface-id options used in subnets 1,2, and 3
+    OptionPtr ifaceid1 = generateInterfaceId("relay1.eth0");
+    OptionPtr ifaceid2 = generateInterfaceId("VL32");
+    // That's a strange interface-id, but this is a real life example
+    OptionPtr ifaceid3 = generateInterfaceId("ISAM144|299|ipv6|nt:vp:1:110");
+
+    // bogus interface-id
+    OptionPtr ifaceid_bogus = generateInterfaceId("non-existent");
+
+    subnet1->setInterfaceId(ifaceid1);
+    subnet2->setInterfaceId(ifaceid2);
+    subnet3->setInterfaceId(ifaceid3);
+
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // If we have only a single subnet and the request came from a local
+    // address, let's use that subnet
+    SubnetSelector selector;
+    selector.first_relay_linkaddr_ = IOAddress("5000::1");
+    selector.client_classes_.insert("bar");
+    selector.interface_id_ = ifaceid1;
+    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid3;
+    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
+
+    subnet1->allowClientClass("foo"); // Serve here only clients from foo class
+    subnet2->allowClientClass("bar"); // Serve here only clients from bar class
+    subnet3->allowClientClass("baz"); // Serve here only clients from baz class
+
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid2;
+    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
+    selector.interface_id_ = ifaceid3;
+    EXPECT_FALSE(cfg.selectSubnet(selector));
+}
+
+// Checks that detection of duplicated subnet IDs works as expected. It should
+// not be possible to add two IPv6 subnets holding the same ID.
+TEST(CfgSubnets6, duplication) {
+    CfgSubnets6 cfg;
+
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4, 123));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4, 124));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4, 123));
+
+    ASSERT_NO_THROW(cfg.add(subnet1));
+    EXPECT_NO_THROW(cfg.add(subnet2));
+    // Subnet 3 has the same ID as subnet 1. It shouldn't be able to add it.
+    EXPECT_THROW(cfg.add(subnet3), isc::dhcp::DuplicateSubnetID);
+}
+
+} // end of anonymous namespace

+ 0 - 322
src/lib/dhcpsrv/tests/cfgmgr_unittest.cc

@@ -283,7 +283,6 @@ public:
 
     void clear() {
         CfgMgr::instance().setVerbose(false);
-        CfgMgr::instance().deleteSubnets6();
         CfgMgr::instance().clear();
     }
 
@@ -304,307 +303,6 @@ TEST_F(CfgMgrTest, configuration) {
     EXPECT_TRUE(configuration->getLoggingInfo().empty());
 }
 
-// This test verifies if the configuration manager is able to hold v6 subnets
-// with their relay address information and return proper subnets, based on
-// those addresses.
-TEST_F(CfgMgrTest, subnet6RelayOverride) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    // Let's configure 3 subnets
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
-
-    cfg_mgr.addSubnet6(subnet1);
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    // Check that without relay-info specified, subnets are not selected
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, true));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, true));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, true));
-
-    // Now specify relay info
-    subnet1->setRelayInfo(IOAddress("2001:db8:ff::1"));
-    subnet2->setRelayInfo(IOAddress("2001:db8:ff::2"));
-    subnet3->setRelayInfo(IOAddress("2001:db8:ff::3"));
-
-    // And try again. This time relay-info is there and should match.
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, true));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, true));
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, true));
-
-    // Finally, check that the relay works only if hint provided is relay address
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::1"), classify_, false));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::2"), classify_, false));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2001:db8:ff::3"), classify_, false));
-}
-
-
-// This test verifies if the configuration manager is able to hold and return
-// valid leases
-TEST_F(CfgMgrTest, classifySubnet6) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    // Let's configure 3 subnets
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-
-    cfg_mgr.addSubnet6(subnet1);
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    // Let's sanity check that we can use that configuration.
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
-
-    // Client now belongs to bar class.
-    classify_.insert("bar");
-
-    // There are no class restrictions defined, so everything should work
-    // as before
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
-
-    // Now let's add client class restrictions.
-    subnet1->allowClientClass("foo"); // Serve here only clients from foo class
-    subnet2->allowClientClass("bar"); // Serve here only clients from bar class
-    subnet3->allowClientClass("baz"); // Serve here only clients from baz class
-
-    // The same check as above should result in client being served only in
-    // bar class, i.e. subnet2
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
-
-    // Now let's check that client with wrong class is not supported
-    classify_.clear();
-    classify_.insert("some_other_class");
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
-
-    // Finally, let's check that client without any classes is not supported
-    classify_.clear();
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::345"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::567"), classify_));
-}
-
-// This test verifies if the configuration manager is able to hold, select
-// and return valid subnets, based on interface names along with client
-// classification.
-TEST_F(CfgMgrTest, classifySubnet6Interface) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    // Let's have an odd configuration: 3 shared subnets available on the
-    // same direct link.
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-    subnet1->setIface("foo");
-    subnet2->setIface("foo");
-    subnet3->setIface("foo");
-    cfg_mgr.addSubnet6(subnet1);
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-
-    // Regular client should get the first subnet, because it meets all
-    // criteria (matching interface name, no class restrictions.
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo", classify_));
-
-    // Now let's add class requirements for subnet1
-    subnet1->allowClientClass("alpha");
-
-    // Client should now get the subnet2, because he no longer meets
-    // requirements for subnet1 (belongs to wrong class)
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6("foo", classify_));
-
-    // Now let's add (not matching) classes to the other two subnets
-    subnet2->allowClientClass("beta");
-    subnet3->allowClientClass("gamma");
-
-    // No subnets are suitable, so nothing will be selected
-    EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
-
-    // Ok, let's add the client to gamme class, so he'll get a subnet
-    classify_.insert("gamma");
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6("foo", classify_));
-}
-
-// This test verifies if the configuration manager is able to hold, select
-// and return valid subnets, based on interface-id option inserted by relay,
-// along with client classification.
-TEST_F(CfgMgrTest, classifySubnet6InterfaceId) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    // Let's have an odd configuration: 3 shared subnets available via the
-    // same remote relay with the same interface-id.
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-    OptionPtr ifaceid = generateInterfaceId("relay1.eth0");
-    subnet1->setInterfaceId(ifaceid);
-    subnet2->setInterfaceId(ifaceid);
-    subnet3->setInterfaceId(ifaceid);
-    cfg_mgr.addSubnet6(subnet1);
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    // Regular client should get the first subnet, because it meets all
-    // criteria (matching interface name, no class restrictions.
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(ifaceid, classify_));
-
-    // Now let's add class requirements for subnet1
-    subnet1->allowClientClass("alpha");
-
-    // Client should now get the subnet2, because he no longer meets
-    // requirements for subnet1 (belongs to wrong class)
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(ifaceid, classify_));
-
-    // Now let's add (not matching) classes to the other two subnets
-    subnet2->allowClientClass("beta");
-    subnet3->allowClientClass("gamma");
-
-    // No subnets are suitable, so nothing will be selected
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid, classify_));
-
-    // Ok, let's add the client to gamme class, so he'll get a subnet
-    classify_.insert("gamma");
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(ifaceid, classify_));
-}
-
-// This test verifies if the configuration manager is able to hold and return
-// valid leases
-TEST_F(CfgMgrTest, subnet6) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-
-    // There shouldn't be any subnet configured at this stage
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::1"), classify_));
-
-    cfg_mgr.addSubnet6(subnet1);
-
-    // Now we have only one subnet, any request will be served from it
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2000::1"), classify_));
-
-    // We used to allow getting a sole subnet if there was only one subnet
-    // configured. That is no longer true. The code should not return
-    // a subnet.
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"), classify_));
-
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123"), classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef"),
-                  classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("5000::1"), classify_));
-
-    // Check that deletion of the subnets works.
-    cfg_mgr.deleteSubnets6();
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("2000::123"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("3000::123"), classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("4000::123"), classify_));
-}
-
-// This test verifies if the configuration manager is able to hold, select
-// and return valid subnets, based on interface names.
-TEST_F(CfgMgrTest, subnet6Interface) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-    subnet1->setIface("foo");
-    subnet2->setIface("bar");
-    subnet3->setIface("foobar");
-
-    // There shouldn't be any subnet configured at this stage
-    EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
-
-    cfg_mgr.addSubnet6(subnet1);
-
-    // Now we have only one subnet, any request will be served from it
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6("foo", classify_));
-
-    // Check that the interface name is checked even when there is
-    // only one subnet defined.
-    EXPECT_FALSE(cfg_mgr.getSubnet6("bar", classify_));
-
-    // We used to allow getting a sole subnet if there was only one subnet
-    // configured. That is no longer true. The code should not return
-    // a subnet.
-    EXPECT_FALSE(cfg_mgr.getSubnet6(IOAddress("fe80::dead:beef"), classify_));
-
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6("foobar", classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6("bar", classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6("xyzzy", classify_)); // no such interface
-
-    // Check that deletion of the subnets works.
-    cfg_mgr.deleteSubnets6();
-    EXPECT_FALSE(cfg_mgr.getSubnet6("foo", classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6("bar", classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6("foobar", classify_));
-}
-
-// This test verifies if the configuration manager is able to hold, select
-// and return valid leases, based on interface-id option values
-TEST_F(CfgMgrTest, subnet6InterfaceId) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
-
-    // interface-id options used in subnets 1,2, and 3
-    OptionPtr ifaceid1 = generateInterfaceId("relay1.eth0");
-    OptionPtr ifaceid2 = generateInterfaceId("VL32");
-    // That's a strange interface-id, but this is a real life example
-    OptionPtr ifaceid3 = generateInterfaceId("ISAM144|299|ipv6|nt:vp:1:110");
-
-    // bogus interface-id
-    OptionPtr ifaceid_bogus = generateInterfaceId("non-existent");
-
-    subnet1->setInterfaceId(ifaceid1);
-    subnet2->setInterfaceId(ifaceid2);
-    subnet3->setInterfaceId(ifaceid3);
-
-    // There shouldn't be any subnet configured at this stage
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1, classify_));
-
-    cfg_mgr.addSubnet6(subnet1);
-
-    // If we have only a single subnet and the request came from a local
-    // address, let's use that subnet
-    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(ifaceid1, classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2, classify_));
-
-    cfg_mgr.addSubnet6(subnet2);
-    cfg_mgr.addSubnet6(subnet3);
-
-    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(ifaceid3, classify_));
-    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(ifaceid2, classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid_bogus, classify_));
-
-    // Check that deletion of the subnets works.
-    cfg_mgr.deleteSubnets6();
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid1, classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid2, classify_));
-    EXPECT_FALSE(cfg_mgr.getSubnet6(ifaceid3, classify_));
-}
-
-
 // This test verifies that new DHCPv4 option spaces can be added to
 // the configuration manager and that duplicated option space is
 // rejected.
@@ -731,26 +429,6 @@ TEST_F(CfgMgrTest, d2ClientConfig) {
     EXPECT_NE(*original_config, *updated_config);
 }
 
-// Checks that detection of duplicated subnet IDs works as expected. It should
-// not be possible to add two IPv6 subnets holding the same ID to the config
-// manager.
-TEST_F(CfgMgrTest, subnet6Duplication) {
-    CfgMgr& cfg_mgr = CfgMgr::instance();
-
-    Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3,
-                                   4, 123));
-    Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 64, 1, 2, 3,
-                                   4, 124));
-    Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 64, 1, 2, 3,
-                                   4, 123));
-
-    ASSERT_NO_THROW(cfg_mgr.addSubnet6(subnet1));
-    EXPECT_NO_THROW(cfg_mgr.addSubnet6(subnet2));
-    // Subnet 3 has the same ID as subnet 1. It shouldn't be able to add it.
-    EXPECT_THROW(cfg_mgr.addSubnet6(subnet3), isc::dhcp::DuplicateSubnetID);
-}
-
-
 // This test verifies that the configuration staging, commit and rollback works
 // as expected.
 TEST_F(CfgMgrTest, staging) {

+ 0 - 1
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -447,7 +447,6 @@ public:
     void reset_context(){
         // Note set context universe to V6 as it has to be something.
         CfgMgr::instance().clear();
-        CfgMgr::instance().deleteSubnets6();
         parser_context_.reset(new ParserContext(Option::V6));
 
         // Ensure no hooks libraries are loaded.

+ 1 - 17
src/lib/dhcpsrv/tests/srv_config_unittest.cc

@@ -41,8 +41,6 @@ public:
     /// is @c TEST_SUBNETS_NUM for IPv4 and IPv6 each.
     SrvConfigTest()
         : iface_mgr_test_config_(true) {
-        // Remove any subnets dangling from previous unit tests.
-        clearSubnets();
 
         // Disable DDNS.
         enableDDNS(false);
@@ -74,10 +72,7 @@ public:
     }
 
     /// @brief Destructor.
-    ///
-    /// Removes any dangling configuration.
     virtual ~SrvConfigTest() {
-        clearSubnets();
     }
 
     /// @brief Convenience function which adds IPv4 subnet to the configuration.
@@ -108,12 +103,6 @@ public:
     /// @c conf_ object.
     void addSubnet6(const unsigned int index);
 
-    /// @brief Removes all subnets from the configuration.
-    ///
-    /// @todo Modify this function once the subnet configuration is migrated
-    /// from @c CfgMgr to @c SrvConfig.
-    void clearSubnets();
-
     /// @brief Enable/disable DDNS.
     ///
     /// @param enable A boolean value indicating if the DDNS should be
@@ -146,12 +135,7 @@ SrvConfigTest::addSubnet6(const unsigned int index) {
         FAIL() << "Subnet index " << index << "out of range (0.."
                << TEST_SUBNETS_NUM << "): " << "unable to add IPv6 subnet";
     }
-    CfgMgr::instance().addSubnet6(test_subnets6_[index]);
-}
-
-void
-SrvConfigTest::clearSubnets() {
-    CfgMgr::instance().deleteSubnets6();
+    conf_.getCfgSubnets6()->add(test_subnets6_[index]);
 }
 
 void