From: Tomek Mrugalski Date: Wed, 13 Sep 2017 08:38:27 +0000 (+0200) Subject: [5357] Parser for shared-networks in DHCPv6 implemented. X-Git-Tag: trac5073a_base~9^2~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a5abebaa0f6ae2c0cc518adb7e41c3ceb7ab8619;p=thirdparty%2Fkea.git [5357] Parser for shared-networks in DHCPv6 implemented. --- diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 8756e502a8..b6cef55691 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -89,7 +89,6 @@ public: cfg->setDhcp4o6Port(dhcp4o6_port); } - /// @brief Copies subnets from shared networks to regular subnets container /// /// @param from pointer to shared networks container (copy from here) @@ -156,6 +155,13 @@ public: sharedNetworksSanityChecks(const SharedNetwork4Collection& networks, ConstElementPtr json) { + /// @todo: in case of errors, use json to extract line numbers. + if (!json) { + // No json? That means that the shared-networks was never specified + // in the config. + return; + } + // Used for names uniqueness checks. std::set names; @@ -315,6 +321,8 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set, parser.parse(cfg_option_def, option_defs); } + // This parser is used in several places, so it should be available + // early. Dhcp4ConfigParser global_parser; // Make parsers grouping. @@ -427,9 +435,10 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set, /// as well. SharedNetworks4ListParser parser; CfgSharedNetworks4Ptr cfg = srv_cfg->getCfgSharedNetworks4(); - parser.parse(cfg, config_pair.second); + // We also need to put the subnets it contains into normal + // subnets list. global_parser.copySubnets4(srv_cfg->getCfgSubnets4(), cfg); continue; } diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index dc2079b166..6e9857a581 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -604,6 +604,13 @@ public: return (ReturnType()); } + /// @brief Checks if specified subnet is part of the collection + /// + /// @param col collection of subnets to be inspected + /// @param subnet text notation (e.g. 192.0.2.0/24) + /// @param t1 expected renew-timer value + /// @param t2 expected rebind-timer value + /// @param valid expected valid-lifetime value void checkSubnet(const Subnet4Collection& col, std::string subnet, uint32_t t1, uint32_t t2, uint32_t valid) { @@ -5066,17 +5073,15 @@ TEST_F(Dhcp4ParserTest, sharedNetworksName) { // Now verify that the shared network was indeed configured. CfgSharedNetworks4Ptr cfg_net = CfgMgr::instance().getStagingCfg() ->getCfgSharedNetworks4(); - ASSERT_TRUE(cfg_net); const SharedNetwork4Collection* nets = cfg_net->getAll(); ASSERT_TRUE(nets); ASSERT_EQ(1, nets->size()); - SharedNetwork4Ptr net = *(nets->begin()); ASSERT_TRUE(net); - EXPECT_EQ("foo", net->getName()); + // Verify that there are no subnets in this shared-network const Subnet4Collection * subs = net->getAllSubnets(); ASSERT_TRUE(subs); EXPECT_EQ(0, subs->size()); diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index f316027cd9..9a6cb969fe 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -129,7 +130,12 @@ public: } }; -/// @brief Parser that takes care of global DHCPv6 parameters. +/// @brief Parser that takes care of global DHCPv6 parameters and utility +/// functions that work on global level. +/// +/// This class is a collection of utility method that either handle +/// global parameters (see @ref parse), or conducts operations on +/// global level (see @ref sanityChecks and @ref copySubnets6). /// /// See @ref parse method for a list of supported parameters. class Dhcp6ConfigParser : public isc::data::SimpleParser { @@ -158,6 +164,134 @@ public: uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port"); srv_config->setDhcp4o6Port(dhcp4o6_port); } + + /// @brief Copies subnets from shared networks to regular subnets container + /// + /// @param from pointer to shared networks container (copy from here) + /// @param dest pointer to cfg subnets6 (copy to here) + /// @throw BadValue if any pointer is missing + /// @throw DhcpConfigError if there are duplicates (or other subnet defects) + void + copySubnets6(const CfgSubnets6Ptr dest, const CfgSharedNetworks6Ptr from) { + + if (!dest || !from) { + isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null"); + } + + const SharedNetwork6Collection* networks = from->getAll(); + if (!networks) { + // Nothing to copy. Technically, it should return a pointer to empty + // container, but let's handle null pointer as well. + return; + } + + // Let's go through all the networks one by one + for (auto net = networks->begin(); net != networks->end(); ++net) { + + // For each network go through all the subnets in it. + const Subnet6Collection* subnets = (*net)->getAllSubnets(); + if (!subnets) { + // Shared network without subnets it weird, but we decided to + // accept such configurations. + continue; + } + + // For each subnet, add it to a list of regular subnets. + for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) { + dest->add(*subnet); + } + } + } + + /// @brief Conducts global sanity checks + /// + /// This method is very simply now, but more sanity checks are expected + /// in the future. + /// + /// @param cfg - the parsed structure + /// @param global global Dhcp4 scope + /// @throw DhcpConfigError in case of issues found + void + sanityChecks(SrvConfigPtr cfg, ConstElementPtr global) { + + /// Shared network sanity checks + const SharedNetwork6Collection* networks = cfg->getCfgSharedNetworks6()->getAll(); + if (networks) { + sharedNetworksSanityChecks(*networks, global->get("shared-networks")); + } + } + + /// @brief Sanity checks for shared networks + /// + /// This method verifies if there are no issues with shared networks. + /// @param networks pointer to shared networks being checked + /// @param json shared-networks element + /// @throw DhcpConfigError if issues are encountered + void + sharedNetworksSanityChecks(const SharedNetwork6Collection& networks, + ConstElementPtr json) { + + /// @todo: in case of errors, use json to extract line numbers. + if (!json) { + // No json? That means that the shared-networks was never specified + // in the config. + return; + } + + // Used for names uniqueness checks. + std::set names; + + // Let's go through all the networks one by one + for (auto net = networks.begin(); net != networks.end(); ++net) { + string txt; + + // Let's check if all subnets have either the same interface + // or don't have the interface specified at all. + string iface = (*net)->getIface(); + + const Subnet6Collection* subnets = (*net)->getAllSubnets(); + if (subnets) { + // For each subnet, add it to a list of regular subnets. + for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) { + if (iface.empty()) { + iface = (*subnet)->getIface(); + continue; + } + + if ((*subnet)->getIface().empty()) { + continue; + } + + if (iface != (*subnet)->getIface()) { + isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText() + << " has specified interface " << (*subnet)->getIface() + << ", but earlier subnet in the same shared-network" + << " or the shared-network itself used " << iface); + } + + // Let's collect the subnets in case we later find out the + // subnet doesn't have a mandatory name. + txt += (*subnet)->toText() + " "; + } + } + + // Next, let's check name of the shared network. + if ((*net)->getName().empty()) { + isc_throw(DhcpConfigError, "Shared-network with subnets " + << txt << " is missing mandatory 'name' parameter"); + } + + // Is it unique? + if (names.find((*net)->getName()) != names.end()) { + isc_throw(DhcpConfigError, "A shared-network with " + "name " << (*net)->getName() << " defined twice."); + } + names.insert((*net)->getName()); + + } + } + + }; } // anonymous namespace @@ -270,6 +404,10 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set, parser.parse(cfg_option_def, option_defs); } + // This parser is used in several places, so it should be available + // early. + Dhcp6ConfigParser global_parser; + BOOST_FOREACH(config_pair, values_map) { // In principle we could have the following code structured as a series // of long if else if clauses. That would give a marginal performance @@ -375,19 +513,26 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set, } if (config_pair.first == "subnet6") { - SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg(); Subnets6ListConfigParser subnets_parser; // parse() returns number of subnets parsed. We may log it one day. - subnets_parser.parse(srv_cfg, config_pair.second); + subnets_parser.parse(srv_config, config_pair.second); continue; } if (config_pair.first == "shared-networks") { - /// @todo We need to create instance of SharedNetworks4ListParser + /// We need to create instance of SharedNetworks4ListParser /// and parse the list of the shared networks into the /// CfgSharedNetworks4 object. One additional step is then to /// add subnets from the CfgSharedNetworks6 into CfgSubnets6 /// as well. + + SharedNetworks6ListParser parser; + CfgSharedNetworks6Ptr cfg = srv_config->getCfgSharedNetworks6(); + parser.parse(cfg, config_pair.second); + + // We also need to put the subnets it contains into normal + // subnets list. + global_parser.copySubnets6(srv_config->getCfgSubnets6(), cfg); continue; } @@ -417,9 +562,13 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set, } // Apply global options in the staging config. - Dhcp6ConfigParser global_parser; global_parser.parse(srv_config, mutable_cfg); + // This method conducts final sanity checks and tweaks. In particular, + // it checks that there is no conflict between plain subnets and those + // defined as part of shared networks. + global_parser.sanityChecks(srv_config, mutable_cfg); + } catch (const isc::Exception& ex) { LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL) .arg(config_pair.first).arg(ex.what()); diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 592eab2565..e71ae989af 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -280,6 +280,62 @@ public: } } + /// @brief Convenience method for running configuration + /// + /// This method does not throw, but signals errors using gtest macros. + /// + /// @param config text to be parsed as JSON + /// @param expected_code expected code (see cc/command_interpreter.h) + /// @param exp_error expected text error (check skipped if empty) + void configure(std::string config, int expected_code, + std::string exp_error = "") { + ConstElementPtr json; + ASSERT_NO_THROW(json = parseDHCP6(config, true)); + + ConstElementPtr status; + EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json)); + ASSERT_TRUE(status); + + int rcode; + ConstElementPtr comment = parseAnswer(rcode, status); + EXPECT_EQ(expected_code, rcode); + + string text; + ASSERT_NO_THROW(text = comment->stringValue()); + + if (expected_code != rcode) { + std::cout << "Reported status: " << text << std::endl; + } + + if ((rcode != 0)) { + if (!exp_error.empty()) { + EXPECT_EQ(exp_error, text); + } + } + } + + /// @brief Checks if specified subnet is part of the collection + /// + /// @param col collection of subnets to be inspected + /// @param subnet text notation (e.g. 192.0.2.0/24) + /// @param t1 expected renew-timer value + /// @param t2 expected rebind-timer value + /// @param preferred expected preferred-lifetime value + /// @param valid expected valid-lifetime value + void + checkSubnet(const Subnet6Collection& col, std::string subnet, + uint32_t t1, uint32_t t2, uint32_t pref, uint32_t valid) { + const auto& index = col.get(); + auto subnet_it = index.find(subnet); + ASSERT_NE(subnet_it, index.cend()); + Subnet6Ptr s = *subnet_it; + + EXPECT_EQ(t1, s->getT1()); + EXPECT_EQ(t2, s->getT2()); + EXPECT_EQ(pref, s->getPreferred()); + EXPECT_EQ(valid, s->getValid()); + } + /// @brief Returns an interface configuration used by the most of the /// unit tests. std::string genIfaceConfig() const { @@ -5060,9 +5116,9 @@ TEST_F(Dhcp6ParserTest, invalidClientClassDictionary) { " \"bogus\": \"bad\" \n" " } \n" "], \n" - "\"subnet4\": [ { \n" - " \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ], \n" - " \"subnet\": \"192.0.2.0/24\" \n" + "\"subnet6\": [ { \n" + " \"pools\": [ { \"pool\": \"2001:db8::1 - 2001:db8::ffff\" } ], \n" + " \"subnet\": \"2001:db8::/64\" \n" " } ] \n" "} \n"; @@ -5303,4 +5359,256 @@ TEST_F(Dhcp6ParserTest, outsideSubnetPool) { EXPECT_EQ(expected, text); } +// Test verifies that empty shared networks are accepted. +TEST_F(Dhcp6ParserTest, sharedNetworksEmpty) { + string config = "{\n" + "\"valid-lifetime\": 4000, \n" + "\"rebind-timer\": 2000, \n" + "\"renew-timer\": 1000, \n" + "\"subnet6\": [ { \n" + " \"subnet\": \"2001:db8::/48\" \n" + " } ],\n" + "\"shared-networks\": [ ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_SUCCESS, ""); +} + +// Test verifies that if a shared network is defined, it at least has to have +// a name. +TEST_F(Dhcp6ParserTest, sharedNetworksNoName) { + string config = "{\n" + "\"valid-lifetime\": 4000, \n" + "\"rebind-timer\": 2000, \n" + "\"renew-timer\": 1000, \n" + "\"subnet6\": [ { \n" + " \"subnet\": \"2001:db8::/48\" \n" + " } ],\n" + "\"shared-networks\": [ { } ]\n" + "} \n"; + + EXPECT_THROW(parseDHCP6(config, true), Dhcp6ParseError); +} + +// Test verifies that empty shared networks are accepted. +TEST_F(Dhcp6ParserTest, sharedNetworksEmptyName) { + string config = "{\n" + "\"valid-lifetime\": 4000, \n" + "\"rebind-timer\": 2000, \n" + "\"renew-timer\": 1000, \n" + "\"subnet6\": [ { \n" + " \"subnet\": \"2001:db8::/48\" \n" + " } ],\n" + "\"shared-networks\": [ { \"name\": \"\" } ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_ERROR, + "Shared-network with subnets is missing mandatory 'name' parameter"); +} + +// Test verifies that a degenerated shared-network (no subnets) is +// accepted. +TEST_F(Dhcp6ParserTest, sharedNetworksName) { + string config = "{\n" + "\"subnet6\": [ { \n" + " \"subnet\": \"2001:db8::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db8::1 - 2001:db8::ffff\" } ]\n" + " } ],\n" + "\"shared-networks\": [ { \"name\": \"foo\" } ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_SUCCESS, ""); + + // Now verify that the shared network was indeed configured. + CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg() + ->getCfgSharedNetworks6(); + ASSERT_TRUE(cfg_net); + const SharedNetwork6Collection* nets = cfg_net->getAll(); + ASSERT_TRUE(nets); + ASSERT_EQ(1, nets->size()); + SharedNetwork6Ptr net = *(nets->begin()); + ASSERT_TRUE(net); + EXPECT_EQ("foo", net->getName()); + + // Verify that there are no subnets in this shared-network + const Subnet6Collection * subs = net->getAllSubnets(); + ASSERT_TRUE(subs); + EXPECT_EQ(0, subs->size()); +} + +// Test verifies that a degenerated shared-network (just one subnet) is +// accepted. Also tests that, unless explicitly specified, the subnet +// gets default values. +TEST_F(Dhcp6ParserTest, sharedNetworks1subnet) { + string config = "{\n" + "\"shared-networks\": [ {\n" + " \"name\": \"foo\"\n," + " \"subnet6\": [ { \n" + " \"subnet\": \"2001:db8::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db8::1 - 2001:db8::ffff\" } ]\n" + " } ]\n" + " } ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_SUCCESS, ""); + + // Now verify that the shared network was indeed configured. + CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg() + ->getCfgSharedNetworks6(); + ASSERT_TRUE(cfg_net); + + // There should be exactly one shared subnet. + const SharedNetwork6Collection* nets = cfg_net->getAll(); + ASSERT_TRUE(nets); + ASSERT_EQ(1, nets->size()); + + SharedNetwork6Ptr net = *(nets->begin()); + ASSERT_TRUE(net); + EXPECT_EQ("foo", net->getName()); + + // It should have one subnet. The subnet should have default values. + const Subnet6Collection * subs = net->getAllSubnets(); + ASSERT_TRUE(subs); + EXPECT_EQ(1, subs->size()); + checkSubnet(*subs, "2001:db8::/48", 900, 1800, 3600, 7200); + + // Now make sure the subnet was added to global list of subnets. + CfgSubnets6Ptr subnets6 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6(); + ASSERT_TRUE(subnets6); + + subs = subnets6->getAll(); + ASSERT_TRUE(subs); + checkSubnet(*subs, "2001:db8::/48", 900, 1800, 3600, 7200); +} + +// Test verifies that a a proper shared-network (three subnets) is +// accepted. It verifies several things: +// - that more than one subnet can be added to shared subnets +// - that each subnet being part of the shared subnets is also stored in +// global subnets collection +// - that a subnet can inherit global values +// - that subnet can override global parameters +// - that overridden parameters only affect one subnet and not others +TEST_F(Dhcp6ParserTest, sharedNetworks3subnets) { + string config = "{\n" + "\"renew-timer\": 1000, \n" + "\"rebind-timer\": 2000, \n" + "\"preferred-lifetime\": 3000, \n" + "\"valid-lifetime\": 4000, \n" + "\"shared-networks\": [ {\n" + " \"name\": \"foo\"\n," + " \"subnet6\": [\n" + " { \n" + " \"subnet\": \"2001:db1::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db1::/64\" } ]\n" + " },\n" + " { \n" + " \"subnet\": \"2001:db2::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db2::/64\" } ],\n" + " \"renew-timer\": 2,\n" + " \"rebind-timer\": 22,\n" + " \"preferred-lifetime\": 222,\n" + " \"valid-lifetime\": 2222\n" + " },\n" + " { \n" + " \"subnet\": \"2001:db3::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db3::/64\" } ]\n" + " }\n" + " ]\n" + " } ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_SUCCESS, ""); + + // Now verify that the shared network was indeed configured. + CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg() + ->getCfgSharedNetworks6(); + + // There is expected one shared subnet. + ASSERT_TRUE(cfg_net); + const SharedNetwork6Collection* nets = cfg_net->getAll(); + ASSERT_TRUE(nets); + ASSERT_EQ(1, nets->size()); + + SharedNetwork6Ptr net = *(nets->begin()); + ASSERT_TRUE(net); + + EXPECT_EQ("foo", net->getName()); + + const Subnet6Collection * subs = net->getAllSubnets(); + ASSERT_TRUE(subs); + EXPECT_EQ(3, subs->size()); + checkSubnet(*subs, "2001:db1::/48", 1000, 2000, 3000, 4000); + checkSubnet(*subs, "2001:db2::/48", 2, 22, 222, 2222); + checkSubnet(*subs, "2001:db3::/48", 1000, 2000, 3000, 4000); + + // Now make sure the subnet was added to global list of subnets. + CfgSubnets6Ptr subnets6 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6(); + ASSERT_TRUE(subnets6); + + subs = subnets6->getAll(); + ASSERT_TRUE(subs); + checkSubnet(*subs, "2001:db1::/48", 1000, 2000, 3000, 4000); + checkSubnet(*subs, "2001:db2::/48", 2, 22, 222, 2222); + checkSubnet(*subs, "2001:db3::/48", 1000, 2000, 3000, 4000); +} + +// This test checks if parameters are derived properly: +// - global to shared network +// - shared network to subnet +TEST_F(Dhcp6ParserTest, sharedNetworksDerive) { + string config = "{\n" + "\"renew-timer\": 1, \n" + "\"rebind-timer\": 2, \n" + "\"preferred-lifetime\": 3,\n" + "\"valid-lifetime\": 4, \n" + "\"shared-networks\": [ {\n" + " \"name\": \"foo\"\n," + " \"renew-timer\": 10,\n" + " \"subnet6\": [\n" + " { \n" + " \"subnet\": \"2001:db1::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db1::/64\" } ]\n" + " },\n" + " { \n" + " \"subnet\": \"2001:db2::/48\",\n" + " \"pools\": [ { \"pool\": \"2001:db2::/64\" } ],\n" + " \"renew-timer\": 100\n" + " }\n" + " ]\n" + " } ]\n" + "} \n"; + + configure(config, CONTROL_RESULT_SUCCESS, ""); + + // Now verify that the shared network was indeed configured. + CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg() + ->getCfgSharedNetworks6(); + + // There is expected one shared subnet. + ASSERT_TRUE(cfg_net); + const SharedNetwork6Collection* nets = cfg_net->getAll(); + ASSERT_TRUE(nets); + ASSERT_EQ(1, nets->size()); + + SharedNetwork6Ptr net = *(nets->begin()); + ASSERT_TRUE(net); + + const Subnet6Collection * subs = net->getAllSubnets(); + ASSERT_TRUE(subs); + EXPECT_EQ(2, subs->size()); + + // For the first subnet, the renew-timer should be 10, because it was + // derived from shared-network level. Other parameters a derived + // from global scope to shared-network level and later again to + // subnet6 level. + checkSubnet(*subs, "2001:db1::/48", 10, 2, 3, 4); + + // For the second subnet, the renew-timer should be 100, because it + // was specified explicitly. Other parameters a derived + // from global scope to shared-network level and later again to + // subnet6 level. + checkSubnet(*subs, "2001:db2::/48", 100, 2, 3, 4); +} + }; diff --git a/src/lib/dhcpsrv/parsers/simple_parser4.cc b/src/lib/dhcpsrv/parsers/simple_parser4.cc index b9b810c51b..3aee76a446 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser4.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser4.cc @@ -159,13 +159,12 @@ size_t SimpleParser4::deriveParameters(isc::data::ElementPtr global) { } } + // Deriving parameters for shared networks is a bit more involved. + // First, the shared-network level derives from global, and then + // subnets within derive from it. ConstElementPtr shared = global->get("shared-networks"); if (shared) { BOOST_FOREACH(ElementPtr net, shared->listValue()) { - - // This level derives global parameters to shared network - // level. - // First try to inherit the parameters from shared network, // if defined there. // Then try to inherit them from global. diff --git a/src/lib/dhcpsrv/parsers/simple_parser6.cc b/src/lib/dhcpsrv/parsers/simple_parser6.cc index 16e0d801b7..d8acd87d8c 100644 --- a/src/lib/dhcpsrv/parsers/simple_parser6.cc +++ b/src/lib/dhcpsrv/parsers/simple_parser6.cc @@ -118,7 +118,6 @@ size_t SimpleParser6::setAllDefaults(isc::data::ElementPtr global) { } } - // Now set the defaults for defined subnets // Now set the defaults for defined subnets ConstElementPtr subnets = global->get("subnet6"); if (subnets) { @@ -132,6 +131,17 @@ size_t SimpleParser6::setAllDefaults(isc::data::ElementPtr global) { cnt += setDefaults(mutable_cfg, IFACE6_DEFAULTS); } + // Set defaults for shared networks + ConstElementPtr shared = global->get("shared-networks"); + if (shared) { + BOOST_FOREACH(ElementPtr net, shared->listValue()) { + ConstElementPtr subs = net->get("subnet6"); + if (subs) { + cnt += setListDefaults(subs, SUBNET6_DEFAULTS); + } + } + } + return (cnt); } @@ -146,6 +156,29 @@ size_t SimpleParser6::deriveParameters(isc::data::ElementPtr global) { } } + // Deriving parameters for shared networks is a bit more involved. + // First, the shared-network level derives from global, and then + // subnets within derive from it. + ConstElementPtr shared = global->get("shared-networks"); + if (shared) { + BOOST_FOREACH(ElementPtr net, shared->listValue()) { + // First try to inherit the parameters from shared network, + // if defined there. + // Then try to inherit them from global. + cnt += SimpleParser::deriveParams(global, net, + INHERIT_GLOBAL_TO_SUBNET6); + + // Now we need to go thrugh all the subnets in this net. + subnets = net->get("subnet6"); + if (subnets) { + BOOST_FOREACH(ElementPtr single_subnet, subnets->listValue()) { + cnt += SimpleParser::deriveParams(net, single_subnet, + INHERIT_GLOBAL_TO_SUBNET6); + } + } + } + } + return (cnt); }